Add URI's that atomically add a value to a particular field (Account or Mailbox)

* Message, etc. could be added, if this had a use case
* Unit test added for both Account and Mailbox cases
* Eclipse changed some parens around; it won't happen again
This commit is contained in:
Marc Blank 2009-08-19 19:07:29 -07:00
parent d366346a64
commit c0c9c33322
5 changed files with 117 additions and 35 deletions

View File

@ -70,6 +70,9 @@ public abstract class EmailContent {
};
private static final int ID_PROJECTION_COLUMN = 0;
public static final String FIELD_COLUMN_NAME = "field";
public static final String ADD_COLUMN_NAME = "add";
// Newly created objects get this id
private static final int NOT_SAVED = -1;
// The base Uri that this piece of content came from
@ -275,7 +278,7 @@ public abstract class EmailContent {
* If the message has no body, a new body is inserted for the message.
* Warning: the argument "values" is modified by this method, setting MESSAGE_KEY.
*/
public static void updateBodyWithMessageId(Context context, long messageId,
public static void updateBodyWithMessageId(Context context, long messageId,
ContentValues values) {
ContentResolver resolver = context.getContentResolver();
long bodyId = lookupBodyIdWithMessageId(resolver, messageId);
@ -754,7 +757,6 @@ public abstract class EmailContent {
return b;
}
}
}
return null;
}
@ -797,7 +799,8 @@ public abstract class EmailContent {
public static final class Account extends EmailContent implements AccountColumns, Parcelable {
public static final String TABLE_NAME = "Account";
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
public static final Uri ADD_TO_FIELD_URI =
Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");
public final static int FLAGS_NOTIFY_NEW_MAIL = 1;
public final static int FLAGS_VIBRATE = 2;
@ -1750,6 +1753,8 @@ public abstract class EmailContent {
public static final class Mailbox extends EmailContent implements SyncColumns, MailboxColumns {
public static final String TABLE_NAME = "Mailbox";
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
public static final Uri ADD_TO_FIELD_URI =
Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
public String mDisplayName;
public String mServerId;

View File

@ -66,11 +66,13 @@ public class EmailProvider extends ContentProvider {
private static final int ACCOUNT = ACCOUNT_BASE;
private static final int ACCOUNT_MAILBOXES = ACCOUNT_BASE + 1;
private static final int ACCOUNT_ID = ACCOUNT_BASE + 2;
private static final int ACCOUNT_ID_ADD_TO_FIELD = ACCOUNT_BASE + 3;
private static final int MAILBOX_BASE = 0x1000;
private static final int MAILBOX = MAILBOX_BASE;
private static final int MAILBOX_MESSAGES = MAILBOX_BASE + 1;
private static final int MAILBOX_ID = MAILBOX_BASE + 2;
private static final int MAILBOX_ID_ADD_TO_FIELD = MAILBOX_BASE + 3;
private static final int MESSAGE_BASE = 0x2000;
private static final int MESSAGE = MESSAGE_BASE;
@ -146,6 +148,8 @@ public class EmailProvider extends ContentProvider {
private static final String DELETE_BODY = "delete from " + Body.TABLE_NAME +
" where " + BodyColumns.MESSAGE_KEY + '=';
private static final String ID_EQUALS = EmailContent.RECORD_ID + "=?";
static {
// Email URI matching table
UriMatcher matcher = sURIMatcher;
@ -199,9 +203,13 @@ public class EmailProvider extends ContentProvider {
// A specific hostauth
matcher.addURI(EMAIL_AUTHORITY, "hostauth/#", HOSTAUTH_ID);
// Atomically a constant value to a particular field of a mailbox/account
matcher.addURI(EMAIL_AUTHORITY, "mailboxIdAddToField/#", MAILBOX_ID_ADD_TO_FIELD);
matcher.addURI(EMAIL_AUTHORITY, "accountIdAddToField/#", ACCOUNT_ID_ADD_TO_FIELD);
/**
* THIS URI HAS SPECIAL SEMANTICS
* ITS USE IS INDENTED FOR THE UI APPLICATION TO MARK CHANGES THAT NEED TO BE SYNCED BACK
* ITS USE IS INTENDED FOR THE UI APPLICATION TO MARK CHANGES THAT NEED TO BE SYNCED BACK
* TO A SERVER VIA A SYNC ADAPTER
*/
matcher.addURI(EMAIL_AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID);
@ -876,7 +884,40 @@ public class EmailProvider extends ContentProvider {
values.remove(MailboxColumns.UNREAD_COUNT);
}
String id;
switch (match) {
case MAILBOX_ID_ADD_TO_FIELD:
case ACCOUNT_ID_ADD_TO_FIELD:
if (!mInTransaction) {
db.beginTransaction();
}
id = uri.getPathSegments().get(1);
String field = values.getAsString(EmailContent.FIELD_COLUMN_NAME);
Long add = values.getAsLong(EmailContent.ADD_COLUMN_NAME);
if (field == null || add == null) {
throw new IllegalArgumentException("No field/add specified " + uri);
}
Cursor c = db.query(TABLE_NAMES[table],
new String[] {EmailContent.RECORD_ID, field}, whereWithId(id, selection),
selectionArgs, null, null, null);
try {
result = 0;
ContentValues cv = new ContentValues();
String[] bind = new String[1];
while (c.moveToNext()) {
bind[0] = c.getString(0);
long value = c.getLong(1) + add;
cv.put(field, value);
result = db.update(TABLE_NAMES[table], cv, ID_EQUALS, bind);
}
} finally {
c.close();
}
if (!mInTransaction) {
db.setTransactionSuccessful();
db.endTransaction();
}
break;
case BODY_ID:
case MESSAGE_ID:
case SYNCED_MESSAGE_ID:
@ -885,7 +926,7 @@ public class EmailProvider extends ContentProvider {
case MAILBOX_ID:
case ACCOUNT_ID:
case HOSTAUTH_ID:
String id = uri.getPathSegments().get(1);
id = uri.getPathSegments().get(1);
if (match == SYNCED_MESSAGE_ID) {
// For synced messages, first copy the old message to the updated table
// Note the insert or ignore semantics, guaranteeing that only the first

View File

@ -22,6 +22,7 @@ import com.android.email.R;
import com.android.email.activity.MessageList;
import com.android.email.mail.MessagingException;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Mailbox;
import android.app.AlarmManager;
@ -62,7 +63,9 @@ public class MailService extends Service {
private static final String EXTRA_CHECK_ACCOUNT = "com.android.email.intent.extra.ACCOUNT";
private static final String EXTRA_ACCOUNT_INFO = "com.android.email.intent.extra.ACCOUNT_INFO";
private static final String EXTRA_MESSAGE_COUNT = "com.android.email.intent.extra.COUNT";
private static final String[] NEW_MESSAGE_COUNT_PROJECTION =
new String[] {AccountColumns.NEW_MESSAGE_COUNT};
private Controller.Result mControllerCallback = new ControllerResults();
@ -120,21 +123,20 @@ public class MailService extends Service {
}
context.getContentResolver().update(uri, mClearNewMessages, null, null);
}
/**
* Entry point for asynchronous message services (e.g. push mode) to post notifications of new
* messages. This assumes that the push provider has already synced the messages into the
* appropriate database - this simply triggers the notification mechanism.
*
*
* @param context a context
* @param accountId the id of the account that is reporting new messages
* @param newCount the number of new messages
*/
public static void actionNotifyNewMessages(Context context, long accountId, int newCount) {
public static void actionNotifyNewMessages(Context context, long accountId) {
Intent i = new Intent(ACTION_NOTIFY_MAIL);
i.setClass(context, MailService.class);
i.putExtra(EXTRA_CHECK_ACCOUNT, accountId);
i.putExtra(EXTRA_MESSAGE_COUNT, newCount);
context.startService(i);
}
@ -156,7 +158,7 @@ public class MailService extends Service {
// If we have the data, restore the last-sync-times for each account
// These are cached in the wakeup intent in case the process was killed.
restoreSyncReports(intent);
// Sync a specific account if given
long checkAccountId = intent.getLongExtra(EXTRA_CHECK_ACCOUNT, -1);
if (checkAccountId != -1) {
@ -185,7 +187,21 @@ public class MailService extends Service {
stopSelf(startId);
} else if (ACTION_NOTIFY_MAIL.equals(action)) {
long accountId = intent.getLongExtra(EXTRA_CHECK_ACCOUNT, -1);
int newMessageCount = intent.getIntExtra(EXTRA_MESSAGE_COUNT, -1);
// Get the current new message count
Cursor c = getContentResolver().query(
ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
NEW_MESSAGE_COUNT_PROJECTION, null, null, null);
int newMessageCount = 0;
try {
if (c.moveToFirst()) {
newMessageCount = c.getInt(0);
} else {
// If the account no longer exists, set to -1 (which is handled below)
accountId = -1;
}
} finally {
c.close();
}
if (Config.LOGD && Email.DEBUG) {
Log.d(Email.LOG_TAG, "*** MailService: notify accountId=" + Long.toString(accountId)
+ " count=" + newMessageCount);

View File

@ -18,8 +18,10 @@
package com.android.exchange.adapter;
import com.android.email.mail.Address;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
@ -464,28 +466,17 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
}
}
// TODO: This should be implemented using an "add to unread messages" URI,
// and then it could be handled in the previous section as just another "op"
int totalNewCount = 0;
if (notifyCount > 0) {
Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId);
Cursor c = mContentResolver.query(uri,
new String[] { Account.NEW_MESSAGE_COUNT }, null, null, null);
try {
if (c.moveToNext()) {
int oldCount = c.getInt(0);
ContentValues cv = new ContentValues();
totalNewCount = oldCount + notifyCount;
cv.put(Account.NEW_MESSAGE_COUNT, totalNewCount);
mContentResolver.update(uri, cv, null, null);
}
} finally {
c.close();
}
}
if (totalNewCount > 0) {
MailService.actionNotifyNewMessages(mContext, mAccount.mId, totalNewCount);
// Use the new atomic add URI in EmailProvider
// We could add this to the operations being done, but it's not strictly
// speaking necessary, as the previous batch preserves the integrity of the
// database, whereas this is purely for notification purposes, and is itself atomic
ContentValues cv = new ContentValues();
cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT);
cv.put(EmailContent.ADD_COLUMN_NAME, notifyCount);
Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, mAccount.mId);
mContentResolver.update(uri, cv, null, null);
MailService.actionNotifyNewMessages(mContext, mAccount.mId);
}
}
}

View File

@ -445,7 +445,7 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
// 3. delete first message
resolver.delete(ContentUris.withAppendedId(Message.CONTENT_URI, message1Id), null, null);
// 4. verify body for second message wasn't deleted
assertNotNull(loadBodyForMessageId(message2Id));
@ -483,7 +483,6 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
true, mMockContext);
long message2Id = message2.mId;
//verify body is there
Body body = loadBodyForMessageId(message2Id);
assertNotNull(loadBodyForMessageId(message2Id));
// 3. delete first message
@ -1030,4 +1029,34 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
String newStr = EmailProvider.createIndex(Message.TABLE_NAME, MessageColumns.TIMESTAMP);
assertEquals(newStr, oldStr);
}
public void testIdAddToField() {
ContentResolver cr = mMockContext.getContentResolver();
ContentValues cv = new ContentValues();
// Try changing the newMessageCount of an account
Account account = ProviderTestUtils.setupAccount("field-add", true, mMockContext);
int startCount = account.mNewMessageCount;
// "field" and "add" are the two required elements
cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT);
cv.put(EmailContent.ADD_COLUMN_NAME, 17);
cr.update(ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, account.mId),
cv, null, null);
Account restoredAccount = Account.restoreAccountWithId(mMockContext, account.mId);
assertEquals(17 + startCount, restoredAccount.mNewMessageCount);
cv.put(EmailContent.ADD_COLUMN_NAME, -11);
cr.update(ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, account.mId),
cv, null, null);
restoredAccount = Account.restoreAccountWithId(mMockContext, account.mId);
assertEquals(17 - 11 + startCount, restoredAccount.mNewMessageCount);
// Now try with a mailbox
Mailbox boxA = ProviderTestUtils.setupMailbox("boxA", account.mId, true, mMockContext);
assertEquals(0, boxA.mUnreadCount);
cv.put(EmailContent.FIELD_COLUMN_NAME, MailboxColumns.UNREAD_COUNT);
cv.put(EmailContent.ADD_COLUMN_NAME, 11);
cr.update(ContentUris.withAppendedId(Mailbox.ADD_TO_FIELD_URI, boxA.mId), cv, null, null);
Mailbox restoredBoxA = Mailbox.restoreMailboxWithId(mMockContext, boxA.mId);
assertEquals(11, restoredBoxA.mUnreadCount);
}
}