EmailNotificationController.java revision 308ce9284793b597797994dfb1fb25155cbe0b20
1899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki/* 2899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Copyright (C) 2010 The Android Open Source Project 3899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * 4899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License"); 5899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * you may not use this file except in compliance with the License. 6899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * You may obtain a copy of the License at 7899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * 8899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * http://www.apache.org/licenses/LICENSE-2.0 9899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * 10899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Unless required by applicable law or agreed to in writing, software 11899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS, 12899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * See the License for the specific language governing permissions and 14899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * limitations under the License. 15899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 16899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 17899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukipackage com.android.email; 18899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 19899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport com.android.email.activity.ContactStatusLoader; 20899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport com.android.email.activity.Welcome; 21308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onukiimport com.android.email.activity.setup.AccountSecurity; 22d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blankimport com.android.email.activity.setup.AccountSettingsXL; 232193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.Address; 24a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent; 25a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Account; 26a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment; 27a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Message; 2831d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.utility.Utility; 29899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 30899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.app.Notification; 31899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.app.NotificationManager; 32899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.app.PendingIntent; 33899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.content.Context; 34d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blankimport android.content.Intent; 35899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.graphics.Bitmap; 3674e094834c6efd3260c717090a89232a892c411bMakoto Onukiimport android.graphics.BitmapFactory; 37899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.media.AudioManager; 38899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.net.Uri; 3974e094834c6efd3260c717090a89232a892c411bMakoto Onukiimport android.text.SpannableString; 40899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukiimport android.text.TextUtils; 4174e094834c6efd3260c717090a89232a892c411bMakoto Onukiimport android.text.style.TextAppearanceSpan; 42899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 43dd6e6b850462fd8617a73dd40b5e7d32f0aaf5edMakoto Onukiimport java.util.concurrent.atomic.AtomicInteger; 44dd6e6b850462fd8617a73dd40b5e7d32f0aaf5edMakoto Onuki 45899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki/** 46899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Class that manages notifications. 47899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 48899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onukipublic class NotificationController { 49308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private static final int NOTIFICATION_ID_SECURITY_NEEDED = 1; 50308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki 51308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki // ID reserved for CalendarSyncEnabler 52308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private static final int PLACEHOLDER_NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED = 2; 53308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private static final int NOTIFICATION_ID_ATTACHMENT_WARNING = 3; 54308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private static final int NOTIFICATION_ID_PASSWORD_EXPIRING = 4; 55308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private static final int NOTIFICATION_ID_PASSWORD_EXPIRED = 5; 56d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank 57d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank private static final int NOTIFICATION_ID_BASE_NEW_MESSAGES = 0x10000000; 58d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank private static final int NOTIFICATION_ID_BASE_LOGIN_WARNING = 0x20000000; 59899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 60899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki private static NotificationController sInstance; 61899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki private final Context mContext; 62899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki private final NotificationManager mNotificationManager; 63899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki private final AudioManager mAudioManager; 64c1c3b6f21ecddbb237195caedc36af442ebf00e8Andy Stadler private final Bitmap mGenericSenderIcon; 6574e094834c6efd3260c717090a89232a892c411bMakoto Onuki private final Clock mClock; 66899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 67899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki /** Constructor */ 6874e094834c6efd3260c717090a89232a892c411bMakoto Onuki /* package */ NotificationController(Context context, Clock clock) { 69899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki mContext = context.getApplicationContext(); 70899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki mNotificationManager = (NotificationManager) context.getSystemService( 71899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki Context.NOTIFICATION_SERVICE); 72899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 73c1c3b6f21ecddbb237195caedc36af442ebf00e8Andy Stadler mGenericSenderIcon = BitmapFactory.decodeResource(mContext.getResources(), 74c1c3b6f21ecddbb237195caedc36af442ebf00e8Andy Stadler R.drawable.ic_contact_picture); 7574e094834c6efd3260c717090a89232a892c411bMakoto Onuki mClock = clock; 76899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 77899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 78899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki /** Singleton access */ 79899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki public static synchronized NotificationController getInstance(Context context) { 80899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (sInstance == null) { 8174e094834c6efd3260c717090a89232a892c411bMakoto Onuki sInstance = new NotificationController(context, Clock.INSTANCE); 82899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 83899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return sInstance; 84899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 85899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 86899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki /** 871ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Generic notifier for any account. Uses notification rules from account. 88c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler * NOTE: Ticker is not shown in Holo XL notifications. 891ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * 901ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param account The account for which the notification is posted 911ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param ticker String for ticker 921ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param contentTitle String for notification content title 931ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param contentText String for notification content text 941ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param intent The intent to launch from the notification 951ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param notificationId The notification id 961ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 97308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private void showAccountNotification(Account account, String ticker, String contentTitle, 981ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String contentText, Intent intent, int notificationId) { 991ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1001ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Pending Intent 101c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler PendingIntent pending = null; 102c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler if (intent != null) { 103c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler pending = 104c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 105c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler } 1061ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1071ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Ringtone & Vibration 1081ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler String ringtoneString = account.getRingtone(); 1091ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Uri ringTone = (ringtoneString == null) ? null : Uri.parse(ringtoneString); 1101ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean vibrate = 0 != (account.mFlags & Account.FLAGS_VIBRATE_ALWAYS); 1111ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean vibrateWhenSilent = 0 != (account.mFlags & Account.FLAGS_VIBRATE_WHEN_SILENT); 1121ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1131ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Use the account's notification rules for sound & vibrate (but always notify) 1141ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler boolean nowSilent = 1151ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 1161ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1171ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler int defaults = Notification.DEFAULT_LIGHTS; 1181ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler if (vibrate || (vibrateWhenSilent && nowSilent)) { 1191ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler defaults |= Notification.DEFAULT_VIBRATE; 1201ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 1211ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1221ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler // Notification 1231ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Notification.Builder nb = new Notification.Builder(mContext); 1241ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setSmallIcon(R.drawable.stat_notify_email_generic); 1251ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setTicker(ticker); 1261ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setContentTitle(contentTitle); 1271ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setContentText(contentText); 1281ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setContentIntent(pending); 1291ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setSound(ringTone); 1301ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler nb.setDefaults(defaults); 1311ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler Notification notification = nb.getNotification(); 1321ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1331ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler mNotificationManager.notify(notificationId, notification); 1341ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 1351ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1361ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 1371ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * Generic notification canceler. 1381ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler * @param notificationId The notification id 1391ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler */ 140308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki private void cancelNotification(int notificationId) { 1411ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler mNotificationManager.cancel(notificationId); 1421ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler } 1431ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler 1441ca111c19c83d54ad23bd8615d9c648e09ec3366Andy Stadler /** 145899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * @return the "new message" notification ID for an account. It just assumes 146899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * accountID won't be too huge. Any other smarter/cleaner way? 147899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 148899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki private int getNewMessageNotificationId(long accountId) { 149d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank return (int) (NOTIFICATION_ID_BASE_NEW_MESSAGES + accountId); 150899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 151899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 152899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki /** 153899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Dismiss new message notification 154899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * 155899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * @param accountId ID of the target account, or -1 for all accounts. 156899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 157899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki public void cancelNewMessageNotification(long accountId) { 158899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (accountId == -1) { 159899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki new Utility.ForEachAccount(mContext) { 160899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki @Override 161899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki protected void performAction(long accountId) { 162899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki cancelNewMessageNotification(accountId); 163899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 164899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki }.execute(); 165899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } else { 166899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki mNotificationManager.cancel(getNewMessageNotificationId(accountId)); 167899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 168899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 169899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 170899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki /** 171899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Show (or update) the "new message" notification. 172899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 173899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki public void showNewMessageNotification(final long accountId, final int unseenMessageCount, 174899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final int justFetchedCount) { 175899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki Utility.runAsync(new Runnable() { 176899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki @Override 177899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki public void run() { 17874e094834c6efd3260c717090a89232a892c411bMakoto Onuki Notification n = createNewMessageNotification(accountId, unseenMessageCount); 179899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (n == null) { 180899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return; 181899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 182899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki mNotificationManager.notify(getNewMessageNotificationId(accountId), n); 183899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 184899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki }); 185899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 186899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 187899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki /** 188899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * @return The sender's photo, if available, or null. 189899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * 190899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Don't call it on the UI thread. 191899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 192899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki private Bitmap getSenderPhoto(Message message) { 193899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki Address sender = Address.unpackFirst(message.mFrom); 194899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (sender == null) { 195899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return null; 196899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 197899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki String email = sender.getAddress(); 198899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (TextUtils.isEmpty(email)) { 199899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return null; 200899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 201899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return ContactStatusLoader.load(mContext, email).mPhoto; 202899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 203899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 204dd6e6b850462fd8617a73dd40b5e7d32f0aaf5edMakoto Onuki private static final AtomicInteger sSequenceNumber = new AtomicInteger(); 205dd6e6b850462fd8617a73dd40b5e7d32f0aaf5edMakoto Onuki 206dd6e6b850462fd8617a73dd40b5e7d32f0aaf5edMakoto Onuki /** 207899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Create a notification 208899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * 209899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki * Don't call it on the UI thread. 210899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki */ 21174e094834c6efd3260c717090a89232a892c411bMakoto Onuki /* package */ Notification createNewMessageNotification(long accountId, 21274e094834c6efd3260c717090a89232a892c411bMakoto Onuki int unseenMessageCount) { 213899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final Account account = Account.restoreAccountWithId(mContext, accountId); 214899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (account == null) { 215899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return null; 216899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 217899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki // Get the latest message 2186f3d167cfa948d20153c2172c34cbede5ec83a1dMakoto Onuki final Message message = Message.getLatestIncomingMessage(mContext, accountId); 219899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (message == null) { 220899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return null; // no message found??? 221899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 222899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 22343c455eb266125a8e09a98e38d7639a865f52d42Makoto Onuki String senderName = Address.toFriendly(Address.unpack(message.mFrom)); 22443c455eb266125a8e09a98e38d7639a865f52d42Makoto Onuki if (senderName == null) { 22543c455eb266125a8e09a98e38d7639a865f52d42Makoto Onuki senderName = ""; // Happens when a message has no from. 22643c455eb266125a8e09a98e38d7639a865f52d42Makoto Onuki } 227899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final String subject = message.mSubject; 228899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final Bitmap senderPhoto = getSenderPhoto(message); 229899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 230899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki // Intent to open inbox 231899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, 232308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki Welcome.createOpenAccountInboxIntent(mContext, accountId), 233dd6e6b850462fd8617a73dd40b5e7d32f0aaf5edMakoto Onuki 0); 234899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 2352e578032d09b376a48146932146f88394f731077Joe Onorato Notification.Builder builder = new Notification.Builder(mContext) 2362e578032d09b376a48146932146f88394f731077Joe Onorato .setSmallIcon(R.drawable.stat_notify_email_generic) 23774e094834c6efd3260c717090a89232a892c411bMakoto Onuki .setWhen(mClock.getTime()) 238c1c3b6f21ecddbb237195caedc36af442ebf00e8Andy Stadler .setLargeIcon(senderPhoto != null ? senderPhoto : mGenericSenderIcon) 23974e094834c6efd3260c717090a89232a892c411bMakoto Onuki .setContentTitle(getNotificationTitle(senderName, account.mDisplayName)) 24074e094834c6efd3260c717090a89232a892c411bMakoto Onuki .setContentText(subject) 2418d3ef1d01e1d3ae548366fe7961af23ed66f1da5Makoto Onuki .setContentIntent(contentIntent); 24274e094834c6efd3260c717090a89232a892c411bMakoto Onuki if (unseenMessageCount > 1) { 24374e094834c6efd3260c717090a89232a892c411bMakoto Onuki builder.setNumber(unseenMessageCount); 24474e094834c6efd3260c717090a89232a892c411bMakoto Onuki } 245899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 2462e578032d09b376a48146932146f88394f731077Joe Onorato Notification notification = builder.getNotification(); 247899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 248899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki setupNotificationSoundAndVibrationFromAccount(notification, account); 249899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki return notification; 250899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 251899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 25274e094834c6efd3260c717090a89232a892c411bMakoto Onuki /** 25374e094834c6efd3260c717090a89232a892c411bMakoto Onuki * Creates the notification title. 25474e094834c6efd3260c717090a89232a892c411bMakoto Onuki * 25574e094834c6efd3260c717090a89232a892c411bMakoto Onuki * If only 1 account, just show the sender name. 25674e094834c6efd3260c717090a89232a892c411bMakoto Onuki * If 2+ accounts, make it "SENDER_NAME to RECEIVER_NAME", and gray out the "to RECEIVER_NAME" 25774e094834c6efd3260c717090a89232a892c411bMakoto Onuki * part. 25874e094834c6efd3260c717090a89232a892c411bMakoto Onuki */ 25974e094834c6efd3260c717090a89232a892c411bMakoto Onuki /* package */ SpannableString getNotificationTitle(String sender, String receiverDisplayName) { 26074e094834c6efd3260c717090a89232a892c411bMakoto Onuki final int numAccounts = EmailContent.count(mContext, Account.CONTENT_URI); 26174e094834c6efd3260c717090a89232a892c411bMakoto Onuki if (numAccounts == 1) { 26274e094834c6efd3260c717090a89232a892c411bMakoto Onuki return new SpannableString(sender); 26374e094834c6efd3260c717090a89232a892c411bMakoto Onuki } else { 26474e094834c6efd3260c717090a89232a892c411bMakoto Onuki // "to [account name]" 26574e094834c6efd3260c717090a89232a892c411bMakoto Onuki String toAcccount = mContext.getResources().getString(R.string.notification_to_account, 26674e094834c6efd3260c717090a89232a892c411bMakoto Onuki receiverDisplayName); 26774e094834c6efd3260c717090a89232a892c411bMakoto Onuki // "[Sender] to [account name]" 26874e094834c6efd3260c717090a89232a892c411bMakoto Onuki SpannableString senderToAccount = new SpannableString(sender + " " + toAcccount); 26974e094834c6efd3260c717090a89232a892c411bMakoto Onuki 27074e094834c6efd3260c717090a89232a892c411bMakoto Onuki // "[Sender] to [account name]" 27174e094834c6efd3260c717090a89232a892c411bMakoto Onuki // ^^^^^^^^^^^^^^^^^ <- Make this part gray 27274e094834c6efd3260c717090a89232a892c411bMakoto Onuki TextAppearanceSpan secondarySpan = new TextAppearanceSpan( 27374e094834c6efd3260c717090a89232a892c411bMakoto Onuki mContext, R.style.notification_secondary_text); 27474e094834c6efd3260c717090a89232a892c411bMakoto Onuki senderToAccount.setSpan(secondarySpan, sender.length() + 1, senderToAccount.length(), 27574e094834c6efd3260c717090a89232a892c411bMakoto Onuki 0); 27674e094834c6efd3260c717090a89232a892c411bMakoto Onuki return senderToAccount; 27774e094834c6efd3260c717090a89232a892c411bMakoto Onuki } 27874e094834c6efd3260c717090a89232a892c411bMakoto Onuki } 27974e094834c6efd3260c717090a89232a892c411bMakoto Onuki 28074e094834c6efd3260c717090a89232a892c411bMakoto Onuki // Overridden for testing (AudioManager can't be mocked out.) 28174e094834c6efd3260c717090a89232a892c411bMakoto Onuki /* package */ int getRingerMode() { 28274e094834c6efd3260c717090a89232a892c411bMakoto Onuki return mAudioManager.getRingerMode(); 28374e094834c6efd3260c717090a89232a892c411bMakoto Onuki } 28474e094834c6efd3260c717090a89232a892c411bMakoto Onuki 28574e094834c6efd3260c717090a89232a892c411bMakoto Onuki /* package */ boolean isRingerModeSilent() { 28674e094834c6efd3260c717090a89232a892c411bMakoto Onuki return getRingerMode() != AudioManager.RINGER_MODE_NORMAL; 287899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 288899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 28974e094834c6efd3260c717090a89232a892c411bMakoto Onuki /* package */ void setupNotificationSoundAndVibrationFromAccount(Notification notification, 290899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki Account account) { 291899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final int flags = account.mFlags; 292899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final String ringtoneUri = account.mRingtoneUri; 293899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final boolean vibrate = (flags & Account.FLAGS_VIBRATE_ALWAYS) != 0; 294899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki final boolean vibrateWhenSilent = (flags & Account.FLAGS_VIBRATE_WHEN_SILENT) != 0; 295899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 296899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki notification.sound = (ringtoneUri == null) ? null : Uri.parse(ringtoneUri); 297899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 298899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki if (vibrate || (vibrateWhenSilent && isRingerModeSilent())) { 299899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki notification.defaults |= Notification.DEFAULT_VIBRATE; 300899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 301899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki 302899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki // This code is identical to that used by Gmail and GTalk for notifications 303899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki notification.flags |= Notification.FLAG_SHOW_LIGHTS; 304899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki notification.defaults |= Notification.DEFAULT_LIGHTS; 305899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki } 306d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank 307d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank /** 308d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank * Alert the user that an attachment couldn't be forwarded. This is a very unusual case, and 309d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank * perhaps we shouldn't even send a notification. For now, it's helpful for debugging. 310c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler * NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS) 311d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank */ 312c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler public void showDownloadForwardFailedNotification(Attachment attachment) { 313c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler final Account account = Account.restoreAccountWithId(mContext, attachment.mAccountKey); 314c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler if (account == null) return; 315308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki showAccountNotification(account, 316d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank mContext.getString(R.string.forward_download_failed_ticker), 317c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler mContext.getString(R.string.forward_download_failed_title), 318c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler attachment.mFileName, 319c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler null, 320c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler NOTIFICATION_ID_ATTACHMENT_WARNING); 321d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank } 322d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank 323d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank /** 324d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank * Alert the user that login failed for the specified account 325d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank */ 326d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank private int getLoginFailedNotificationId(long accountId) { 327d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank return NOTIFICATION_ID_BASE_LOGIN_WARNING + (int)accountId; 328d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank } 329d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank 330c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler /** 331c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler * Alert the user that login failed on a particular account. 332c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler * NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS) 333c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler */ 334d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank public void showLoginFailedNotification(long accountId) { 335d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank final Account account = Account.restoreAccountWithId(mContext, accountId); 336d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank if (account == null) return; 337308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki showAccountNotification(account, 338d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank mContext.getString(R.string.login_failed_ticker, account.mDisplayName), 339c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler mContext.getString(R.string.login_failed_title), 340c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler account.getDisplayName(), 341f4894131427ec7562fcb05388b9f3aa094e388bcAndy Stadler AccountSettingsXL.createAccountSettingsIntent(mContext, accountId, 342f4894131427ec7562fcb05388b9f3aa094e388bcAndy Stadler account.mDisplayName), 343c6d344ad2aab6bc46a87033af53d5a19a080e5f4Andy Stadler getLoginFailedNotificationId(accountId)); 344d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank } 345d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank 346d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank public void cancelLoginFailedNotification(long accountId) { 347d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank mNotificationManager.cancel(getLoginFailedNotificationId(accountId)); 348d3e4f3ca7e43fb7ebaa140f93a44a1fb96a0577eMarc Blank } 349308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki 350308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki /** 351308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Show "password expiring" notification. 352308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * 353308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Note all accounts share the same notification ID. 354308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki */ 355308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki public void showPasswordExpiringNotification(long accountId) { 356308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki Account account = Account.restoreAccountWithId(mContext, accountId); 357308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki if (account == null) return; 358308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki Intent intent = AccountSecurity.actionDevicePasswordExpirationIntent(mContext, 359308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki accountId, false); 360308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String ticker = mContext.getString( 361308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki R.string.password_expire_warning_ticker_fmt, account.getDisplayName()); 362308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String contentTitle = mContext.getString( 363308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki R.string.password_expire_warning_content_title); 364308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String contentText = account.getDisplayName(); 365308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki showAccountNotification(account, ticker, contentTitle, contentText, intent, 366308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki NOTIFICATION_ID_PASSWORD_EXPIRING); 367308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki } 368308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki 369308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki /** 370308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Show "password expired" notification. 371308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * 372308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Note all accounts share the same notification ID. 373308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki */ 374308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki public void showPasswordExpiredNotification(long accountId) { 375308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki Account account = Account.restoreAccountWithId(mContext, accountId); 376308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki if (account == null) return; 377308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki Intent intent = AccountSecurity.actionDevicePasswordExpirationIntent(mContext, 378308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki accountId, true); 379308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String ticker = mContext.getString(R.string.password_expired_ticker); 380308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String contentTitle = mContext.getString(R.string.password_expired_content_title); 381308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String contentText = account.getDisplayName(); 382308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki showAccountNotification(account, ticker, contentTitle, 383308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki contentText, intent, NOTIFICATION_ID_PASSWORD_EXPIRED); 384308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki } 385308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki 386308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki /** 387308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Cancel both "password expired/expiring" notifications. 388308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki */ 389308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki public void cancelPasswordExpirationNotifications() { 390308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki cancelNotification(NOTIFICATION_ID_PASSWORD_EXPIRING); 391308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki cancelNotification(NOTIFICATION_ID_PASSWORD_EXPIRED); 392308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki } 393308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki 394308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki /** 395308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Show "security needed" notification. 396308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki */ 397308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki public void showSecurityNeededNotification(Account account) { 398308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String tickerText = mContext.getString(R.string.security_notification_ticker_fmt, 399308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki account.getDisplayName()); 400308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String contentTitle = mContext.getString(R.string.security_notification_content_title); 401308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki String contentText = account.getDisplayName(); 402308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki Intent intent = AccountSecurity.actionUpdateSecurityIntent(mContext, account.mId, true); 403308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki showAccountNotification( 404308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki account, tickerText, contentTitle, contentText, intent, 405308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki NOTIFICATION_ID_SECURITY_NEEDED); 406308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki } 407308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki 408308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki /** 409308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki * Cancel "security needed" notification. 410308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki */ 411308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki public void cancelSecurityNeededNotification() { 412308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki cancelNotification(NOTIFICATION_ID_SECURITY_NEEDED); 413308ce9284793b597797994dfb1fb25155cbe0b20Makoto Onuki } 414899c5b866192a4c4a12413446d10e5d98dbf94faMakoto Onuki} 415