diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java index cf4e08f85..e73fe6158 100644 --- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java +++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java @@ -965,14 +965,21 @@ public abstract class EmailContent { public static final Uri NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); + // Define all pseudo account IDs here to avoid conflict with one another. /** - * Value used by UI to represent "combined view". + * Pseudo account ID to represent a "combined account" that includes messages and mailboxes + * from all defined accounts. * - * NOTE: This must be used only by UI, and mustn't be stored in the database. - * - * This is defined here to avoid conflict with other pseudo account IDs, if any. + * IMPORTANT: This must never be stored to the database. */ public static final long ACCOUNT_ID_COMBINED_VIEW = 0x1000000000000000L; + /** + * Pseudo account ID to represent "no account". This may be used any time the account ID + * may not be known or when we want to specifically select "no" account. + * + * IMPORTANT: This must never be stored to the database. + */ + public static final long PSEUDO_ACCOUNT_ID_NONE = -1L; // Whether or not the user has asked for notifications of new mail in this account public final static int FLAGS_NOTIFY_NEW_MAIL = 1<<0; @@ -1120,6 +1127,15 @@ public abstract class EmailContent { Account.CONTENT_URI, Account.CONTENT_PROJECTION, id); } + /** + * Returns {@code true} if the given account ID is a "normal" account. Normal accounts + * always have an ID greater than {@code 0} and not equal to any pseudo account IDs + * (such as {@link #ACCOUNT_ID_COMBINED_VIEW}) + */ + public static boolean isNormalAccount(long accountId) { + return (accountId > 0L) && (accountId != ACCOUNT_ID_COMBINED_VIEW); + } + /** * Refresh an account that has already been loaded. This is slightly less expensive * that generating a brand-new account object. @@ -1217,13 +1233,11 @@ public abstract class EmailContent { mSignature = signature; } - /** * @return the minutes per check (for polling) * TODO define sentinel values for "never", "push", etc. See Account.java */ - public int getSyncInterval() - { + public int getSyncInterval() { return mSyncInterval; } @@ -1232,8 +1246,7 @@ public abstract class EmailContent { * TODO define sentinel values for "never", "push", etc. See Account.java * @param minutes the number of minutes between polling checks */ - public void setSyncInterval(int minutes) - { + public void setSyncInterval(int minutes) { mSyncInterval = minutes; } @@ -1776,6 +1789,7 @@ public abstract class EmailContent { /** * Supports Parcelable */ + @Override public int describeContents() { return 0; } @@ -1785,10 +1799,12 @@ public abstract class EmailContent { */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public EmailContent.Account createFromParcel(Parcel in) { return new EmailContent.Account(in); } + @Override public EmailContent.Account[] newArray(int size) { return new EmailContent.Account[size]; } @@ -1797,6 +1813,7 @@ public abstract class EmailContent { /** * Supports Parcelable */ + @Override public void writeToParcel(Parcel dest, int flags) { // mBaseUri is not parceled dest.writeLong(mId); @@ -2157,10 +2174,12 @@ public abstract class EmailContent { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public EmailContent.Attachment createFromParcel(Parcel in) { return new EmailContent.Attachment(in); } + @Override public EmailContent.Attachment[] newArray(int size) { return new EmailContent.Attachment[size]; } @@ -2926,6 +2945,7 @@ public abstract class EmailContent { /** * Supports Parcelable */ + @Override public int describeContents() { return 0; } @@ -2935,10 +2955,12 @@ public abstract class EmailContent { */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public EmailContent.HostAuth createFromParcel(Parcel in) { return new EmailContent.HostAuth(in); } + @Override public EmailContent.HostAuth[] newArray(int size) { return new EmailContent.HostAuth[size]; } @@ -2947,6 +2969,7 @@ public abstract class EmailContent { /** * Supports Parcelable */ + @Override public void writeToParcel(Parcel dest, int flags) { // mBaseUri is not parceled dest.writeLong(mId); diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java index a21193b43..46bc6df64 100644 --- a/emailcommon/src/com/android/emailcommon/utility/Utility.java +++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java @@ -546,6 +546,7 @@ public class Utility { */ public static void showToast(final Context context, final String message) { getMainThreadHandler().post(new Runnable() { + @Override public void run() { Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } @@ -624,24 +625,28 @@ public class Utility { } private static final CursorGetter LONG_GETTER = new CursorGetter() { + @Override public Long get(Cursor cursor, int column) { return cursor.getLong(column); } }; private static final CursorGetter INT_GETTER = new CursorGetter() { + @Override public Integer get(Cursor cursor, int column) { return cursor.getInt(column); } }; private static final CursorGetter STRING_GETTER = new CursorGetter() { + @Override public String get(Cursor cursor, int column) { return cursor.getString(column); } }; private static final CursorGetter BLOB_GETTER = new CursorGetter() { + @Override public byte[] get(Cursor cursor, int column) { return cursor.getBlob(column); } @@ -925,7 +930,7 @@ public class Utility { } finally { c.close(); } - } else { + } else if (accountId > 0L) { Mailbox mailbox = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_INBOX); diff --git a/src/com/android/email/NotificationController.java b/src/com/android/email/NotificationController.java index 17e90d5e0..178b3d3b6 100644 --- a/src/com/android/email/NotificationController.java +++ b/src/com/android/email/NotificationController.java @@ -75,8 +75,6 @@ public class NotificationController { /** Selection to retrieve accounts that should we notify user for changes */ private final static String NOTIFIED_ACCOUNT_SELECTION = Account.FLAGS + "&" + Account.FLAGS_NOTIFY_NEW_MAIL + " != 0"; - /** special account ID for the new message notification APIs to specify "all accounts" */ - private static final long ALL_ACCOUNTS = -1L; private static NotificationThread sNotificationThread; private static Handler sNotificationHandler; @@ -92,6 +90,12 @@ public class NotificationController { /** Maps account id to the message data */ private final HashMap mNotificationMap; private ContentObserver mAccountObserver; + /** + * Suspend notifications for this account. If {@link Account#PSEUDO_ACCOUNT_ID_NONE}, no + * account notifications are suspended. If {@link Account#ACCOUNT_ID_COMBINED_VIEW}, + * notifications for all accounts are suspended. + */ + private long mSuspendAccountId = Account.PSEUDO_ACCOUNT_ID_NONE; /** Constructor */ @VisibleForTesting @@ -192,18 +196,13 @@ public class NotificationController { * notification shown to the user. And, when we start observing database changes, we restore * the saved state. * @param watch If {@code true}, we register observers for all accounts whose settings have - * notifications enabled. Otherwise, all observers are unregistered with the database. + * notifications enabled. Otherwise, all observers are unregistered. */ public void watchForMessages(final boolean watch) { // Don't create the thread if we're only going to stop watching - if (!watch && sNotificationHandler == null) return; + if (!watch && sNotificationThread == null) return; - synchronized(sInstance) { - if (sNotificationHandler == null) { - sNotificationThread = new NotificationThread(); - sNotificationHandler = new Handler(sNotificationThread.getLooper()); - } - } + ensureHandlerExists(); // Run this on the message notification handler sNotificationHandler.post(new Runnable() { @Override @@ -211,7 +210,7 @@ public class NotificationController { ContentResolver resolver = mContext.getContentResolver(); HashMap table; if (!watch) { - unregisterMessageNotification(ALL_ACCOUNTS); + unregisterMessageNotification(Account.ACCOUNT_ID_COMBINED_VIEW); if (mAccountObserver != null) { resolver.unregisterContentObserver(mAccountObserver); mAccountObserver = null; @@ -219,13 +218,12 @@ public class NotificationController { // tear down the event loop sNotificationThread.quit(); - sNotificationHandler = null; sNotificationThread = null; return; } // otherwise, start new observers for all notified accounts - registerMessageNotification(ALL_ACCOUNTS); + registerMessageNotification(Account.ACCOUNT_ID_COMBINED_VIEW); // If we're already observing account changes, don't do anything else if (mAccountObserver == null) { mAccountObserver = new AccountContentObserver(sNotificationHandler, mContext); @@ -235,17 +233,61 @@ public class NotificationController { }); } + /** + * Temporarily suspend a single account from receiving notifications. NOTE: only a single + * account may ever be suspended at a time. So, if this method is invoked a second time, + * notifications for the previously suspended account will automatically be re-activated. + * @param suspend If {@code true}, suspend notifications for the given account. Otherwise, + * re-activate notifications for the previously suspended account. + * @param accountId The ID of the account. If this is the special account ID + * {@link Account#ACCOUNT_ID_COMBINED_VIEW}, notifications for all accounts are + * suspended. If {@code suspend} is {@code false}, the account ID is ignored. + */ + public void suspendMessageNotification(boolean suspend, long accountId) { + if (mSuspendAccountId != Account.PSEUDO_ACCOUNT_ID_NONE) { + // we're already suspending an account; un-suspend it + mSuspendAccountId = Account.PSEUDO_ACCOUNT_ID_NONE; + } + if (suspend && accountId != Account.PSEUDO_ACCOUNT_ID_NONE && accountId > 0L) { + mSuspendAccountId = accountId; + if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { + // Only go onto the notification handler if we really, absolutely need to + ensureHandlerExists(); + sNotificationHandler.post(new Runnable() { + @Override + public void run() { + for (long accountId : mNotificationMap.keySet()) { + mNotificationManager.cancel(getNewMessageNotificationId(accountId)); + } + } + }); + } else { + mNotificationManager.cancel(getNewMessageNotificationId(accountId)); + } + } + } + + /** + * Ensures the notification handler exists and is ready to handle requests. + */ + private static synchronized void ensureHandlerExists() { + if (sNotificationThread == null) { + sNotificationThread = new NotificationThread(); + sNotificationHandler = new Handler(sNotificationThread.getLooper()); + } + } + /** * Registers an observer for changes to the INBOX for the given account. Since accounts * may only have a single INBOX, we will never have more than one observer for an account. * NOTE: This must be called on the notification handler thread. * @param accountId The ID of the account to register the observer for. May be - * {@link #ALL_ACCOUNTS} to register observers for all accounts that allow - * for user notification. + * {@link Account#ACCOUNT_ID_COMBINED_VIEW} to register observers for all + * accounts that allow for user notification. */ private void registerMessageNotification(long accountId) { ContentResolver resolver = mContext.getContentResolver(); - if (accountId == ALL_ACCOUNTS) { + if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { Cursor c = resolver.query( Account.CONTENT_URI, EmailContent.ID_PROJECTION, NOTIFIED_ACCOUNT_SELECTION, null, null); @@ -282,12 +324,12 @@ public class NotificationController { * 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. + * @param accountId The ID of the account to unregister from. To unregister all accounts that + * have observers, specify an ID of {@link Account#ACCOUNT_ID_COMBINED_VIEW}. */ private void unregisterMessageNotification(long accountId) { ContentResolver resolver = mContext.getContentResolver(); - if (accountId == ALL_ACCOUNTS) { + if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { // cancel all existing message observers for (MessageData data : mNotificationMap.values()) { ContentObserver observer = data.mObserver; @@ -303,42 +345,6 @@ public class NotificationController { } } - /** - * 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 - * the user even if the Email UX is visible. - * NOTE: Only for short-term legacy support. To be replaced with a way to temporarily stop - * notifications for a mailbox. - * @deprecated - */ - @Deprecated - public void resetMessageNotification(final long accountId) { - synchronized(sInstance) { - if (sNotificationHandler == null) { - sNotificationThread = new NotificationThread(); - sNotificationHandler = new Handler(sNotificationThread.getLooper()); - } - } - - // Run this on the message notification handler - sNotificationHandler.post(new Runnable() { - private void resetNotification(long accountId) { - if (accountId == ALL_ACCOUNTS) { - for (long id : mNotificationMap.keySet()) { - resetNotification(id); - } - } else { - Utility.updateLastSeenMessageKey(mContext, accountId); - mNotificationManager.cancel(getNewMessageNotificationId(accountId)); - } - } - @Override - public void run() { - resetNotification(accountId); - } - }); - } - /** * Returns a picture of the sender of the given message. If no picture is available, returns * {@code null}. @@ -382,6 +388,7 @@ public class NotificationController { final String subject = message.mSubject; final Bitmap senderPhoto = getSenderPhoto(message); final SpannableString title = getNewMessageTitle(senderName, account.mDisplayName); + // TODO Set the intent according to number of unseen messages final Intent intent = Welcome.createOpenAccountInboxIntent(mContext, accountId); final Bitmap largeIcon = senderPhoto != null ? senderPhoto : mGenericSenderIcon; final Integer number = unseenMessageCount > 1 ? unseenMessageCount : null; @@ -581,7 +588,10 @@ public class NotificationController { @Override public void onChange(boolean selfChange) { - super.onChange(selfChange); + if (mAccountId == sInstance.mSuspendAccountId + || sInstance.mSuspendAccountId == Account.ACCOUNT_ID_COMBINED_VIEW) { + return; + } MessageData data = sInstance.mNotificationMap.get(mAccountId); if (data == null) { diff --git a/src/com/android/email/activity/MailboxListFragment.java b/src/com/android/email/activity/MailboxListFragment.java index 7450e1e29..4000b7f62 100644 --- a/src/com/android/email/activity/MailboxListFragment.java +++ b/src/com/android/email/activity/MailboxListFragment.java @@ -21,6 +21,7 @@ import com.android.email.Email; import com.android.email.R; import com.android.email.RefreshManager; import com.android.email.provider.EmailProvider; +import com.android.email.service.MailService; import com.android.emailcommon.Logging; import com.android.emailcommon.provider.EmailContent.Mailbox; import com.android.emailcommon.provider.EmailContent.Message; @@ -33,6 +34,7 @@ import android.app.LoaderManager; import android.app.LoaderManager.LoaderCallbacks; import android.content.ClipData; import android.content.ClipDescription; +import android.content.Context; import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; @@ -213,6 +215,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList * Called to do initial creation of a fragment. This is called after * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. */ + @SuppressWarnings("unused") @Override public void onCreate(Bundle savedInstanceState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -239,6 +242,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList return inflater.inflate(R.layout.mailbox_list_fragment, container, false); } + @SuppressWarnings("unused") @Override public void onActivityCreated(Bundle savedInstanceState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -274,6 +278,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList * Otherwise, only load the list of top-level mailboxes if the account changes. */ // STOPSHIP Make it private once phone activities are gone + @SuppressWarnings("unused") void openMailboxes(long accountId, long parentMailboxId) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "MailboxListFragment openMailboxes"); @@ -315,6 +320,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList /** * Called when the Fragment is visible to the user. */ + @SuppressWarnings("unused") @Override public void onStart() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -326,6 +332,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList /** * Called when the fragment is visible to the user and actively running. */ + @SuppressWarnings("unused") @Override public void onResume() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -341,6 +348,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList } } + @SuppressWarnings("unused") @Override public void onPause() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -349,12 +357,12 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList mResumed = false; super.onPause(); mSavedListState = getListView().onSaveInstanceState(); - Utility.updateLastSeenMessageKey(mActivity, mAccountId); } /** * Called when the Fragment is no longer started. */ + @SuppressWarnings("unused") @Override public void onStop() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -366,6 +374,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList /** * Called when the fragment is no longer in use. */ + @SuppressWarnings("unused") @Override public void onDestroy() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -374,6 +383,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList super.onDestroy(); } + @SuppressWarnings("unused") @Override public void onSaveInstanceState(Bundle outState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -384,6 +394,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList outState.putParcelable(BUNDLE_LIST_STATE, getListView().onSaveInstanceState()); } + @SuppressWarnings("unused") private void restoreInstanceState(Bundle savedInstanceState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "MailboxListFragment restoreInstanceState"); @@ -392,6 +403,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList mSavedListState = savedInstanceState.getParcelable(BUNDLE_LIST_STATE); } + @SuppressWarnings("unused") private void startLoading() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "MailboxListFragment startLoading"); @@ -408,6 +420,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList private class MailboxListLoaderCallbacks implements LoaderCallbacks { private boolean mIsFirstLoad; + @SuppressWarnings("unused") @Override public Loader onCreateLoader(int id, Bundle args) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -417,6 +430,7 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList return MailboxFragmentAdapter.createLoader(getActivity(), mAccountId, mParentMailboxId); } + @SuppressWarnings("unused") @Override public void onLoadFinished(Loader loader, Cursor cursor) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -466,8 +480,15 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList } } - public void onItemClick(AdapterView parent, View view, int position, - long idDontUseIt /* see MailboxesAdapter */ ) { + /** + * {@inheritDoc} + *

