Merge "Switch to tablet style notification"
This commit is contained in:
commit
4e400caab4
@ -163,6 +163,41 @@
|
||||
<item quantity="other">in <xliff:g id="number_accounts" example="10">%d</xliff:g> accounts</item>
|
||||
</plurals>
|
||||
|
||||
<!-- "new message" notification message title.
|
||||
"sender_name" will be replaced with the sender name of the latest email,
|
||||
and "num_more_mails" will be replaced with the number of other messages that are received
|
||||
together.
|
||||
[CHAR LIMIT=none] -->
|
||||
<plurals name="notification_sender_name_multi_messages">
|
||||
<item quantity="one"><xliff:g id="sender_name">%1$s</xliff:g>
|
||||
+ <xliff:g id="num_more_mails">%2$d</xliff:g> more</item>
|
||||
<item quantity="other"><xliff:g id="sender_name">%1$s</xliff:g>
|
||||
+ <xliff:g id="num_more_mails">%2$d</xliff:g> more</item>
|
||||
</plurals>
|
||||
|
||||
<!-- "new message" notification body for the number of new messages,
|
||||
used when only one account is set up.
|
||||
"num_new_message" will be replaced with the number of new messages. [CHAR LIMIT=none] -->
|
||||
<plurals name="notification_num_new_messages_single_account">
|
||||
<item quantity="one"><xliff:g id="num_new_message">%1$d</xliff:g></item> new
|
||||
<item quantity="other"><xliff:g id="num_new_message">%1$d</xliff:g></item> new
|
||||
</plurals>
|
||||
|
||||
<!-- "new message" notification body for the number of new messages,
|
||||
used when multiple accounts are set up.
|
||||
"num_new_message" will be replaced with the number of new messages, and "account_name"
|
||||
will be replaced with the receiving account name.
|
||||
"separator" should be between these two elements.
|
||||
[CHAR LIMIT=none] -->
|
||||
<plurals name="notification_num_new_messages_multi_account">
|
||||
<item quantity="one"><xliff:g id="num_new_message">%1$d</xliff:g
|
||||
> new<xliff:g id="separator"> </xliff:g>
|
||||
<xliff:g id="account_name">%2$s</xliff:g></item>
|
||||
<item quantity="other"><xliff:g id="num_new_message">%1$d</xliff:g
|
||||
> new<xliff:g id="separator"> </xliff:g>
|
||||
<xliff:g id="account_name">%2$s</xliff:g></item>
|
||||
</plurals>
|
||||
|
||||
<!-- The next set of strings are used server-side and must not be localized. -->
|
||||
<!-- Do Not Translate. This is the name of the "inbox" folder, on the server. -->
|
||||
<string name="mailbox_name_server_inbox" translatable="false">Inbox</string>
|
||||
|
227
src/com/android/email/NotificationController.java
Normal file
227
src/com/android/email/NotificationController.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!notify) {
|
||||
AccountSyncReport report = mSyncReports.get(accountId);
|
||||
if (report == null || report.unseenMessageCount == 0 || !report.notify) {
|
||||
return;
|
||||
}
|
||||
|
||||
showNewMessageNotification(this, accountId, vibrate, vibrateWhenSilent, ringtone,
|
||||
accountsWithNewMessages, numNewMessages, reportName);
|
||||
unseenMessageCount = report.unseenMessageCount;
|
||||
justFetchedCount = report.getJustFetchedMessageCount();
|
||||
report.lastUnseenMessageCount = report.unseenMessageCount;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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. */
|
||||
|
@ -2210,4 +2210,39 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user