Fix ui notifications on folder list changes.

uifolders and uiallfolders cursors now setNotificationUri on
the appropriate uri. That uri is notified whenever:

- A folder is inserted, deleted, or modified.
- A message is inserted or modified (since this can affect
  message counts).

This second one is still not quite right: there are some
conditions where counts aren't updating correctly.

While I was here, I renamed the notification uris to
avoid collisions between different versions of the app.

Bug: 9111855
Change-Id: Ia29bb6a65b4f673bf352fdf0e14270b3f1443ca8
This commit is contained in:
Yu Ping Hu 2013-08-06 16:09:00 -07:00
parent 6edccbf1f1
commit c3ceed6894
2 changed files with 123 additions and 64 deletions

View File

@ -131,6 +131,12 @@ public class Mailbox extends EmailContent implements MailboxColumns, Parcelable
};
private static final int MAILBOX_DISPLAY_NAME_COLUMN = 0;
/**
* Projection to use when reading {@link MailboxColumns#ACCOUNT_KEY} for a mailbox.
*/
private static final String[] ACCOUNT_KEY_PROJECTION = { MailboxColumns.ACCOUNT_KEY };
private static final int ACCOUNT_KEY_PROJECTION_ACCOUNT_KEY_COLUMN = 0;
public static final long NO_MAILBOX = -1;
// Sentinel values for the mSyncInterval field of both Mailbox records
@ -760,4 +766,18 @@ public class Mailbox extends EmailContent implements MailboxColumns, Parcelable
OUTBOX_PLUS_SYNCING_AND_ACCOUNT_SELECTION,
new String[] { Long.toString(accountId) }, null);
}
/**
* Get the account id for a mailbox.
* @param context The {@link Context}.
* @param mailboxId The id of the mailbox we're interested in, as a {@link String}.
* @return The account id for the mailbox, or {@link Account#NO_ACCOUNT} if the mailbox doesn't
* exist.
*/
public static long getAccountIdForMailbox(final Context context, final String mailboxId) {
return Utility.getFirstRowLong(context,
Mailbox.CONTENT_URI.buildUpon().appendEncodedPath(mailboxId).build(),
ACCOUNT_KEY_PROJECTION, null, null, null,
ACCOUNT_KEY_PROJECTION_ACCOUNT_KEY_COLUMN, Account.NO_ACCOUNT);
}
}

View File

