diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1f1496558..789716c9b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -163,6 +163,41 @@
- in %d accounts
+
+
+ - %1$s
+ + %2$d more
+ - %1$s
+ + %2$d more
+
+
+
+
+ - %1$d
new
+ - %1$d
new
+
+
+
+
+ - %1$d new
+ %2$s
+ - %1$d new
+ %2$s
+
+
Inbox
diff --git a/src/com/android/email/NotificationController.java b/src/com/android/email/NotificationController.java
new file mode 100644
index 000000000..dd7e35e89
--- /dev/null
+++ b/src/com/android/email/NotificationController.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.email;
+
+import com.android.email.activity.ContactStatusLoader;
+import com.android.email.activity.Welcome;
+import com.android.email.mail.Address;
+import com.android.email.provider.EmailContent;
+import com.android.email.provider.EmailContent.Account;
+import com.android.email.provider.EmailContent.Message;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.text.TextUtils;
+
+/**
+ * Class that manages notifications.
+ *
+ * TODO Gather all notification related code here
+ */
+public class NotificationController {
+ public static final int NOTIFICATION_ID_SECURITY_NEEDED = 1;
+ public static final int NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED = 2;
+ public static final int NOTIFICATION_ID_WARNING = 3;
+ private static final int NOTIFICATION_ID_NEW_MESSAGES_BASE = 10;
+
+ private static NotificationController sInstance;
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final AudioManager mAudioManager;
+
+ /** Constructor */
+ private NotificationController(Context context) {
+ mContext = context.getApplicationContext();
+ mNotificationManager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ /** Singleton access */
+ public static synchronized NotificationController getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new NotificationController(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * @return the "new message" notification ID for an account. It just assumes
+ * accountID won't be too huge. Any other smarter/cleaner way?
+ */
+ private int getNewMessageNotificationId(long accountId) {
+ return (int) (NOTIFICATION_ID_NEW_MESSAGES_BASE + accountId);
+ }
+
+ /**
+ * Dismiss new message notification
+ *
+ * @param accountId ID of the target account, or -1 for all accounts.
+ */
+ public void cancelNewMessageNotification(long accountId) {
+ if (accountId == -1) {
+ new Utility.ForEachAccount(mContext) {
+ @Override
+ protected void performAction(long accountId) {
+ cancelNewMessageNotification(accountId);
+ }
+ }.execute();
+ } else {
+ mNotificationManager.cancel(getNewMessageNotificationId(accountId));
+ }
+ }
+
+ /**
+ * Show (or update) the "new message" notification.
+ */
+ public void showNewMessageNotification(final long accountId, final int unseenMessageCount,
+ final int justFetchedCount) {
+ Utility.runAsync(new Runnable() {
+ @Override
+ public void run() {
+ Notification n = createNewMessageNotification(accountId, unseenMessageCount,
+ justFetchedCount);
+ if (n == null) {
+ return;
+ }
+ mNotificationManager.notify(getNewMessageNotificationId(accountId), n);
+ }
+ });
+ }
+
+ /**
+ * @return The sender's photo, if available, or null.
+ *
+ * Don't call it on the UI thread.
+ */
+ private Bitmap getSenderPhoto(Message message) {
+ Address sender = Address.unpackFirst(message.mFrom);
+ if (sender == null) {
+ return null;
+ }
+ String email = sender.getAddress();
+ if (TextUtils.isEmpty(email)) {
+ return null;
+ }
+ return ContactStatusLoader.load(mContext, email).mPhoto;
+ }
+
+ private Bitmap[] getNotificationBitmaps(Bitmap senderPhoto) {
+ // TODO Should we cache these objects? (bitmaps and arrays)
+ // They don't have to be on this process's memory once we post a notification request to
+ // the system, and decodeResource() seems to be reasonably fast. We don't want them to
+ // take up memory when not necessary.
+ Bitmap appIcon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.icon);
+ if (senderPhoto == null) {
+ return new Bitmap[] {appIcon};
+ } else {
+ return new Bitmap[] {senderPhoto, appIcon};
+ }
+ }
+
+ /**
+ * Create a notification
+ *
+ * Don't call it on the UI thread.
+ *
+ * TODO Test it when the UI is settled.
+ */
+ private Notification createNewMessageNotification(long accountId, int unseenMessageCount,
+ int justFetchedCount) {
+ final Account account = Account.restoreAccountWithId(mContext, accountId);
+ if (account == null) {
+ return null;
+ }
+ // Get the latest message
+ final Message message = Message.getLatestMessage(mContext, accountId);
+ if (message == null) {
+ return null; // no message found???
+ }
+
+ final String senderName = Address.toFriendly(Address.unpack(message.mFrom));
+ final String subject = message.mSubject;
+ final Bitmap senderPhoto = getSenderPhoto(message);
+
+ // Intent to open inbox
+ PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
+ Welcome.createOpenAccountInboxIntent(mContext, accountId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ final String notificationTitle;
+ if (justFetchedCount == 1) {
+ notificationTitle = senderName;
+ } else {
+ notificationTitle = mContext.getResources().getQuantityString(
+ R.plurals.notification_sender_name_multi_messages, justFetchedCount - 1,
+ senderName, justFetchedCount - 1);
+ }
+ final String content = subject;
+ final String numNewMessages;
+ final int numAccounts = EmailContent.count(mContext, Account.CONTENT_URI);
+ if (numAccounts == 1) {
+ numNewMessages = mContext.getResources().getQuantityString(
+ R.plurals.notification_num_new_messages_single_account, unseenMessageCount,
+ unseenMessageCount, account.mDisplayName);
+ } else {
+ numNewMessages = mContext.getResources().getQuantityString(
+ R.plurals.notification_num_new_messages_multi_account, unseenMessageCount,
+ unseenMessageCount, account.mDisplayName);
+ }
+
+ Notification notification = new Notification(R.drawable.stat_notify_email_generic,
+ mContext.getString(R.string.notification_new_title), System.currentTimeMillis());
+ notification.setLatestEventInfo(mContext, notificationTitle, subject, contentIntent);
+
+ notification.tickerTitle = notificationTitle;
+ // STOPSHIPO numNewMessages should be the 3rd line on expanded notification. But it's not
+ // clear how to do that yet.
+ // For now we just append it to subject.
+ notification.tickerSubtitle = subject + " " + numNewMessages;
+ notification.tickerIcons = getNotificationBitmaps(senderPhoto);
+
+ setupNotificationSoundAndVibrationFromAccount(notification, account);
+ return notification;
+ }
+
+ private boolean isRingerModeSilent() {
+ return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ }
+
+ private void setupNotificationSoundAndVibrationFromAccount(Notification notification,
+ Account account) {
+ final int flags = account.mFlags;
+ final String ringtoneUri = account.mRingtoneUri;
+ final boolean vibrate = (flags & Account.FLAGS_VIBRATE_ALWAYS) != 0;
+ final boolean vibrateWhenSilent = (flags & Account.FLAGS_VIBRATE_WHEN_SILENT) != 0;
+
+ notification.sound = (ringtoneUri == null) ? null : Uri.parse(ringtoneUri);
+
+ if (vibrate || (vibrateWhenSilent && isRingerModeSilent())) {
+ notification.defaults |= Notification.DEFAULT_VIBRATE;
+ }
+
+ // This code is identical to that used by Gmail and GTalk for notifications
+ notification.flags |= Notification.FLAG_SHOW_LIGHTS;
+ notification.defaults |= Notification.DEFAULT_LIGHTS;
+ }
+}
diff --git a/src/com/android/email/SecurityPolicy.java b/src/com/android/email/SecurityPolicy.java
index 4daaf7236..5f9250016 100644
--- a/src/com/android/email/SecurityPolicy.java
+++ b/src/com/android/email/SecurityPolicy.java
@@ -363,7 +363,8 @@ public class SecurityPolicy {
NotificationManager notificationManager =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(MailService.NOTIFICATION_ID_SECURITY_NEEDED, notification);
+ notificationManager.notify(NotificationController.NOTIFICATION_ID_SECURITY_NEEDED,
+ notification);
}
/**
@@ -373,7 +374,7 @@ public class SecurityPolicy {
public void clearNotification(long accountId) {
NotificationManager notificationManager =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(MailService.NOTIFICATION_ID_SECURITY_NEEDED);
+ notificationManager.cancel(NotificationController.NOTIFICATION_ID_SECURITY_NEEDED);
}
/**
diff --git a/src/com/android/email/activity/AccountFolderList.java b/src/com/android/email/activity/AccountFolderList.java
index cdf92e08b..8af544b75 100644
--- a/src/com/android/email/activity/AccountFolderList.java
+++ b/src/com/android/email/activity/AccountFolderList.java
@@ -19,6 +19,7 @@ package com.android.email.activity;
import com.android.email.Controller;
import com.android.email.ControllerResultUiThreadWrapper;
import com.android.email.Email;
+import com.android.email.NotificationController;
import com.android.email.R;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.email.activity.setup.AccountSetupBasics;
@@ -26,7 +27,6 @@ import com.android.email.mail.MessagingException;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Mailbox;
-import com.android.email.service.MailService;
import android.app.Activity;
import android.app.AlertDialog;
@@ -37,7 +37,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
-import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -190,6 +189,7 @@ public class AccountFolderList extends Activity implements AccountFolderListFrag
}
private Dialog createRemoveAccountDialog() {
+ final Activity activity = this;
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.account_delete_dlg_title)
@@ -197,17 +197,18 @@ public class AccountFolderList extends Activity implements AccountFolderListFrag
mSelectedContextAccount.getDisplayName()))
.setPositiveButton(R.string.okay_action, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
+ final long accountId = mSelectedContextAccount.mId;
dismissDialog(DIALOG_REMOVE_ACCOUNT);
- // Clear notifications, which may become stale here
- MailService.cancelNewMessageNotification(AccountFolderList.this);
- int numAccounts = EmailContent.count(AccountFolderList.this,
+ // Dismiss new message notification.
+ NotificationController.getInstance(activity)
+ .cancelNewMessageNotification(accountId);
+ int numAccounts = EmailContent.count(activity,
Account.CONTENT_URI, null, null);
mListFragment.hideDeletingAccount(mSelectedContextAccount.mId);
- Controller.getInstance(AccountFolderList.this).deleteAccount(
- mSelectedContextAccount.mId);
+ Controller.getInstance(activity).deleteAccount(accountId);
if (numAccounts == 1) {
- AccountSetupBasics.actionNewAccount(AccountFolderList.this);
+ AccountSetupBasics.actionNewAccount(activity);
finish();
}
}
diff --git a/src/com/android/email/activity/MessageList.java b/src/com/android/email/activity/MessageList.java
index 8f97cc9fa..6a8065612 100644
--- a/src/com/android/email/activity/MessageList.java
+++ b/src/com/android/email/activity/MessageList.java
@@ -19,6 +19,7 @@ package com.android.email.activity;
import com.android.email.Controller;
import com.android.email.ControllerResultUiThreadWrapper;
import com.android.email.Email;
+import com.android.email.NotificationController;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.activity.setup.AccountSecurity;
@@ -29,10 +30,8 @@ import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
-import com.android.email.service.MailService;
import android.app.Activity;
-import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -237,7 +236,8 @@ public class MessageList extends Activity implements OnClickListener,
mController.addResultCallback(mControllerCallback);
// clear notifications here
- MailService.cancelNewMessageNotification(this);
+ NotificationController.getInstance(this).cancelNewMessageNotification(
+ mListFragment.getAccountId());
// Exit immediately if the accounts list has changed (e.g. externally deleted)
if (Email.getNotifyUiAccountsChanged()) {
diff --git a/src/com/android/email/activity/MessageListXL.java b/src/com/android/email/activity/MessageListXL.java
index 69922341d..eda5b0321 100644
--- a/src/com/android/email/activity/MessageListXL.java
+++ b/src/com/android/email/activity/MessageListXL.java
@@ -18,6 +18,7 @@ package com.android.email.activity;
import com.android.email.Clock;
import com.android.email.Email;
+import com.android.email.NotificationController;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.RefreshManager;
@@ -26,7 +27,6 @@ import com.android.email.activity.setup.AccountSecurity;
import com.android.email.activity.setup.AccountSettingsXL;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Mailbox;
-import com.android.email.service.MailService;
import android.app.ActionBar;
import android.app.Activity;
@@ -174,7 +174,8 @@ public class MessageListXL extends Activity implements
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) Log.d(Email.LOG_TAG, "MessageListXL onResume");
super.onResume();
- MailService.cancelNewMessageNotification(this);
+ NotificationController.getInstance(this).cancelNewMessageNotification(
+ mFragmentManager.getAccountId());
// On MessageList.onResume, we go back to Welcome if an account has been added/removed.
// We don't need to do that here, because when the activity resumes, the account list loader
diff --git a/src/com/android/email/activity/setup/AccountSettingsXL.java b/src/com/android/email/activity/setup/AccountSettingsXL.java
index a5c5a5d5d..fdf4e9ea3 100644
--- a/src/com/android/email/activity/setup/AccountSettingsXL.java
+++ b/src/com/android/email/activity/setup/AccountSettingsXL.java
@@ -18,6 +18,7 @@ package com.android.email.activity.setup;
import com.android.email.Controller;
import com.android.email.Email;
+import com.android.email.NotificationController;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.mail.Sender;
@@ -25,7 +26,6 @@ import com.android.email.mail.Store;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
-import com.android.email.service.MailService;
import android.app.Activity;
import android.app.AlertDialog;
@@ -558,8 +558,8 @@ public class AccountSettingsXL extends PreferenceActivity implements OnClickList
public void deleteAccount(Account account) {
// Kick off the work to actually delete the account
- // Clear notifications, which may become stale here
- MailService.cancelNewMessageNotification(this);
+ // 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);
diff --git a/src/com/android/email/provider/EmailContent.java b/src/com/android/email/provider/EmailContent.java
index 110b0ebed..53f4eec62 100644
--- a/src/com/android/email/provider/EmailContent.java
+++ b/src/com/android/email/provider/EmailContent.java
@@ -32,6 +32,7 @@ import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.text.TextUtils;
import java.io.File;
import java.net.URI;
@@ -450,6 +451,7 @@ public abstract class EmailContent {
// To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
+ public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
public static final Uri SYNCED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
public static final Uri DELETED_CONTENT_URI =
@@ -539,6 +541,9 @@ public abstract class EmailContent {
private static final String FAVORITE_COUNT_SELECTION =
MessageColumns.FLAG_FAVORITE + "= 1";
+ private static final String ACCOUNT_KEY_SELECTION =
+ MessageColumns.ACCOUNT_KEY + "=?";
+
// _id field is in AbstractContent
public String mDisplayName;
public long mTimeStamp;
@@ -829,6 +834,26 @@ public abstract class EmailContent {
}
return -1;
}
+
+ /**
+ * @return the latest messages on an account.
+ */
+ public static Message getLatestMessage(Context context, Long accountId) {
+ Cursor c = context.getContentResolver().query(Message.CONTENT_URI_LIMIT_1,
+ Message.CONTENT_PROJECTION,
+ ACCOUNT_KEY_SELECTION, new String[] {Long.toString(accountId)},
+ EmailContent.MessageColumns.TIMESTAMP + " DESC");
+ try {
+ if (c.moveToFirst()) {
+ Message m = new Message();
+ m.restore(c);
+ return m;
+ }
+ } finally {
+ c.close();
+ }
+ return null; // not found;
+ }
}
public interface AccountColumns {
diff --git a/src/com/android/email/service/AttachmentDownloadService.java b/src/com/android/email/service/AttachmentDownloadService.java
index f59748d7f..2e0017f11 100644
--- a/src/com/android/email/service/AttachmentDownloadService.java
+++ b/src/com/android/email/service/AttachmentDownloadService.java
@@ -16,11 +16,12 @@
package com.android.email.service;
+import com.android.email.Controller.ControllerService;
import com.android.email.Email;
+import com.android.email.ExchangeUtils.NullEmailService;
+import com.android.email.NotificationController;
import com.android.email.R;
import com.android.email.Utility;
-import com.android.email.Controller.ControllerService;
-import com.android.email.ExchangeUtils.NullEmailService;
import com.android.email.activity.Welcome;
import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent;
@@ -466,7 +467,7 @@ public class AttachmentDownloadService extends Service implements Runnable {
n.flags = Notification.FLAG_AUTO_CANCEL;
NotificationManager nm =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(MailService.NOTIFICATION_ID_WARNING, n);
+ nm.notify(NotificationController.NOTIFICATION_ID_WARNING, n);
}
/**
diff --git a/src/com/android/email/service/MailService.java b/src/com/android/email/service/MailService.java
index cb775c399..1f36993a1 100644
--- a/src/com/android/email/service/MailService.java
+++ b/src/com/android/email/service/MailService.java
@@ -19,16 +19,20 @@ package com.android.email.service;
import com.android.email.AccountBackupRestore;
import com.android.email.Controller;
import com.android.email.Email;
+import com.android.email.NotificationController;
import com.android.email.R;
import com.android.email.SecurityPolicy;
import com.android.email.Utility;
+import com.android.email.activity.ContactStatusLoader;
import com.android.email.activity.Welcome;
+import com.android.email.mail.Address;
import com.android.email.mail.MessagingException;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.HostAuth;
import com.android.email.provider.EmailContent.Mailbox;
+import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailProvider;
import android.accounts.AccountManager;
@@ -44,11 +48,12 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SyncStatusObserver;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.Uri;
@@ -56,6 +61,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
@@ -73,11 +79,6 @@ public class MailService extends Service {
private static final String LOG_TAG = "Email-MailService";
- private static final int NOTIFICATION_ID_NEW_MESSAGES = 1;
- public static final int NOTIFICATION_ID_SECURITY_NEEDED = 2;
- public static final int NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED = 3;
- public static final int NOTIFICATION_ID_WARNING = 4;
-
private static final String ACTION_CHECK_MAIL =
"com.android.email.intent.action.MAIL_SERVICE_WAKEUP";
private static final String ACTION_RESCHEDULE =
@@ -149,7 +150,7 @@ public class MailService extends Service {
synchronized (mSyncReports) {
for (AccountSyncReport report : mSyncReports.values()) {
if (accountId == -1 || accountId == report.accountId) {
- report.numNewMessages = 0;
+ report.unseenMessageCount = 0;
}
}
}
@@ -182,12 +183,6 @@ public class MailService extends Service {
context.startService(i);
}
- public static void cancelNewMessageNotification(Context context) {
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(MailService.NOTIFICATION_ID_NEW_MESSAGES);
- }
-
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
@@ -282,7 +277,7 @@ public class MailService extends Service {
// As a precaution, clear any outstanding Email notifications
// We could be smarter and only do this when the list of accounts changes,
// but that's an edge condition and this is much safer.
- cancelNewMessageNotification(this);
+ NotificationController.getInstance(this).cancelNewMessageNotification(-1);
// When called externally, we refresh the sync reports table to pick up
// any changes in the account list or account settings
@@ -496,22 +491,34 @@ public class MailService extends Service {
long accountId;
long prevSyncTime; // 0 == unknown
long nextSyncTime; // 0 == ASAP -1 == don't sync
- int numNewMessages;
+
+ /** # of "unseen" messages to show in notification */
+ int unseenMessageCount;
+
+ /**
+ * # of unseen, the value shown on the last notification. Used to
+ * calculate "the number of messages that have just been fetched".
+ *
+ * TODO It's a sort of cheating. Should we use the "real" number? The only difference
+ * is the first notification after reboot / process restart.
+ */
+ int lastUnseenMessageCount;
int syncInterval;
boolean notify;
- boolean vibrate;
- boolean vibrateWhenSilent;
- Uri ringtoneUri;
- String accountDisplayName;
boolean syncEnabled; // whether auto sync is enabled for this account
+ /** # of messages that have just been fetched */
+ int getJustFetchedMessageCount() {
+ return unseenMessageCount - lastUnseenMessageCount;
+ }
@Override
public String toString() {
- return accountDisplayName + ": id=" + accountId + " prevSync=" + prevSyncTime
- + " nextSync=" + nextSyncTime + " numNew=" + numNewMessages;
+ return "id=" + accountId
+ + " prevSync=" + prevSyncTime + " nextSync=" + nextSyncTime + " numUnseen="
+ + unseenMessageCount;
}
}
@@ -560,7 +567,6 @@ public class MailService extends Service {
int syncInterval = c.getInt(Account.CONTENT_SYNC_INTERVAL_COLUMN);
int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN);
long id = c.getInt(Account.CONTENT_ID_COLUMN);
- String ringtoneString = c.getString(Account.CONTENT_RINGTONE_URI_COLUMN);
// If we're not using MessagingController (EAS at this point), don't schedule syncs
if (!mController.isMessagingController(id)) {
@@ -572,16 +578,10 @@ public class MailService extends Service {
report.accountId = c.getLong(Account.CONTENT_ID_COLUMN);
report.prevSyncTime = 0;
report.nextSyncTime = (syncInterval > 0) ? 0 : -1; // 0 == ASAP -1 == no sync
- report.numNewMessages = 0;
+ report.unseenMessageCount = 0;
report.syncInterval = syncInterval;
report.notify = (flags & Account.FLAGS_NOTIFY_NEW_MAIL) != 0;
- report.vibrate = (flags & Account.FLAGS_VIBRATE_ALWAYS) != 0;
- report.vibrateWhenSilent = (flags & Account.FLAGS_VIBRATE_WHEN_SILENT) != 0;
- report.ringtoneUri = (ringtoneString == null) ? null
- : Uri.parse(ringtoneString);
-
- report.accountDisplayName = c.getString(Account.CONTENT_DISPLAY_NAME_COLUMN);
// See if the account is enabled for sync in AccountManager
Account providerAccount = Account.restoreAccountWithId(this, id);
@@ -623,7 +623,7 @@ public class MailService extends Service {
report.nextSyncTime = report.prevSyncTime + (report.syncInterval * 1000 * 60);
}
if (newCount != -1) {
- report.numNewMessages = newCount;
+ report.unseenMessageCount = newCount;
}
if (Config.LOGD && Email.DEBUG) {
Log.d(LOG_TAG, "update account " + report.toString());
@@ -709,114 +709,23 @@ public class MailService extends Service {
}
/**
- * Prepare notifications for a given new account having received mail
- * The notification is organized around the account that has the new mail (e.g. selecting
- * the alert preferences) but the notification will include a summary if other
- * accounts also have new mail.
+ * Show "new message" notification for an account. (Notification is shown per account.)
*/
- private void notifyNewMessages(long accountId) {
- boolean notify = false;
- boolean vibrate = false;
- boolean vibrateWhenSilent = false;
- Uri ringtone = null;
- int accountsWithNewMessages = 0;
- int numNewMessages = 0;
- String reportName = null;
+ private void notifyNewMessages(final long accountId) {
+ final int unseenMessageCount;
+ final int justFetchedCount;
synchronized (mSyncReports) {
- for (AccountSyncReport report : mSyncReports.values()) {
- if (report.numNewMessages == 0) {
- continue;
- }
- numNewMessages += report.numNewMessages;
- accountsWithNewMessages += 1;
- if (report.accountId == accountId) {
- notify = report.notify;
- vibrate = report.vibrate;
- vibrateWhenSilent = report.vibrateWhenSilent;
- ringtone = report.ringtoneUri;
- reportName = report.accountDisplayName;
- }
+ AccountSyncReport report = mSyncReports.get(accountId);
+ if (report == null || report.unseenMessageCount == 0 || !report.notify) {
+ return;
}
- }
- if (!notify) {
- return;
+ unseenMessageCount = report.unseenMessageCount;
+ justFetchedCount = report.getJustFetchedMessageCount();
+ report.lastUnseenMessageCount = report.unseenMessageCount;
}
- showNewMessageNotification(this, accountId, vibrate, vibrateWhenSilent, ringtone,
- accountsWithNewMessages, numNewMessages, reportName);
- }
-
- /** Simply runs {@link #showNewMessageNotificationInternal} on a worker thread. */
- private static void showNewMessageNotification(final Context context,
- final long accountId, final boolean vibrate, final boolean vibrateWhenSilent,
- final Uri ringtone, final int accountsWithNewMessages, final int numNewMessages,
- final String reportName) {
- Utility.runAsync(new Runnable() {
- @Override
- public void run() {
- showNewMessageNotificationInternal(context, accountId, vibrate, vibrateWhenSilent,
- ringtone, accountsWithNewMessages, numNewMessages, reportName);
- }
- });
- }
-
- /**
- * Show (or update) a notification.
- *
- * TODO Implement new style notification. (show sender photo, sender name, subject, ...)
- */
- private static void showNewMessageNotificationInternal(Context context, long accountId,
- boolean vibrate, boolean vibrateWhenSilent, Uri ringtone, int accountsWithNewMessages,
- int numNewMessages, String reportName) {
- // set up to post a notification
- Intent intent;
- String reportString;
-
- if (accountsWithNewMessages == 1) {
- // Prepare a report for a single account
- // "12 unread (gmail)"
- reportString = context.getResources().getQuantityString(
- R.plurals.notification_new_one_account_fmt, numNewMessages,
- numNewMessages, reportName);
- intent = Welcome.createOpenAccountInboxIntent(context, accountId);
- } else {
- // Prepare a report for multiple accounts
- // "4 accounts"
- reportString = context.getResources().getQuantityString(
- R.plurals.notification_new_multi_account_fmt, accountsWithNewMessages,
- accountsWithNewMessages);
- intent = Welcome.createOpenCombinedInboxIntent(context);
- }
-
- // prepare appropriate pending intent, set up notification, and send
- PendingIntent pending =
- PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- Notification notification = new Notification(
- R.drawable.stat_notify_email_generic,
- context.getString(R.string.notification_new_title),
- System.currentTimeMillis());
- notification.setLatestEventInfo(context,
- context.getString(R.string.notification_new_title),
- reportString,
- pending);
-
- notification.sound = ringtone;
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- boolean nowSilent = audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
-
- // Use same code here as in Gmail and GTalk for vibration
- if (vibrate || (vibrateWhenSilent && nowSilent)) {
- notification.defaults |= Notification.DEFAULT_VIBRATE;
- }
-
- // This code is identical to that used by Gmail and GTalk for notifications
- notification.flags |= Notification.FLAG_SHOW_LIGHTS;
- notification.defaults |= Notification.DEFAULT_LIGHTS;
-
- NotificationManager notificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFICATION_ID_NEW_MESSAGES, notification);
+ NotificationController.getInstance(this).showNewMessageNotification(accountId,
+ unseenMessageCount, justFetchedCount);
}
/**
diff --git a/src/com/android/exchange/CalendarSyncEnabler.java b/src/com/android/exchange/CalendarSyncEnabler.java
index 4eaad8eec..5837bb2d2 100644
--- a/src/com/android/exchange/CalendarSyncEnabler.java
+++ b/src/com/android/exchange/CalendarSyncEnabler.java
@@ -17,8 +17,8 @@
package com.android.exchange;
import com.android.email.Email;
+import com.android.email.NotificationController;
import com.android.email.R;
-import com.android.email.service.MailService;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Calendar;
-import android.provider.ContactsContract;
import android.util.Log;
/**
@@ -106,7 +105,7 @@ public class CalendarSyncEnabler {
NotificationManager nm =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(MailService.NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED, n);
+ nm.notify(NotificationController.NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED, n);
}
/** @return {@link Intent} to launch the Calendar app. */
diff --git a/tests/src/com/android/email/provider/ProviderTests.java b/tests/src/com/android/email/provider/ProviderTests.java
index 4af4cea80..6ec8d9a38 100644
--- a/tests/src/com/android/email/provider/ProviderTests.java
+++ b/tests/src/com/android/email/provider/ProviderTests.java
@@ -2210,4 +2210,39 @@ public class ProviderTests extends ProviderTestCase2 {
assertEquals(0, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount);
assertEquals(0, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount);
}
+
+ private static Message createMessageWithTimestamp(Context c, Mailbox b, long timestamp) {
+ Message m = ProviderTestUtils.setupMessage("1", b.mAccountKey, b.mId, true, false, c, false,
+ false);
+ m.mTimeStamp = timestamp;
+ m.save(c);
+ return m;
+ }
+
+ public void testMessageGetLatestMessage() {
+ final Context c = mMockContext;
+
+ // Create 2 accounts with a inbox.
+ Account a1 = ProviderTestUtils.setupAccount("a1", true, c);
+ Account a2 = ProviderTestUtils.setupAccount("a2", true, c);
+
+ Mailbox b1 = ProviderTestUtils.setupMailbox("box1", a1.mId, true, c, Mailbox.TYPE_INBOX);
+ Mailbox b2 = ProviderTestUtils.setupMailbox("box3", a2.mId, true, c, Mailbox.TYPE_INBOX);
+
+ // Create some messages
+ Message m11 = createMessageWithTimestamp(c, b1, 33);
+ Message m12 = createMessageWithTimestamp(c, b1, 10);
+ Message m13 = createMessageWithTimestamp(c, b1, 1000);
+
+ Message m21 = createMessageWithTimestamp(c, b2, 99);
+ Message m22 = createMessageWithTimestamp(c, b2, 1);
+ Message m23 = createMessageWithTimestamp(c, b2, 2);
+
+ // Check!
+ assertEquals(m13.mId, Message.getLatestMessage(c, a1.mId).mId);
+ assertEquals(m21.mId, Message.getLatestMessage(c, a2.mId).mId);
+
+ // No such account
+ assertEquals(null, Message.getLatestMessage(c, 9999999L));
+ }
}
diff --git a/tests/src/com/android/exchange/CalendarSyncEnablerTest.java b/tests/src/com/android/exchange/CalendarSyncEnablerTest.java
index 16b9b28ab..b244c93bb 100644
--- a/tests/src/com/android/exchange/CalendarSyncEnablerTest.java
+++ b/tests/src/com/android/exchange/CalendarSyncEnablerTest.java
@@ -18,7 +18,7 @@ package com.android.exchange;
import com.android.email.AccountTestCase;
import com.android.email.Email;
-import com.android.email.service.MailService;
+import com.android.email.NotificationController;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -193,7 +193,9 @@ public class CalendarSyncEnablerTest extends AccountTestCase {
enabler.showNotification("a@b.com");
// Remove the notification. Comment it out when you want to know how it looks like.
+ // TODO If NotificationController supports this notification, we can just mock it out
+ // and remove this code.
((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE))
- .cancel(MailService.NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED);
+ .cancel(NotificationController.NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED);
}
}