+ * @param doNotUse IMPORTANT: Do not use this parameter. The ID in the list widget + * must be a positive value. However, we rely on negative IDs for special mailboxes. Instead, + * we use the ID returned by {@link MailboxesAdapter#getId(int)}. + */ + @Override + public void onItemClick(AdapterView parent, View view, int position, long doNotUse) { final long id = mListAdapter.getId(position); if (mListAdapter.isAccountRow(position)) { mCallback.onAccountSelected(id); diff --git a/src/com/android/email/activity/MessageListFragment.java b/src/com/android/email/activity/MessageListFragment.java index 9fe6bbede..f8579ad42 100644 --- a/src/com/android/email/activity/MessageListFragment.java +++ b/src/com/android/email/activity/MessageListFragment.java @@ -18,11 +18,11 @@ package com.android.email.activity; import com.android.email.Controller; import com.android.email.Email; +import com.android.email.NotificationController; import com.android.email.R; import com.android.email.RefreshManager; import com.android.email.data.MailboxAccountLoader; import com.android.email.provider.EmailProvider; -import com.android.email.service.MailService; import com.android.emailcommon.Logging; import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent.Account; @@ -37,7 +37,6 @@ import android.app.ListFragment; import android.app.LoaderManager; import android.content.ClipData; import android.content.ContentUris; -import android.content.Context; import android.content.Loader; import android.content.res.Configuration; import android.content.res.Resources; @@ -238,6 +237,7 @@ public class MessageListFragment extends ListFragment return instance; } + @SuppressWarnings("unused") @Override public void onCreate(Bundle savedInstanceState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -261,6 +261,7 @@ public class MessageListFragment extends ListFragment return root; } + @SuppressWarnings("unused") @Override public void onActivityCreated(Bundle savedInstanceState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -292,6 +293,7 @@ public class MessageListFragment extends ListFragment } } + @SuppressWarnings("unused") @Override public void onStart() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -300,6 +302,7 @@ public class MessageListFragment extends ListFragment super.onStart(); } + @SuppressWarnings("unused") @Override public void onResume() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -307,6 +310,7 @@ public class MessageListFragment extends ListFragment } super.onResume(); mResumed = true; + adjustMessageNotification(false); // If we're recovering from the stopped state, we don't have to reload. // (when mOpenRequested = false) @@ -315,6 +319,7 @@ public class MessageListFragment extends ListFragment } } + @SuppressWarnings("unused") @Override public void onPause() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -323,8 +328,10 @@ public class MessageListFragment extends ListFragment mResumed = false; super.onStop(); mSavedListState = getListView().onSaveInstanceState(); + adjustMessageNotification(true); } + @SuppressWarnings("unused") @Override public void onStop() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -333,6 +340,7 @@ public class MessageListFragment extends ListFragment super.onStop(); } + @SuppressWarnings("unused") @Override public void onDestroy() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -349,6 +357,7 @@ public class MessageListFragment extends ListFragment super.onDestroy(); } + @SuppressWarnings("unused") @Override public void onSaveInstanceState(Bundle outState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -360,6 +369,7 @@ public class MessageListFragment extends ListFragment outState.putLong(BUNDLE_KEY_SELECTED_MESSAGE_ID, mSelectedMessageId); } + @SuppressWarnings("unused") @VisibleForTesting void restoreInstanceState(Bundle savedInstanceState) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -414,6 +424,7 @@ public class MessageListFragment extends ListFragment * {@link Mailbox#QUERY_ALL_INBOXES}. -1 is not allowed. */ // STOPSHIP Make it private once phone activities are gone + @SuppressWarnings("unused") void openMailbox(long mailboxId) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "MessageListFragment openMailbox"); @@ -446,10 +457,15 @@ public class MessageListFragment extends ListFragment } /** - * @return the account id or -1 if it's unknown yet. It's also -1 if it's a magic mailbox. + * Returns the account id of the currently viewed messages. May return + * {@link Account#PSEUDO_ACCOUNT_ID_NONE} if the mailbox is not yet known or + * {@link Account#ACCOUNT_ID_COMBINED_VIEW} if viewing a combined mailbox. */ - public long getAccountId() { - return (mMailbox == null) ? -1 : mMailbox.mAccountKey; + private long getAccountId() { + return (mMailbox == null) + ? (mMailboxId < Mailbox.NO_MAILBOX) + ? Account.ACCOUNT_ID_COMBINED_VIEW : Account.PSEUDO_ACCOUNT_ID_NONE + : mMailbox.mAccountKey; } /** @@ -492,6 +508,7 @@ public class MessageListFragment extends ListFragment /** * Called when a message is clicked. */ + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (view != mListFooterView) { MessageListItem itemView = (MessageListItem) view; @@ -593,6 +610,7 @@ public class MessageListFragment extends ListFragment } } + @Override public boolean onDrag(View view, DragEvent event) { switch(event.getAction()) { case DragEvent.ACTION_DRAG_ENDED: @@ -614,6 +632,7 @@ public class MessageListFragment extends ListFragment return false; } + @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view != mListFooterView) { // Start drag&drop. @@ -732,7 +751,7 @@ public class MessageListFragment extends ListFragment return; } long accountId = getAccountId(); - if (accountId != -1) { + if (Account.isNormalAccount(accountId)) { mRefreshManager.refreshMessageList(accountId, mMailboxId, userRequest); } } @@ -753,7 +772,7 @@ public class MessageListFragment extends ListFragment */ private void onLoadMoreMessages() { long accountId = getAccountId(); - if (accountId != -1) { + if (Account.isNormalAccount(accountId)) { mRefreshManager.loadMoreMessages(accountId, mMailboxId); } } @@ -794,10 +813,12 @@ public class MessageListFragment extends ListFragment private void toggleRead(Set selectedSet) { toggleMultiple(selectedSet, new MultiToggleHelper() { + @Override public boolean getField(long messageId, Cursor c) { return c.getInt(MessagesAdapter.COLUMN_READ) == 0; } + @Override public boolean setField(long messageId, Cursor c, boolean newValue) { boolean oldValue = getField(messageId, c); if (oldValue != newValue) { @@ -817,10 +838,12 @@ public class MessageListFragment extends ListFragment private void toggleFavorite(Set selectedSet) { toggleMultiple(selectedSet, new MultiToggleHelper() { + @Override public boolean getField(long messageId, Cursor c) { return c.getInt(MessagesAdapter.COLUMN_FAVORITE) != 0; } + @Override public boolean setField(long messageId, Cursor c, boolean newValue) { boolean oldValue = getField(messageId, c); if (oldValue != newValue) { @@ -1058,6 +1081,28 @@ public class MessageListFragment extends ListFragment showNoMessageText(noItem); } + /** + * Adjusts message notification depending upon the state of the fragment and the currently + * viewed mailbox. If the fragment is resumed, notifications for the current mailbox may + * be suspended. Otherwise, notifications may be re-activated. Not all mailbox types are + * supported for notifications. These include (but are not limited to) special mailboxes + * such as {@link Mailbox#QUERY_ALL_DRAFTS}, {@link Mailbox#QUERY_ALL_FAVORITES}, etc... + * + * @param updateLastSeenKey If {@code true}, the last seen message key for the currently + * viewed mailbox will be updated. + */ + private void adjustMessageNotification(boolean updateLastSeenKey) { + if (mMailboxId == Mailbox.QUERY_ALL_INBOXES || mMailboxId > 0) { + long id = getAccountId(); + if (updateLastSeenKey) { + Utility.updateLastSeenMessageKey(mActivity, id); + } + NotificationController notifier = NotificationController.getInstance(mActivity); + notifier.suspendMessageNotification(mResumed, id); + } + } + + @SuppressWarnings("unused") private void startLoading() { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "MessageListFragment startLoading"); @@ -1079,6 +1124,7 @@ public class MessageListFragment extends ListFragment */ private class MailboxAccountLoaderCallback implements LoaderManager.LoaderCallbacks< MailboxAccountLoader.Result> { + @SuppressWarnings("unused") @Override public Loader onCreateLoader(int id, Bundle args) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -1088,6 +1134,7 @@ public class MessageListFragment extends ListFragment return new MailboxAccountLoader(getActivity().getApplicationContext(), mMailboxId); } + @SuppressWarnings("unused") @Override public void onLoadFinished(Loader loader, MailboxAccountLoader.Result result) { @@ -1120,6 +1167,7 @@ public class MessageListFragment extends ListFragment private class MessagesLoaderCallback implements LoaderManager.LoaderCallbacks { private boolean mIsFirstLoad; + @SuppressWarnings("unused") @Override public Loader onCreateLoader(int id, Bundle args) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -1130,6 +1178,7 @@ public class MessageListFragment extends ListFragment return MessagesAdapter.createLoader(getActivity(), mMailboxId); } + @SuppressWarnings("unused") @Override public void onLoadFinished(Loader loader, Cursor cursor) { if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { @@ -1137,6 +1186,9 @@ public class MessageListFragment extends ListFragment "MessageListFragment onLoadFinished(messages) mailboxId=" + mMailboxId); } + // Suspend message notifications as long as we're resumed + adjustMessageNotification(false); + // Save list view state (primarily scroll position) final ListView lv = getListView(); final Parcelable listState; @@ -1178,8 +1230,6 @@ public class MessageListFragment extends ListFragment lv.onRestoreInstanceState(listState); } - resetNewMessageCount(mActivity, mMailboxId, getAccountId()); - // Clear this for next reload triggered by content changed events. mIsFirstLoad = false; @@ -1192,26 +1242,6 @@ public class MessageListFragment extends ListFragment } } - /** - * Reset the "new message" count. - *

    - *
  • If {@code mailboxId} is {@link Mailbox#QUERY_ALL_INBOXES}, reset the - * counts of all accounts. - *
  • If {@code mailboxId} is not of a magic inbox (i.e. >= 0) and {@code - * accountId} is valid, reset the count of the specified account. - *
- * TODO Instead of resetting the message count, we should just be suspending the notification - * controller for the correct accounts. Need to ensure we resume notifications appropriately. - */ - private static void resetNewMessageCount( - Context context, long mailboxId, long accountId) { - if (mailboxId == Mailbox.QUERY_ALL_INBOXES) { - MailService.resetNewMessageCount(context, -1); - } else if (mailboxId >= 0 && accountId != -1) { - MailService.resetNewMessageCount(context, accountId); - } - } - /** * Show/hide the "selection" action mode, according to the number of selected messages and * the visibility of the fragment. diff --git a/src/com/android/email/activity/UIControllerBase.java b/src/com/android/email/activity/UIControllerBase.java index 2c24828d9..2d6f4aae8 100644 --- a/src/com/android/email/activity/UIControllerBase.java +++ b/src/com/android/email/activity/UIControllerBase.java @@ -49,7 +49,7 @@ abstract class UIControllerBase { protected static final String BUNDLE_KEY_MESSAGE_ID = "UIController.state.message_id"; /** No account selected */ - static final long NO_ACCOUNT = -1; + static final long NO_ACCOUNT = Account.PSEUDO_ACCOUNT_ID_NONE; /** No mailbox selected */ static final long NO_MAILBOX = -1; /** No message selected */ diff --git a/src/com/android/email/service/MailService.java b/src/com/android/email/service/MailService.java index 417c19167..47bf9b94b 100644 --- a/src/com/android/email/service/MailService.java +++ b/src/com/android/email/service/MailService.java @@ -143,17 +143,6 @@ public class MailService extends Service { context.startService(i); } - /** - * Reset new message counts for one or all accounts. This clears both our local copy and - * the values (if any) stored in the account records. - * - * @param accountId account to clear, or -1 for all accounts - */ - @SuppressWarnings("deprecation") - public static void resetNewMessageCount(final Context context, final long accountId) { - NotificationController.getInstance(context).resetMessageNotification(accountId); - } - /** * 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 @@ -162,7 +151,7 @@ public class MailService extends Service { * @param context a context * @param accountId the id of the account that is reporting new messages */ - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") public static void actionNotifyNewMessages( Context context, long accountId, List messageIdList) { Intent i = new Intent(ACTION_NOTIFY_MAIL);