@ -577,13 +577,20 @@ public class EmailProvider extends ContentProvider {
db.execSQL(UPDATED_MESSAGE_DELETE + id);
}
final long accountId;
if (match == MAILBOX_ID) {
accountId = Mailbox.getAccountIdForMailbox(context, id);
} else {
accountId = Account.NO_ACCOUNT;
}
result = db.delete(tableName, whereWithId(id, selection), selectionArgs);
if (match == ACCOUNT_ID) {
notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, id);
resolver.notifyChange(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
} else if (match == MAILBOX_ID) {
notifyUIFolder(id, null);
notifyUIFolder(id, accountId);
} else if (match == ATTACHMENT_ID) {
notifyUI(UIPROVIDER_ATTACHMENT_NOTIFIER, id);
}
@ -684,26 +691,30 @@ public class EmailProvider extends ContentProvider {
}
}
// These URIs are used for specific UI notifications. We don't use EmailContent.CONTENT_URI
// as the base because that gets spammed.
private static final String UI_NOTIFICATION_AUTHORITY =
EmailContent.EMAIL_PACKAGE_NAME + ".uinotifications";
private static final Uri UIPROVIDER_CONVERSATION_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uimessages");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uimessages");
private static final Uri UIPROVIDER_FOLDER_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uifolder");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uifolder");
private static final Uri UIPROVIDER_FOLDERLIST_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uifolders");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uifolders");
private static final Uri UIPROVIDER_ACCOUNT_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uiaccount");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uiaccount");
public static final Uri UIPROVIDER_SETTINGS_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uisettings");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uisettings");
private static final Uri UIPROVIDER_ATTACHMENT_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uiattachment");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uiattachment");
private static final Uri UIPROVIDER_ATTACHMENTS_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uiattachments");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uiattachments");
public static final Uri UIPROVIDER_ALL_ACCOUNTS_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uiaccts");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uiaccts");
private static final Uri UIPROVIDER_MESSAGE_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uimessage");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uimessage");
private static final Uri UIPROVIDER_RECENT_FOLDERS_NOTIFIER =
Uri.parse("content://" + UIProvider.AUTHORITY + "/uirecentfolders");
Uri.parse("content://" + UI_NOTIFICATION_AUTHORITY + "/uirecentfolders");
@Override
public Uri insert(Uri uri, ContentValues values) {
@ -744,25 +755,25 @@ public class EmailProvider extends ContentProvider {
resultUri = ContentUris.withAppendedId(uri, longId);
switch(match) {
case MESSAGE:
final long mailboxId = values.getAsLong(Message.MAILBOX_KEY);
if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
notifyUIConversationMailbox(values.getAsLong(Message.MAILBOX_KEY));
notifyUIConversationMailbox(mailboxId);
}
notifyUIFolder(mailboxId, values.getAsLong(Message.ACCOUNT_KEY));
break;
case MAILBOX:
if (values.containsKey(MailboxColumns.TYPE)) {
// Only notify for special mailbox types
int type = values.getAsInteger(MailboxColumns.TYPE);
if (type != Mailbox.TYPE_INBOX && type != Mailbox.TYPE_OUTBOX &&
type != Mailbox.TYPE_DRAFTS && type != Mailbox.TYPE_SENT &&
type != Mailbox.TYPE_TRASH && type != Mailbox.TYPE_SEARCH) {
break;
if (values.getAsInteger(MailboxColumns.TYPE) <
Mailbox.TYPE_NOT_EMAIL) {
// Notify the account when a new mailbox is added
final Long accountId =
values.getAsLong(MailboxColumns.ACCOUNT_KEY);
if (accountId != null && accountId.longValue() > 0) {
notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, accountId);
notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
}
}
}
// Notify the account when a new mailbox is added
Long accountId = values.getAsLong(MailboxColumns.ACCOUNT_KEY);
if (accountId != null && accountId.longValue() > 0) {
notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, accountId);
}
break;
case ACCOUNT:
updateAccountSyncInterval(longId, values);
@ -1438,27 +1449,19 @@ public class EmailProvider extends ContentProvider {
case QUICK_RESPONSE_ID:
case POLICY_ID:
id = uri.getPathSegments().get(1);
try {
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
// update will be reflected in the updated message table; therefore this
// row will always have the "original" data
db.execSQL(UPDATED_MESSAGE_INSERT + id);
} else if (match == MESSAGE_ID) {
db.execSQL(UPDATED_MESSAGE_DELETE + id);
}
result = db.update(tableName, values, whereWithId(id, selection),
selectionArgs);
} catch (SQLiteException e) {
// Null out values (so they aren't cached) and re-throw
values = null;
throw e;
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
// update will be reflected in the updated message table; therefore this
// row will always have the "original" data
db.execSQL(UPDATED_MESSAGE_INSERT + id);
} else if (match == MESSAGE_ID) {
db.execSQL(UPDATED_MESSAGE_DELETE + id);
}
result = db.update(tableName, values, whereWithId(id, selection),
selectionArgs);
if (match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
notifyUIConversation(uri);
}
handleMessageUpdateNotifications(uri, id, values);
} else if (match == ATTACHMENT_ID) {
long attId = Integer.parseInt(id);
if (values.containsKey(Attachment.FLAGS)) {
@ -1477,9 +1480,8 @@ public class EmailProvider extends ContentProvider {
notifyUI(UIPROVIDER_ATTACHMENTS_NOTIFIER, att.mMessageKey);
}
}
} else if (match == MAILBOX_ID && values.containsKey(Mailbox.UI_SYNC_STATUS)) {
// TODO: should this notify on keys other than sync status?
notifyUIFolder(id, null);
} else if (match == MAILBOX_ID) {
notifyUIFolder(id, Mailbox.getAccountIdForMailbox(context, id));
} else if (match == ACCOUNT_ID) {
updateAccountSyncInterval(Long.parseLong(id), values);
// Notify individual account and "all accounts"
@ -3055,8 +3057,10 @@ public class EmailProvider extends ContentProvider {
row = getVirtualMailboxRow(acctId, Mailbox.TYPE_UNREAD);
mc.addRow(row);
mc.setNotificationUri(context.getContentResolver(), uri);
c.setNotificationUri(context.getContentResolver(), uri);
final Uri notifyUri =
UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
mc.setNotificationUri(context.getContentResolver(), notifyUri);
c.setNotificationUri(context.getContentResolver(), notifyUri);
Cursor[] cursors = new Cursor[] {mc, c};
return new MergeCursor(cursors);
}
@ -3436,8 +3440,11 @@ public class EmailProvider extends ContentProvider {
Uri notifyUri = null;
switch(match) {
case UI_ALL_FOLDERS:
// TODO: Should this just do uiFolders()?
c = db.rawQuery(genQueryAccountAllMailboxes(uiProjection), new String[] {id});
c = getFolderListCursor(db, c, uiProjection);
notifyUri =
UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
break;
case UI_RECENT_FOLDERS:
c = db.rawQuery(genQueryRecentMailboxes(uiProjection), new String[] {id});
@ -3446,6 +3453,11 @@ public class EmailProvider extends ContentProvider {
case UI_SUBFOLDERS:
c = db.rawQuery(genQuerySubfolders(uiProjection), new String[] {id});
c = getFolderListCursor(db, c, uiProjection);
// Get notifications for any folder changes on this account. This is broader than
// we need but otherwise we'd need for every folder change to notify on all relevant
// subtrees. For now we opt for simplicity.
final long accountId = Mailbox.getAccountIdForMailbox(context, id);
notifyUri = ContentUris.withAppendedId(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
break;
case UI_MESSAGES:
long mailboxId = Long.parseLong(id);
@ -4117,9 +4129,48 @@ public class EmailProvider extends ContentProvider {
.build();
addToSequence(uri, op);
}
return update(ourUri, ourValues, null, null);
}
/**
* Projection for use with getting mailbox & account keys for a message.
*/
private static final String[] MESSAGE_KEYS_PROJECTION =
{ MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY };
private static final int MESSAGE_KEYS_MAILBOX_KEY_COLUMN = 0;
private static final int MESSAGE_KEYS_ACCOUNT_KEY_COLUMN = 1;
/**
* Notify necessary UI components in response to a message update.
* @param uri The {@link Uri} for this message update.
* @param messageId The id of the message that's been updated.
* @param values The {@link ContentValues} that were updated in the message.
*/
private void handleMessageUpdateNotifications(final Uri uri, final String messageId,
final ContentValues values) {
if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
notifyUIConversation(uri);
}
// TODO: Ideally, also test that the values actually changed.
if (values.containsKey(MessageColumns.FLAG_READ) ||
values.containsKey(MessageColumns.MAILBOX_KEY)) {
final Cursor c = query(
Message.CONTENT_URI.buildUpon().appendEncodedPath(messageId).build(),
MESSAGE_KEYS_PROJECTION, null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
notifyUIFolder(c.getLong(MESSAGE_KEYS_MAILBOX_KEY_COLUMN),
c.getLong(MESSAGE_KEYS_ACCOUNT_KEY_COLUMN));
}
} finally {
c.close();
}
}
}
}
public static final String PICKER_UI_ACCOUNT = "picker_ui_account";
public static final String PICKER_MAILBOX_TYPE = "picker_mailbox_type";
public static final String PICKER_MESSAGE_ID = "picker_message_id";
@ -4248,30 +4299,18 @@ public class EmailProvider extends ContentProvider {
* Notify about a folder update. Because folder changes can affect the conversation cursor's
* extras, the conversation must also be notified here.
* @param folderId the folder id to be notified
* @param accountId the account id to be notified (for folder list notification); if null, then
* lookup the accountId from the folder.
* @param accountId the account id to be notified (for folder list notification).
*/
private void notifyUIFolder(String folderId, String accountId) {
private void notifyUIFolder(final String folderId, final long accountId) {
notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, folderId);
notifyUI(UIPROVIDER_FOLDER_NOTIFIER, folderId);
if (accountId == null) {
try {
final Mailbox mailbox = Mailbox.restoreMailboxWithId(getContext(),
Long.parseLong(folderId));
if (mailbox != null) {
accountId = Long.toString(mailbox.mAccountKey);
}
} catch (NumberFormatException e) {
// Bad folderId, so we can't lookup account.
}
}
if (accountId != null) {
if (accountId != Account.NO_ACCOUNT) {
notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
}
}
private void notifyUIFolder(long folderId, long accountId) {
notifyUIFolder(Long.toString(folderId), Long.toString(accountId));
private void notifyUIFolder(final long folderId, final long accountId) {
notifyUIFolder(Long.toString(folderId), accountId);
}
private void notifyUI(Uri uri, String id) {