Add account observer to NotificationController
The notification controller now observes changes to the account database and adds or removes message observers as appropriate. Change-Id: I1670fcfd6ce744030199b86708a6ada55b239a84
This commit is contained in:
parent
03b0870ae2
commit
e7fb4ac9e3
@ -962,6 +962,8 @@ public abstract class EmailContent {
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");
|
||||
public static final Uri RESET_NEW_MESSAGE_COUNT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount");
|
||||
public static final Uri NOTIFIER_URI =
|
||||
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account");
|
||||
|
||||
/**
|
||||
* Value used by UI to represent "combined view".
|
||||
@ -1988,7 +1990,6 @@ public abstract class EmailContent {
|
||||
public static final int FLAG_SMART_FORWARD = 1<<8;
|
||||
// Indicates that the attachment cannot be forwarded due to a policy restriction
|
||||
public static final int FLAG_POLICY_DISALLOWS_DOWNLOAD = 1<<9;
|
||||
|
||||
/**
|
||||
* no public constructor since this is a utility class
|
||||
*/
|
||||
|
@ -29,7 +29,6 @@ import com.android.emailcommon.provider.EmailContent.Mailbox;
|
||||
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.Message;
|
||||
import com.android.emailcommon.provider.EmailContent.MessageColumns;
|
||||
import com.android.emailcommon.provider.ProviderUnavailableException;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@ -56,6 +55,7 @@ import android.text.style.TextAppearanceSpan;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Class that manages notifications.
|
||||
@ -78,8 +78,8 @@ public class NotificationController {
|
||||
/** special account ID for the new message notification APIs to specify "all accounts" */
|
||||
private static final long ALL_ACCOUNTS = -1L;
|
||||
|
||||
private static NotificationThread sNewMessageThread;
|
||||
private static Handler sNewMessageHandler;
|
||||
private static NotificationThread sNotificationThread;
|
||||
private static Handler sNotificationHandler;
|
||||
private static NotificationController sInstance;
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNotificationManager;
|
||||
@ -91,6 +91,7 @@ public class NotificationController {
|
||||
// INBOX. We should adjust our logic to use the mailbox ID instead.
|
||||
/** Maps account id to the message data */
|
||||
private final HashMap<Long, MessageData> mNotificationMap;
|
||||
private ContentObserver mAccountObserver;
|
||||
|
||||
/** Constructor */
|
||||
@VisibleForTesting
|
||||
@ -195,41 +196,41 @@ public class NotificationController {
|
||||
*/
|
||||
public void watchForMessages(final boolean watch) {
|
||||
// Don't create the thread if we're only going to stop watching
|
||||
if (!watch && sNewMessageHandler == null) return;
|
||||
if (!watch && sNotificationHandler == null) return;
|
||||
|
||||
synchronized(sInstance) {
|
||||
if (sNewMessageHandler == null) {
|
||||
sNewMessageThread = new NotificationThread();
|
||||
sNewMessageHandler = new Handler(sNewMessageThread.getLooper());
|
||||
if (sNotificationHandler == null) {
|
||||
sNotificationThread = new NotificationThread();
|
||||
sNotificationHandler = new Handler(sNotificationThread.getLooper());
|
||||
}
|
||||
}
|
||||
// Run this on the message notification handler
|
||||
sNewMessageHandler.post(new Runnable() {
|
||||
sNotificationHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
HashMap<Long, long[]> table;
|
||||
if (!watch) {
|
||||
unregisterMessageNotification(ALL_ACCOUNTS);
|
||||
// TODO cancel existing account observers
|
||||
if (mAccountObserver != null) {
|
||||
resolver.unregisterContentObserver(mAccountObserver);
|
||||
mAccountObserver = null;
|
||||
}
|
||||
|
||||
// tear down the event loop
|
||||
sNewMessageThread.quit();
|
||||
sNewMessageHandler = null;
|
||||
sNewMessageThread = null;
|
||||
sNotificationThread.quit();
|
||||
sNotificationHandler = null;
|
||||
sNotificationThread = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, start new observers for all notified accounts
|
||||
registerMessageNotification(ALL_ACCOUNTS);
|
||||
// Loop through the observers and fire them once
|
||||
for (MessageData data : mNotificationMap.values()) {
|
||||
if (data.mObserver != null) {
|
||||
data.mObserver.onChange(true);
|
||||
}
|
||||
// If we're already observing account changes, don't do anything else
|
||||
if (mAccountObserver == null) {
|
||||
mAccountObserver = new AccountContentObserver(sNotificationHandler, mContext);
|
||||
resolver.registerContentObserver(Account.NOTIFIER_URI, true, mAccountObserver);
|
||||
}
|
||||
// TODO Add an account observer to track when an account is removed
|
||||
// TODO Add an account observer to track when an account is added
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -267,16 +268,19 @@ public class NotificationController {
|
||||
return;
|
||||
}
|
||||
ContentObserver observer = new MessageContentObserver(
|
||||
sNewMessageHandler, mContext, mailbox.mId, accountId);
|
||||
sNotificationHandler, mContext, mailbox.mId, accountId);
|
||||
resolver.registerContentObserver(Message.NOTIFIER_URI, true, observer);
|
||||
data.mObserver = observer;
|
||||
mNotificationMap.put(accountId, data);
|
||||
// Now, ping the observer for any initial notifications
|
||||
data.mObserver.onChange(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the observer for the given account. If the specified account does not have
|
||||
* a registered observer, no action is performed.
|
||||
* a registered observer, no action is performed. This will not clear any existing notification
|
||||
* for the specified account. Use {@link NotificationManager#cancel(int)}.
|
||||
* NOTE: This must be called on the notification handler thread.
|
||||
* @param accountId The ID of the account to unregister from. May be {@link #ALL_ACCOUNTS} to
|
||||
* unregister all accounts that have observers.
|
||||
@ -299,40 +303,6 @@ public class NotificationController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a "new message" notification for the specified account.
|
||||
*
|
||||
* @param accountId The ID of the account to cancel for. If {@link #ALL_ACCOUNTS}, "new message"
|
||||
* notifications for all accounts will be canceled.
|
||||
* @deprecated Components should not be invoking the notification controller directly.
|
||||
*/
|
||||
@Deprecated
|
||||
public void cancelNewMessageNotification(final long accountId) {
|
||||
synchronized(sInstance) {
|
||||
// If we don't have a handler, we'll figure out the correct accounts to
|
||||
// notify the next time we start the controller.
|
||||
if (sNewMessageHandler == null) return;
|
||||
}
|
||||
|
||||
// Run this on the message notification handler
|
||||
sNewMessageHandler.post(new Runnable() {
|
||||
private void clearNotification(long accountId) {
|
||||
if (accountId == ALL_ACCOUNTS) {
|
||||
for (long id : mNotificationMap.keySet()) {
|
||||
clearNotification(id);
|
||||
}
|
||||
} else {
|
||||
unregisterMessageNotification(accountId);
|
||||
mNotificationManager.cancel(getNewMessageNotificationId(accountId));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
clearNotification(accountId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the message notification for the given account to the most recent message ID.
|
||||
* This is not complete and will still exhibit the existing (wrong) behaviour of notifying
|
||||
@ -344,14 +314,14 @@ public class NotificationController {
|
||||
@Deprecated
|
||||
public void resetMessageNotification(final long accountId) {
|
||||
synchronized(sInstance) {
|
||||
if (sNewMessageHandler == null) {
|
||||
sNewMessageThread = new NotificationThread();
|
||||
sNewMessageHandler = new Handler(sNewMessageThread.getLooper());
|
||||
if (sNotificationHandler == null) {
|
||||
sNotificationThread = new NotificationThread();
|
||||
sNotificationHandler = new Handler(sNotificationThread.getLooper());
|
||||
}
|
||||
}
|
||||
|
||||
// Run this on the message notification handler
|
||||
sNewMessageHandler.post(new Runnable() {
|
||||
sNotificationHandler.post(new Runnable() {
|
||||
private void resetNotification(long accountId) {
|
||||
if (accountId == ALL_ACCOUNTS) {
|
||||
for (long id : mNotificationMap.keySet()) {
|
||||
@ -633,7 +603,11 @@ public class NotificationController {
|
||||
MESSAGE_SELECTION,
|
||||
new String[] { Long.toString(mMailboxId), Long.toString(lastSeenMessageId) },
|
||||
MessageColumns.ID + " DESC");
|
||||
if (c == null) throw new ProviderUnavailableException();
|
||||
if (c == null) {
|
||||
// Suspender time ... theoretically, this will never happen
|
||||
Log.wtf(Logging.LOG_TAG, "#onChange(); NULL response for message id query");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int newMessageCount = c.getCount();
|
||||
long newMessageId = 0L;
|
||||
@ -643,8 +617,8 @@ public class NotificationController {
|
||||
|
||||
if (newMessageCount == 0) {
|
||||
// No messages to notify for; clear the notification
|
||||
sInstance.mNotificationManager.cancel(
|
||||
sInstance.getNewMessageNotificationId(mAccountId));
|
||||
int notificationId = sInstance.getNewMessageNotificationId(mAccountId);
|
||||
sInstance.mNotificationManager.cancel(notificationId);
|
||||
} else if (newMessageCount != oldMessageCount
|
||||
|| (newMessageId != 0 && newMessageId != oldMessageId)) {
|
||||
// Either the count or last message has changed; update the notification
|
||||
@ -665,6 +639,62 @@ public class NotificationController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer invoked whenever an account is modified. This could mean the user changed the
|
||||
* notification settings.
|
||||
*/
|
||||
private static class AccountContentObserver extends ContentObserver {
|
||||
private final Context mContext;
|
||||
public AccountContentObserver(Handler handler, Context context) {
|
||||
super(handler);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
final Cursor c = resolver.query(
|
||||
Account.CONTENT_URI, EmailContent.ID_PROJECTION,
|
||||
NOTIFIED_ACCOUNT_SELECTION, null, null);
|
||||
final HashSet<Long> newAccountList = new HashSet<Long>();
|
||||
final HashSet<Long> removedAccountList = new HashSet<Long>();
|
||||
if (c == null) {
|
||||
// Suspender time ... theoretically, this will never happen
|
||||
Log.wtf(Logging.LOG_TAG, "#onChange(); NULL response for account id query");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
long accountId = c.getLong(EmailContent.ID_PROJECTION_COLUMN);
|
||||
newAccountList.add(accountId);
|
||||
}
|
||||
} finally {
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
// NOTE: Looping over three lists is not necessarily the most efficient. However, the
|
||||
// account lists are going to be very small, so, this will not be necessarily bad.
|
||||
// Cycle through existing notification list and adjust as necessary
|
||||
for (long accountId : sInstance.mNotificationMap.keySet()) {
|
||||
if (!newAccountList.remove(accountId)) {
|
||||
// account id not in the current set of notifiable accounts
|
||||
removedAccountList.add(accountId);
|
||||
}
|
||||
}
|
||||
// A new account was added to the notification list
|
||||
for (long accountId : newAccountList) {
|
||||
sInstance.registerMessageNotification(accountId);
|
||||
}
|
||||
// An account was removed from the notification list
|
||||
for (long accountId : removedAccountList) {
|
||||
sInstance.unregisterMessageNotification(accountId);
|
||||
int notificationId = sInstance.getNewMessageNotificationId(accountId);
|
||||
sInstance.mNotificationManager.cancel(notificationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Information about the message(s) we're notifying the user about. */
|
||||
private static class MessageData {
|
||||
/** The database observer */
|
||||
|
@ -663,12 +663,6 @@ public class AccountSettingsXL extends PreferenceActivity {
|
||||
*/
|
||||
public void deleteAccount(Account account) {
|
||||
// Kick off the work to actually delete the account
|
||||
|
||||
// TODO Do this automatically in the NotificationController as part of a
|
||||
// ContentObserver
|
||||
// Clear notifications for the account.
|
||||
NotificationController.getInstance(this).cancelNewMessageNotification(account.mId);
|
||||
|
||||
// Delete the account (note, this is async. Would be nice to get a callback.
|
||||
Controller.getInstance(this).deleteAccount(account.mId);
|
||||
|
||||
|
@ -1221,9 +1221,7 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
// Notify all notifier cursors
|
||||
if (match == MESSAGE || match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
|
||||
sendNotifierChange(Message.NOTIFIER_URI, NOTIFICATION_OP_DELETE, id);
|
||||
}
|
||||
sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_DELETE, id);
|
||||
|
||||
// Notify all email content cursors
|
||||
resolver.notifyChange(EmailContent.CONTENT_URI, null);
|
||||
@ -1358,9 +1356,7 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
// Notify all notifier cursors
|
||||
if (match == MESSAGE) {
|
||||
sendNotifierChange(Message.NOTIFIER_URI, NOTIFICATION_OP_INSERT, id);
|
||||
}
|
||||
sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_INSERT, id);
|
||||
|
||||
// Notify all existing cursors.
|
||||
resolver.notifyChange(EmailContent.CONTENT_URI, null);
|
||||
@ -1858,14 +1854,33 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
// Notify all notifier cursors
|
||||
if (match == MESSAGE || match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
|
||||
sendNotifierChange(Message.NOTIFIER_URI, NOTIFICATION_OP_UPDATE, id);
|
||||
}
|
||||
sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_UPDATE, id);
|
||||
|
||||
resolver.notifyChange(notificationUri, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base notification URI for the given content type.
|
||||
*
|
||||
* @param match The type of content that was modified.
|
||||
*/
|
||||
private Uri getBaseNotificationUri(int match) {
|
||||
Uri baseUri = null;
|
||||
switch (match) {
|
||||
case MESSAGE:
|
||||
case MESSAGE_ID:
|
||||
case SYNCED_MESSAGE_ID:
|
||||
baseUri = Message.NOTIFIER_URI;
|
||||
break;
|
||||
case ACCOUNT:
|
||||
case ACCOUNT_ID:
|
||||
baseUri = Account.NOTIFIER_URI;
|
||||
break;
|
||||
}
|
||||
return baseUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a change notification to any cursors observers of the given base URI. The final
|
||||
* notification URI is dynamically built to contain the specified information. It will be
|
||||
@ -1881,6 +1896,8 @@ public class EmailProvider extends ContentProvider {
|
||||
* appended to the base URI.
|
||||
*/
|
||||
private void sendNotifierChange(Uri baseUri, String op, String id) {
|
||||
if (baseUri == null) return;
|
||||
|
||||
final ContentResolver resolver = getContext().getContentResolver();
|
||||
|
||||
// Append the operation, if specified
|
||||
|
Loading…
Reference in New Issue
Block a user