Suspend notifications for currently shown account

We will suspend notifications whenever we display the message list for an
account (including "combined inbox").  As soon as the message list is paused,
notifications will be resumed.

Change-Id: I481a0f59ce68f89c32210d862d0267f3f334063b
This commit is contained in:
Todd Kennedy 2011-05-12 10:13:45 -07:00
parent ffeb7de284
commit 5701e0a555
7 changed files with 189 additions and 111 deletions

View File

@ -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.
* <em>IMPORTANT</em>: 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.
*
* <em>IMPORTANT</em>: 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<EmailContent.Account> CREATOR
= new Parcelable.Creator<EmailContent.Account>() {
@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<EmailContent.Attachment> CREATOR
= new Parcelable.Creator<EmailContent.Attachment>() {
@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<EmailContent.HostAuth> CREATOR
= new Parcelable.Creator<EmailContent.HostAuth>() {
@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);

View File

@ -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> LONG_GETTER = new CursorGetter<Long>() {
@Override
public Long get(Cursor cursor, int column) {
return cursor.getLong(column);
}
};
private static final CursorGetter<Integer> INT_GETTER = new CursorGetter<Integer>() {
@Override
public Integer get(Cursor cursor, int column) {
return cursor.getInt(column);
}
};
private static final CursorGetter<String> STRING_GETTER = new CursorGetter<String>() {
@Override
public String get(Cursor cursor, int column) {
return cursor.getString(column);
}
};
private static final CursorGetter<byte[]> BLOB_GETTER = new CursorGetter<byte[]>() {
@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);

View File

@ -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<Long, MessageData> 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<Long, long[]> 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) {

View File

@ -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<Cursor> {
private boolean mIsFirstLoad;
@SuppressWarnings("unused")
@Override
public Loader<Cursor> 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<Cursor> 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}
* <p>
* @param doNotUse <em>IMPORTANT</em>: 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);

View File

@ -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<Long> 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<Long> 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<MailboxAccountLoader.Result> 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<MailboxAccountLoader.Result> loader,
MailboxAccountLoader.Result result) {
@ -1120,6 +1167,7 @@ public class MessageListFragment extends ListFragment
private class MessagesLoaderCallback implements LoaderManager.LoaderCallbacks<Cursor> {
private boolean mIsFirstLoad;
@SuppressWarnings("unused")
@Override
public Loader<Cursor> 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<Cursor> 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.
* <ul>
* <li>If {@code mailboxId} is {@link Mailbox#QUERY_ALL_INBOXES}, reset the
* counts of all accounts.
* <li>If {@code mailboxId} is not of a magic inbox (i.e. >= 0) and {@code
* accountId} is valid, reset the count of the specified account.
* </ul>
* 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.

View File

@ -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 */

View File

@ -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);