NotificationUtils.java revision 439aa5d7d9226de4682c3697c68c0703b8847518
1d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy/* 2d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Copyright (C) 2013 The Android Open Source Project 3d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * 4d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Licensed under the Apache License, Version 2.0 (the "License"); 5d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * you may not use this file except in compliance with the License. 6d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * You may obtain a copy of the License at 7d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * 8d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * http://www.apache.org/licenses/LICENSE-2.0 9d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * 10d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Unless required by applicable law or agreed to in writing, software 11d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * distributed under the License is distributed on an "AS IS" BASIS, 12d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * See the License for the specific language governing permissions and 14d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * limitations under the License. 15d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 16d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedypackage com.android.mail.utils; 17d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 18d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.app.Notification; 19d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.app.NotificationManager; 20d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.app.PendingIntent; 21d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.content.ContentResolver; 22d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.content.ContentUris; 23d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.content.ContentValues; 24d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.content.Context; 25d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.content.Intent; 26d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.content.res.Resources; 27d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.database.Cursor; 28d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.graphics.Bitmap; 29d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.graphics.BitmapFactory; 30d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.net.Uri; 31d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.provider.ContactsContract; 32d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.provider.ContactsContract.CommonDataKinds.Email; 33d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.support.v4.app.NotificationCompat; 34439aa5d7d9226de4682c3697c68c0703b8847518Alan Lauimport android.support.v4.app.NotificationManagerCompat; 35f58109699c207a2eaeca38d557d9b9a7e7664decAndrew Sappersteinimport android.support.v4.text.BidiFormatter; 368f10ced86b3c8e7229900d6089f56bf12b026116Andy Huangimport android.support.v4.util.ArrayMap; 37d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.text.SpannableString; 38d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.text.SpannableStringBuilder; 39d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.text.TextUtils; 40d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.text.style.CharacterStyle; 41d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.text.style.TextAppearanceSpan; 42d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport android.util.Pair; 4361bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedyimport android.util.SparseArray; 44d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 45821e578a71c7015646522e729600618f0ec16fc0Tony Mantlerimport com.android.emailcommon.mail.Address; 46d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.EmailAddress; 47d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.MailIntentService; 48d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.R; 494fe0af81874976a1995191321e35c844b2229811Andy Huangimport com.android.mail.analytics.Analytics; 5089bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sappersteinimport com.android.mail.browse.ConversationItemView; 51d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.browse.MessageCursor; 52d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.browse.SendersView; 53fe23512c2be21d7c87677f5263078021002d89cbScott Kennedyimport com.android.mail.photomanager.LetterTileProvider; 54d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.preferences.AccountPreferences; 55d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.preferences.FolderPreferences; 56d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.preferences.MailPrefs; 57d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.providers.Account; 58d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.providers.Conversation; 59d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.providers.Folder; 60d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.providers.Message; 61d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.providers.UIProvider; 62c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedyimport com.android.mail.ui.ImageCanvas.Dimensions; 63d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport com.android.mail.utils.NotificationActionUtils.NotificationAction; 641bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HTML; 651bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HTML4; 661bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HtmlDocument; 671bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HtmlTree; 68ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sappersteinimport com.google.common.base.Objects; 697f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedyimport com.google.common.collect.ImmutableList; 7009400efa442422299acf21abe20e3470f9d965abScott Kennedyimport com.google.common.collect.Lists; 7109400efa442422299acf21abe20e3470f9d965abScott Kennedyimport com.google.common.collect.Sets; 72644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lauimport com.google.common.io.Closeables; 73d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 74644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lauimport java.io.InputStream; 75644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lauimport java.lang.ref.WeakReference; 76d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport java.util.ArrayList; 77d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport java.util.Arrays; 78d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport java.util.Collection; 7915490239c82cdb4765e87e926c8c1945adedd1edAlan Lauimport java.util.HashMap; 8015490239c82cdb4765e87e926c8c1945adedd1edAlan Lauimport java.util.HashSet; 81d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport java.util.List; 8215490239c82cdb4765e87e926c8c1945adedd1edAlan Lauimport java.util.Map; 83d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport java.util.Set; 84d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedyimport java.util.concurrent.ConcurrentHashMap; 85d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 86d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedypublic class NotificationUtils { 87c7d754703a0b5c2ca4b4dc9675d733881f85b746Scott Kennedy public static final String LOG_TAG = "NotifUtils"; 88d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 89d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** Contains a list of <(account, label), unread conversations> */ 90d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static NotificationMap sActiveNotificationMap = null; 91d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 9261bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy private static final SparseArray<Bitmap> sNotificationIcons = new SparseArray<Bitmap>(); 93644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau private static WeakReference<Bitmap> sDefaultWearableBg = new WeakReference<Bitmap>(null); 94d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 95d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static TextAppearanceSpan sNotificationUnreadStyleSpan; 96d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static CharacterStyle sNotificationReadStyleSpan; 97d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 98d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** A factory that produces a plain text converter that removes elided text. */ 99d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final HtmlTree.PlainTextConverterFactory MESSAGE_CONVERTER_FACTORY = 100d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new HtmlTree.PlainTextConverterFactory() { 101d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy @Override 102d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public HtmlTree.PlainTextConverter createInstance() { 103d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return new MailMessagePlainTextConverter(); 104d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 105d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy }; 106d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 107963c7e4f178037acebddfd13e16fc627ef368c82Andrew Sapperstein private static BidiFormatter sBidiFormatter = BidiFormatter.getInstance(); 108f58109699c207a2eaeca38d557d9b9a7e7664decAndrew Sapperstein 10915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau private static Map<NotificationKey, Set<Integer>> sChildNotificationsMap = 11015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau new HashMap<NotificationKey, Set<Integer>>(); 11115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 112d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 113d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Clears all notifications in response to the user tapping "Clear" in the status bar. 114d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 115d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static void clearAllNotfications(Context context) { 1162f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.v(LOG_TAG, "Clearing all notifications."); 117d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 118d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.clear(); 119d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.saveNotificationMap(context); 120d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 121d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 122d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 123d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Returns the notification map, creating it if necessary. 124d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 125d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static synchronized NotificationMap getNotificationMap(Context context) { 126d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (sActiveNotificationMap == null) { 127d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sActiveNotificationMap = new NotificationMap(); 128d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 129d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // populate the map from the cached data 130d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sActiveNotificationMap.loadNotificationMap(context); 131d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 132d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return sActiveNotificationMap; 133d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 134d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 135d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 136d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Class representing the existing notifications, and the number of unread and 137d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * unseen conversations that triggered each. 138d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1392354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee private static final class NotificationMap { 140d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 141d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final String NOTIFICATION_PART_SEPARATOR = " "; 142d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final int NUM_NOTIFICATION_PARTS= 4; 1432354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee private final ConcurrentHashMap<NotificationKey, Pair<Integer, Integer>> mMap = 1442354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee new ConcurrentHashMap<NotificationKey, Pair<Integer, Integer>>(); 145d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 146d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1472354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Returns the number of key values pairs in the inner map. 1482354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee */ 1492354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee public int size() { 1502354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee return mMap.size(); 1512354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee } 1522354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee 1532354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee /** 1542354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Returns a set of key values. 1552354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee */ 1562354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee public Set<NotificationKey> keySet() { 1572354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee return mMap.keySet(); 1582354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee } 1592354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee 1602354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee /** 1612354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Remove the key from the inner map and return its value. 1622354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * 1632354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * @param key The key {@link NotificationKey} to be removed. 1642354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * @return The value associated with this key. 1652354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee */ 1662354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee public Pair<Integer, Integer> remove(NotificationKey key) { 1672354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee return mMap.remove(key); 1682354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee } 1692354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee 1702354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee /** 1712354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Clear all key-value pairs in the map. 1722354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee */ 1732354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee public void clear() { 1742354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee mMap.clear(); 1752354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee } 1762354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee 1772354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee /** 1782354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Discover if a key-value pair with this key exists. 1792354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * 1802354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * @param key The key {@link NotificationKey} to be checked. 1812354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * @return If a key-value pair with this key exists in the map. 1822354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee */ 1832354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee public boolean containsKey(NotificationKey key) { 1842354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee return mMap.containsKey(key); 1852354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee } 1862354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee 1872354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee /** 1882354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Returns the unread count for the given NotificationKey. 189d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 190d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public Integer getUnread(NotificationKey key) { 1912354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee final Pair<Integer, Integer> value = mMap.get(key); 192d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return value != null ? value.first : null; 193d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 194d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 195d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1962354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee * Returns the unread unseen count for the given NotificationKey. 197d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 198d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public Integer getUnseen(NotificationKey key) { 1992354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee final Pair<Integer, Integer> value = mMap.get(key); 200d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return value != null ? value.second : null; 201d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 202d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 203d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 204d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Store the unread and unseen value for the given NotificationKey 205d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 206d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public void put(NotificationKey key, int unread, int unseen) { 207d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Pair<Integer, Integer> value = 208d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new Pair<Integer, Integer>(Integer.valueOf(unread), Integer.valueOf(unseen)); 2092354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee mMap.put(key, value); 210d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 211d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 212d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 213d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Populates the notification map with previously cached data. 214d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 215d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public synchronized void loadNotificationMap(final Context context) { 216d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final MailPrefs mailPrefs = MailPrefs.get(context); 217d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Set<String> notificationSet = mailPrefs.getActiveNotificationSet(); 218d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (notificationSet != null) { 219d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (String notificationEntry : notificationSet) { 220d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Get the parts of the string that make the notification entry 221d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String[] notificationParts = 222d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy TextUtils.split(notificationEntry, NOTIFICATION_PART_SEPARATOR); 223d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (notificationParts.length == NUM_NOTIFICATION_PARTS) { 224d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Uri accountUri = Uri.parse(notificationParts[0]); 225d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Cursor accountCursor = context.getContentResolver().query( 226d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy accountUri, UIProvider.ACCOUNTS_PROJECTION, null, null, null); 227d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Account account; 228d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy try { 229d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (accountCursor.moveToFirst()) { 230d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy account = new Account(accountCursor); 231d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 232d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy continue; 233d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 234d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } finally { 235d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy accountCursor.close(); 236d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 237d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 238d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Uri folderUri = Uri.parse(notificationParts[1]); 239d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Cursor folderCursor = context.getContentResolver().query( 240d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy folderUri, UIProvider.FOLDERS_PROJECTION, null, null, null); 241d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Folder folder; 242d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy try { 243d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (folderCursor.moveToFirst()) { 244d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy folder = new Folder(folderCursor); 245d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 246d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy continue; 247d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 248d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } finally { 249d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy folderCursor.close(); 250d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 251d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 252d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationKey key = new NotificationKey(account, folder); 253d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Integer unreadValue = Integer.valueOf(notificationParts[2]); 254d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Integer unseenValue = Integer.valueOf(notificationParts[3]); 2552354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee put(key, unreadValue, unseenValue); 256d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 257d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 258d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 259d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 260d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 261d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 262d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Cache the notification map. 263d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 264d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public synchronized void saveNotificationMap(Context context) { 265d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Set<String> notificationSet = Sets.newHashSet(); 2662354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee final Set<NotificationKey> keys = mMap.keySet(); 267d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey key : keys) { 2682354fd98dec04736bd35c1ec8c130cb1aa18ff9cAnthony Lee final Pair<Integer, Integer> value = mMap.get(key); 269d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Integer unreadCount = value.first; 270d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Integer unseenCount = value.second; 271ff8553f20964f4c31b0c503a9e1daff6ae08a9c7Scott Kennedy if (unreadCount != null && unseenCount != null) { 272d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String[] partValues = new String[] { 273259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy key.account.uri.toString(), key.folder.folderUri.fullUri.toString(), 274d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy unreadCount.toString(), unseenCount.toString()}; 275d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationSet.add(TextUtils.join(NOTIFICATION_PART_SEPARATOR, partValues)); 276d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 277d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 278d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final MailPrefs mailPrefs = MailPrefs.get(context); 279d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy mailPrefs.cacheActiveNotificationSet(notificationSet); 280d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 281d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 282d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 283d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 284d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @return the title of this notification with each account and the number of unread and unseen 285d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * conversations for it. Also remove any account in the map that has 0 unread. 286d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 287d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static String createNotificationString(NotificationMap notifications) { 288d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy StringBuilder result = new StringBuilder(); 289d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy int i = 0; 290d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Set<NotificationKey> keysToRemove = Sets.newHashSet(); 291d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey key : notifications.keySet()) { 292d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Integer unread = notifications.getUnread(key); 293d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Integer unseen = notifications.getUnseen(key); 294d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unread == null || unread.intValue() == 0) { 295d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy keysToRemove.add(key); 296d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 297d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (i > 0) result.append(", "); 298d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy result.append(key.toString() + " (" + unread + ", " + unseen + ")"); 299d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy i++; 300d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 301d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 302d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 303d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey key : keysToRemove) { 304d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notifications.remove(key); 305d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 306d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 307d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return result.toString(); 308d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 309d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 310d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 311d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Get all notifications for all accounts and cancel them. 312d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy **/ 313d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static void cancelAllNotifications(Context context) { 3142f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "cancelAllNotifications - cancelling all"); 31515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationManagerCompat nm = NotificationManagerCompat.from(context); 316d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy nm.cancelAll(); 317d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy clearAllNotfications(context); 318d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 319d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 320d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 321d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Get all notifications for all accounts, cancel them, and repost. 322d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * This happens when locale changes. 323d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy **/ 324963c7e4f178037acebddfd13e16fc627ef368c82Andrew Sapperstein public static void cancelAndResendNotificationsOnLocaleChange(Context context) { 325963c7e4f178037acebddfd13e16fc627ef368c82Andrew Sapperstein LogUtils.d(LOG_TAG, "cancelAndResendNotificationsOnLocaleChange"); 326963c7e4f178037acebddfd13e16fc627ef368c82Andrew Sapperstein sBidiFormatter = BidiFormatter.getInstance(); 327ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein resendNotifications(context, true, null, null); 328d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 329d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 330d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 331d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Get all notifications for all accounts, optionally cancel them, and repost. 332ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * This happens when locale changes. If you only want to resend messages from one 333ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * account-folder pair, pass in the account and folder that should be resent. 334ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * All other account-folder pairs will not have their notifications resent. 335ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * All notifications will be resent if account or folder is null. 336ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * 337ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * @param context Current context. 338ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * @param cancelExisting True, if all notifications should be canceled before resending. 339ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * False, otherwise. 340ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * @param accountUri The {@link Uri} of the {@link Account} of the notification 341ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * upon which an action occurred. 342ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * @param folderUri The {@link Uri} of the {@link Folder} of the notification 343ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein * upon which an action occurred. 344ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein */ 345ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein public static void resendNotifications(Context context, final boolean cancelExisting, 346259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy final Uri accountUri, final FolderUri folderUri) { 3472f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "resendNotifications "); 3482638b48e063e469d48daf6a7c0d3a9b2d56d7beaScott Kennedy 349d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (cancelExisting) { 3502f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "resendNotifications - cancelling all"); 35115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationManagerCompat nm = NotificationManagerCompat.from(context); 352d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy nm.cancelAll(); 353d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 354d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Re-validate the notifications. 355d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 356d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Set<NotificationKey> keys = notificationMap.keySet(); 357d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey notification : keys) { 358d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Folder folder = notification.folder; 359b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final int notificationId = 360b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler getNotificationId(notification.account.getAccountManagerAccount(), folder); 361ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein 362ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein // Only resend notifications if the notifications are from the same folder 363ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein // and same account as the undo notification that was previously displayed. 36426b29ef558cce109c51584081c52781760a02dd9Scott Kennedy if (accountUri != null && !Objects.equal(accountUri, notification.account.uri) && 365259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy folderUri != null && !Objects.equal(folderUri, folder.folderUri)) { 3662f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "resendNotifications - not resending %s / %s" 3672638b48e063e469d48daf6a7c0d3a9b2d56d7beaScott Kennedy + " because it doesn't match %s / %s", 368259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy notification.account.uri, folder.folderUri, accountUri, folderUri); 369ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein continue; 370ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein } 371d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 3722f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "resendNotifications - resending %s / %s", 373259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy notification.account.uri, folder.folderUri); 3742638b48e063e469d48daf6a7c0d3a9b2d56d7beaScott Kennedy 375d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationAction undoableAction = 376d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationActionUtils.sUndoNotifications.get(notificationId); 377d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (undoableAction == null) { 378ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein validateNotifications(context, folder, notification.account, true, 379ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein false, notification); 380d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 381d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Create an undo notification 382d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationActionUtils.createUndoNotification(context, undoableAction); 383d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 384d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 385d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 386d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 387d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 388d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Validate the notifications for the specified account. 389d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 390d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static void validateAccountNotifications(Context context, String account) { 3912f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "validateAccountNotifications - %s", account); 3924162f83633a001cf03ff7bbb5e1abae92392049bScott Kennedy 393d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy List<NotificationKey> notificationsToCancel = Lists.newArrayList(); 394d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Iterate through the notification map to see if there are any entries that correspond to 395d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // labels that are not in the sync set. 396d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 397d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Set<NotificationKey> keys = notificationMap.keySet(); 398d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final AccountPreferences accountPreferences = new AccountPreferences(context, account); 399d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean enabled = accountPreferences.areNotificationsEnabled(); 400d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!enabled) { 401d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Cancel all notifications for this account 402d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey notification : keys) { 403b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler if (notification.account.getAccountManagerAccount().name.equals(account)) { 404d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationsToCancel.add(notification); 405d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 406d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 407d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 408d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Iterate through the notification map to see if there are any entries that 409d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // correspond to labels that are not in the notification set. 410d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey notification : keys) { 411b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler if (notification.account.getAccountManagerAccount().name.equals(account)) { 412d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If notification is not enabled for this label, remember this NotificationKey 413d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // to later cancel the notification, and remove the entry from the map 414d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Folder folder = notification.folder; 415259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy final boolean isInbox = folder.folderUri.equals( 416259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy notification.account.settings.defaultInbox); 417d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final FolderPreferences folderPreferences = new FolderPreferences( 418afb10d038c70c0a8646f0315836c49c704765ff9Tony Mantler context, notification.account.getEmailAddress(), folder, isInbox); 419d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 420d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!folderPreferences.areNotificationsEnabled()) { 421d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationsToCancel.add(notification); 422d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 423d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 424d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 425d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 426d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 427d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Cancel & remove the invalid notifications. 428d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (notificationsToCancel.size() > 0) { 429d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationManager nm = (NotificationManager) context.getSystemService( 430d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Context.NOTIFICATION_SERVICE); 431d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (NotificationKey notification : notificationsToCancel) { 432d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Folder folder = notification.folder; 433b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final int notificationId = 434b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler getNotificationId(notification.account.getAccountManagerAccount(), folder); 4352f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "validateAccountNotifications - cancelling %s / %s", 43626a20758413f0f8629ea71a0943304e3ae740165Tony Mantler notification.account.getEmailAddress(), folder.persistentId); 437d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy nm.cancel(notificationId); 438d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.remove(notification); 439d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationActionUtils.sUndoNotifications.remove(notificationId); 440d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationActionUtils.sNotificationTimestamps.delete(notificationId); 441d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 442d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.saveNotificationMap(context); 443d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 444d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 445d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 446d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 447d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Display only one notification. 448d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 449d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static void setNewEmailIndicator(Context context, final int unreadCount, 450d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int unseenCount, final Account account, final Folder folder, 451d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean getAttention) { 4522f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.d(LOG_TAG, "setNewEmailIndicator unreadCount = %d, unseenCount = %d, account = %s," 45326a20758413f0f8629ea71a0943304e3ae740165Tony Mantler + " folder = %s, getAttention = %b", unreadCount, unseenCount, 45426a20758413f0f8629ea71a0943304e3ae740165Tony Mantler account.getEmailAddress(), folder.folderUri, getAttention); 4554162f83633a001cf03ff7bbb5e1abae92392049bScott Kennedy 456d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy boolean ignoreUnobtrusiveSetting = false; 457d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 458b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final int notificationId = getNotificationId(account.getAccountManagerAccount(), folder); 459dab8a94a939988e99597845e89cfe3fd1da0ab88Scott Kennedy 460d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Update the notification map 461d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 462d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationKey key = new NotificationKey(account, folder); 463d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unreadCount == 0) { 46426a20758413f0f8629ea71a0943304e3ae740165Tony Mantler LogUtils.d(LOG_TAG, "setNewEmailIndicator - cancelling %s / %s", 46526a20758413f0f8629ea71a0943304e3ae740165Tony Mantler account.getEmailAddress(), folder.persistentId); 466d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.remove(key); 467dab8a94a939988e99597845e89cfe3fd1da0ab88Scott Kennedy ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)) 468dab8a94a939988e99597845e89cfe3fd1da0ab88Scott Kennedy .cancel(notificationId); 469d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 470b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook LogUtils.d(LOG_TAG, "setNewEmailIndicator - update count for: %s / %s " + 47126a20758413f0f8629ea71a0943304e3ae740165Tony Mantler "to: unread: %d unseen %d", account.getEmailAddress(), folder.persistentId, 472b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook unreadCount, unseenCount); 473d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!notificationMap.containsKey(key)) { 474d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // This account previously didn't have any unread mail; ignore the "unobtrusive 475d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // notifications" setting and play sound and/or vibrate the device even if a 476d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // notification already exists (bug 2412348). 477b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook LogUtils.d(LOG_TAG, "setNewEmailIndicator - ignoringUnobtrusiveSetting"); 478d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy ignoreUnobtrusiveSetting = true; 479d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 480d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.put(key, unreadCount, unseenCount); 481d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 482d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.saveNotificationMap(context); 483d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 484d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) { 4852f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.v(LOG_TAG, "New email: %s mapSize: %d getAttention: %b", 486d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy createNotificationString(notificationMap), notificationMap.size(), 487d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy getAttention); 488d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 489d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 490d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (NotificationActionUtils.sUndoNotifications.get(notificationId) == null) { 491d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy validateNotifications(context, folder, account, getAttention, ignoreUnobtrusiveSetting, 492d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy key); 493d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 494d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 495d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 496d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 497d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Validate the notifications notification. 498d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 499d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static void validateNotifications(Context context, final Folder folder, 500d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Account account, boolean getAttention, boolean ignoreUnobtrusiveSetting, 501d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationKey key) { 502d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 50315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationManagerCompat nm = NotificationManagerCompat.from(context); 504d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 505d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 506d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) { 50732fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "Validating Notification: %s mapSize: %d " 508b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook + "folder: %s getAttention: %b ignoreUnobtrusive: %b", 509b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook createNotificationString(notificationMap), 510b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook notificationMap.size(), folder.name, getAttention, ignoreUnobtrusiveSetting); 51132fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy } else { 51232fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "Validating Notification, mapSize: %d " 513b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook + "getAttention: %b ignoreUnobtrusive: %b", notificationMap.size(), 514b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook getAttention, ignoreUnobtrusiveSetting); 515d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 516d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // The number of unread messages for this account and label. 517d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Integer unread = notificationMap.getUnread(key); 518d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int unreadCount = unread != null ? unread.intValue() : 0; 519d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Integer unseen = notificationMap.getUnseen(key); 520d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy int unseenCount = unseen != null ? unseen.intValue() : 0; 521d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 522d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Cursor cursor = null; 523d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 524d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy try { 525d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Uri.Builder uriBuilder = folder.conversationListUri.buildUpon(); 526d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy uriBuilder.appendQueryParameter( 527d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy UIProvider.SEEN_QUERY_PARAMETER, Boolean.FALSE.toString()); 5280be9243458fc713bc34b3ab76bfb0529f7e67871Andy Huang // Do not allow this quick check to disrupt any active network-enabled conversation 5290be9243458fc713bc34b3ab76bfb0529f7e67871Andy Huang // cursor. 5300be9243458fc713bc34b3ab76bfb0529f7e67871Andy Huang uriBuilder.appendQueryParameter( 5310be9243458fc713bc34b3ab76bfb0529f7e67871Andy Huang UIProvider.ConversationListQueryParameters.USE_NETWORK, 5320be9243458fc713bc34b3ab76bfb0529f7e67871Andy Huang Boolean.FALSE.toString()); 533d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy cursor = context.getContentResolver().query(uriBuilder.build(), 534d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy UIProvider.CONVERSATION_PROJECTION, null, null, null); 5353a7b45bd1a03ce6158d766ac8837f1c8cb3758e1Yu Ping Hu if (cursor == null) { 5363a7b45bd1a03ce6158d766ac8837f1c8cb3758e1Yu Ping Hu // This folder doesn't exist. 53732fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, 53832fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy "The cursor is null, so the specified folder probably does not exist"); 5393a7b45bd1a03ce6158d766ac8837f1c8cb3758e1Yu Ping Hu clearFolderNotification(context, account, folder, false); 5403a7b45bd1a03ce6158d766ac8837f1c8cb3758e1Yu Ping Hu return; 5413a7b45bd1a03ce6158d766ac8837f1c8cb3758e1Yu Ping Hu } 542d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int cursorUnseenCount = cursor.getCount(); 543d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 544d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Make sure the unseen count matches the number of items in the cursor. But, we don't 545d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // want to overwrite a 0 unseen count that was specified in the intent 546d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unseenCount != 0 && unseenCount != cursorUnseenCount) { 54732fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, 5482f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy "Unseen count doesn't match cursor count. unseen: %d cursor count: %d", 549d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy unseenCount, cursorUnseenCount); 550d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy unseenCount = cursorUnseenCount; 551d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 552d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 553d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // For the purpose of the notifications, the unseen count should be capped at the num of 554d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // unread conversations. 555d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unseenCount > unreadCount) { 556d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy unseenCount = unreadCount; 557d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 558d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 559b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final int notificationId = 560b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler getNotificationId(account.getAccountManagerAccount(), folder); 561d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 56215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationKey notificationKey = new NotificationKey(account, folder); 56315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 564d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unseenCount == 0) { 56532fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "validateNotifications - cancelling account %s / folder %s", 56626a20758413f0f8629ea71a0943304e3ae740165Tony Mantler LogUtils.sanitizeName(LOG_TAG, account.getEmailAddress()), 56732fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.sanitizeName(LOG_TAG, folder.persistentId)); 568d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy nm.cancel(notificationId); 56915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau cancelChildNotifications(notificationKey, nm); 57015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 571d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return; 572d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 573d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 574d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // We now have all we need to create the notification and the pending intent 575d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy PendingIntent clickIntent; 576d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 577d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationCompat.Builder notification = new NotificationCompat.Builder(context); 578439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau NotificationCompat.WearableExtender wearableExtender = 579439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau new NotificationCompat.WearableExtender(); 580c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau Map<Integer, NotificationBuilders> msgNotifications = 581c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau new ArrayMap<Integer, NotificationBuilders>(); 582d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setSmallIcon(R.drawable.stat_notify_email); 58326a20758413f0f8629ea71a0943304e3ae740165Tony Mantler notification.setTicker(account.getDisplayName()); 584d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 585d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final long when; 586d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 587d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final long oldWhen = 588d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationActionUtils.sNotificationTimestamps.get(notificationId); 589d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (oldWhen != 0) { 590d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy when = oldWhen; 591d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 592d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy when = System.currentTimeMillis(); 593d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 594d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 595d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setWhen(when); 596d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 597d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // The timestamp is now stored in the notification, so we can remove it from here 598d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationActionUtils.sNotificationTimestamps.delete(notificationId); 599d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 600d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Dispatch a CLEAR_NEW_MAIL_NOTIFICATIONS intent if the user taps the "X" next to a 601d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // notification. Also this intent gets fired when the user taps on a notification as 602d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // the AutoCancel flag has been set 603d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Intent cancelNotificationIntent = 604d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new Intent(MailIntentService.ACTION_CLEAR_NEW_MAIL_NOTIFICATIONS); 605d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy cancelNotificationIntent.setPackage(context.getPackageName()); 60609400efa442422299acf21abe20e3470f9d965abScott Kennedy cancelNotificationIntent.setData(Utils.appendVersionQueryParameter(context, 607259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy folder.folderUri.fullUri)); 60848cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy cancelNotificationIntent.putExtra(Utils.EXTRA_ACCOUNT, account); 60948cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy cancelNotificationIntent.putExtra(Utils.EXTRA_FOLDER, folder); 610d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 611d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setDeleteIntent(PendingIntent.getService( 612d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy context, notificationId, cancelNotificationIntent, 0)); 613d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 614d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Ensure that the notification is cleared when the user selects it 615d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setAutoCancel(true); 616d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 617d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy boolean eventInfoConfigured = false; 618d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 619259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy final boolean isInbox = folder.folderUri.equals(account.settings.defaultInbox); 620d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final FolderPreferences folderPreferences = 621afb10d038c70c0a8646f0315836c49c704765ff9Tony Mantler new FolderPreferences(context, account.getEmailAddress(), folder, isInbox); 622d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 623d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (isInbox) { 624d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final AccountPreferences accountPreferences = 625afb10d038c70c0a8646f0315836c49c704765ff9Tony Mantler new AccountPreferences(context, account.getEmailAddress()); 626d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy moveNotificationSetting(accountPreferences, folderPreferences); 627d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 628d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 629d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!folderPreferences.areNotificationsEnabled()) { 63032fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "Notifications are disabled for this folder; not notifying"); 631d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Don't notify 632d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return; 633d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 634d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 635d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unreadCount > 0) { 636d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // How can I order this properly? 637d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (cursor.moveToNext()) { 638bd8acad098abbf074184aa1273d269f7f7a54b37Scott Kennedy final Intent notificationIntent; 639d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 640bd8acad098abbf074184aa1273d269f7f7a54b37Scott Kennedy // Launch directly to the conversation, if there is only 1 unseen conversation 6414fe0af81874976a1995191321e35c844b2229811Andy Huang final boolean launchConversationMode = (unseenCount == 1); 6424fe0af81874976a1995191321e35c844b2229811Andy Huang if (launchConversationMode) { 64309400efa442422299acf21abe20e3470f9d965abScott Kennedy notificationIntent = createViewConversationIntent(context, account, folder, 64409400efa442422299acf21abe20e3470f9d965abScott Kennedy cursor); 645bd8acad098abbf074184aa1273d269f7f7a54b37Scott Kennedy } else { 646bd8acad098abbf074184aa1273d269f7f7a54b37Scott Kennedy notificationIntent = createViewConversationIntent(context, account, folder, 647bd8acad098abbf074184aa1273d269f7f7a54b37Scott Kennedy null); 648d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 649d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 6504fe0af81874976a1995191321e35c844b2229811Andy Huang Analytics.getInstance().sendEvent("notification_create", 6514fe0af81874976a1995191321e35c844b2229811Andy Huang launchConversationMode ? "conversation" : "conversation_list", 6524fe0af81874976a1995191321e35c844b2229811Andy Huang folder.getTypeDescription(), unseenCount); 6534fe0af81874976a1995191321e35c844b2229811Andy Huang 654d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (notificationIntent == null) { 6552f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.e(LOG_TAG, "Null intent when building notification"); 656d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return; 657d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 658d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 65905a18446a96fa8ea2ae077b482499abf67366e32Alan Lau clickIntent = createClickPendingIntent(context, notificationIntent); 6604fe0af81874976a1995191321e35c844b2229811Andy Huang 661d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy configureLatestEventInfoFromConversation(context, account, folderPreferences, 662439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau notification, wearableExtender, msgNotifications, notificationId, 66315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau cursor, clickIntent, notificationIntent, unreadCount, unseenCount, 66415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau folder, when); 665d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy eventInfoConfigured = true; 666d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 667d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 668d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 669d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean vibrate = folderPreferences.isNotificationVibrateEnabled(); 670d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String ringtoneUri = folderPreferences.getNotificationRingtoneUri(); 671d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean notifyOnce = !folderPreferences.isEveryMessageNotificationEnabled(); 672d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 673ff8553f20964f4c31b0c503a9e1daff6ae08a9c7Scott Kennedy if (!ignoreUnobtrusiveSetting && notifyOnce) { 674d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If the user has "unobtrusive notifications" enabled, only alert the first time 675d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // new mail is received in this account. This is the default behavior. See 676d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // bugs 2412348 and 2413490. 677b1b855b71e502459e1571444fb1a9f74883c8330Paul Westbrook LogUtils.d(LOG_TAG, "Setting Alert Once"); 678d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setOnlyAlertOnce(true); 679d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 680d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 68132fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "Account: %s vibrate: %s", 68226a20758413f0f8629ea71a0943304e3ae740165Tony Mantler LogUtils.sanitizeName(LOG_TAG, account.getEmailAddress()), 683ff8553f20964f4c31b0c503a9e1daff6ae08a9c7Scott Kennedy Boolean.toString(folderPreferences.isNotificationVibrateEnabled())); 684d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 685d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy int defaults = 0; 686d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 68715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Check if any current child notifications exist previously. Only notify if one of 68815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // them is new. 68915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau boolean hasNewChildNotification; 69015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau Set<Integer> prevChildNotifications = sChildNotificationsMap.get(notificationKey); 69115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (prevChildNotifications != null) { 69215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau hasNewChildNotification = false; 69315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau for (Integer currentNotificationId : msgNotifications.keySet()) { 69415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (!prevChildNotifications.contains(currentNotificationId)) { 69515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau hasNewChildNotification = true; 69615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau break; 69715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 69815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 69915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } else { 70015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau hasNewChildNotification = true; 70115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 70215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 70315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau LogUtils.d(LOG_TAG, "getAttention=%s,oldWhen=%s,hasNewChildNotification=%s", 70415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau getAttention, oldWhen, hasNewChildNotification); 70515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 706d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /* 707d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * We do not want to notify if this is coming back from an Undo notification, hence the 708d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * oldWhen check. 709d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 71015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (getAttention && oldWhen == 0 && hasNewChildNotification) { 711d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final AccountPreferences accountPreferences = 712cfb969b27f83777e08cd2f1a994dc023574a4739Tony Mantler new AccountPreferences(context, account.getEmailAddress()); 713d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (accountPreferences.areNotificationsEnabled()) { 714d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (vibrate) { 715d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy defaults |= Notification.DEFAULT_VIBRATE; 716d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 717d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 718d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setSound(TextUtils.isEmpty(ringtoneUri) ? null 719d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy : Uri.parse(ringtoneUri)); 72032fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "New email in %s vibrateWhen: %s, playing notification: %s", 72126a20758413f0f8629ea71a0943304e3ae740165Tony Mantler LogUtils.sanitizeName(LOG_TAG, account.getEmailAddress()), vibrate, 72226a20758413f0f8629ea71a0943304e3ae740165Tony Mantler ringtoneUri); 723d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 724d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 725d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 72632fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy // TODO(skennedy) Why do we do any of the above if we're just going to bail here? 727d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (eventInfoConfigured) { 728d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy defaults |= Notification.DEFAULT_LIGHTS; 729d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setDefaults(defaults); 730d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 731d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (oldWhen != 0) { 732d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // We do not want to display the ticker again if we are re-displaying this 733d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // notification (like from an Undo notification) 734d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setTicker(null); 735d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 736d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 737439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau notification.extend(wearableExtender); 738c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau nm.notify(notificationId, notification.build()); 73915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 74015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (prevChildNotifications != null) { 74115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau Set<Integer> currentNotificationIds = msgNotifications.keySet(); 74215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau for (Integer prevChildNotificationId : prevChildNotifications) { 74315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (!currentNotificationIds.contains(prevChildNotificationId)) { 74415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau nm.cancel(prevChildNotificationId); 74515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau LogUtils.d(LOG_TAG, "canceling child notification %s", 74615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau prevChildNotificationId); 74715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 74815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 74915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 75015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 751c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau for (Map.Entry<Integer, NotificationBuilders> entry : msgNotifications.entrySet()) { 752c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau NotificationBuilders builders = entry.getValue(); 753439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau builders.notifBuilder.extend(builders.wearableNotifBuilder); 754c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau nm.notify(entry.getKey(), builders.notifBuilder.build()); 75515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau LogUtils.d(LOG_TAG, "notifying child notification %s", entry.getKey()); 75615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 75715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 75815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau Set<Integer> childNotificationIds = new HashSet<Integer>(); 75915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau childNotificationIds.addAll(msgNotifications.keySet()); 76015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau sChildNotificationsMap.put(notificationKey, childNotificationIds); 76132fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy } else { 76232fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "event info not configured - not notifying"); 763d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 764d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } finally { 765d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (cursor != null) { 766d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy cursor.close(); 767d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 768d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 769d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 770d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 77105a18446a96fa8ea2ae077b482499abf67366e32Alan Lau private static PendingIntent createClickPendingIntent(Context context, 77205a18446a96fa8ea2ae077b482499abf67366e32Alan Lau Intent notificationIntent) { 77305a18446a96fa8ea2ae077b482499abf67366e32Alan Lau // Amend the click intent with a hint that its source was a notification, 77405a18446a96fa8ea2ae077b482499abf67366e32Alan Lau // but remove the hint before it's used to generate notification action 77505a18446a96fa8ea2ae077b482499abf67366e32Alan Lau // intents. This prevents the following sequence: 77605a18446a96fa8ea2ae077b482499abf67366e32Alan Lau // 1. generate single notification 77705a18446a96fa8ea2ae077b482499abf67366e32Alan Lau // 2. user clicks reply, then completes Compose activity 77805a18446a96fa8ea2ae077b482499abf67366e32Alan Lau // 3. main activity launches, gets FROM_NOTIFICATION hint in intent 77905a18446a96fa8ea2ae077b482499abf67366e32Alan Lau notificationIntent.putExtra(Utils.EXTRA_FROM_NOTIFICATION, true); 78005a18446a96fa8ea2ae077b482499abf67366e32Alan Lau PendingIntent clickIntent = PendingIntent.getActivity(context, -1, notificationIntent, 78105a18446a96fa8ea2ae077b482499abf67366e32Alan Lau PendingIntent.FLAG_UPDATE_CURRENT); 78205a18446a96fa8ea2ae077b482499abf67366e32Alan Lau notificationIntent.removeExtra(Utils.EXTRA_FROM_NOTIFICATION); 78305a18446a96fa8ea2ae077b482499abf67366e32Alan Lau return clickIntent; 78405a18446a96fa8ea2ae077b482499abf67366e32Alan Lau } 78505a18446a96fa8ea2ae077b482499abf67366e32Alan Lau 786d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 787d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @return an {@link Intent} which, if launched, will display the corresponding conversation 788d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 78909400efa442422299acf21abe20e3470f9d965abScott Kennedy private static Intent createViewConversationIntent(final Context context, final Account account, 79009400efa442422299acf21abe20e3470f9d965abScott Kennedy final Folder folder, final Cursor cursor) { 791d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (folder == null || account == null) { 7922f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.e(LOG_TAG, "createViewConversationIntent(): " 79313a732747e1cf835b04211fad09b722accf877d5Scott Kennedy + "Null account or folder. account: %s folder: %s", account, folder); 794d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return null; 795d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 796d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 797d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Intent intent; 798d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 799d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (cursor == null) { 800259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy intent = Utils.createViewFolderIntent(context, folder.folderUri.fullUri, account); 801d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 802d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // A conversation cursor has been specified, so this intent is intended to be go 803d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // directly to the one new conversation 804d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 805d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Get the Conversation object 806d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Conversation conversation = new Conversation(cursor); 807259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy intent = Utils.createViewConversationIntent(context, conversation, 808259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy folder.folderUri.fullUri, account); 809d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 810d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 811d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return intent; 812d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 813d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 81461bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy private static Bitmap getDefaultNotificationIcon( 81561bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy final Context context, final Folder folder, final boolean multipleNew) { 816c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final int resId; 81761bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy if (folder.notificationIconResId != 0) { 818c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy resId = folder.notificationIconResId; 81961bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy } else if (multipleNew) { 820c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy resId = R.drawable.ic_notification_multiple_mail_holo_dark; 821d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 822c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy resId = R.drawable.ic_contact_picture; 823d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 824c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 825c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final Bitmap icon = getIcon(context, resId); 826c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 827c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy if (icon == null) { 828c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy LogUtils.e(LOG_TAG, "Couldn't decode notif icon res id %d", resId); 829c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy } 830c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 831d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return icon; 832d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 833d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 83461bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy private static Bitmap getIcon(final Context context, final int resId) { 83561bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy final Bitmap cachedIcon = sNotificationIcons.get(resId); 83661bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy if (cachedIcon != null) { 83761bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy return cachedIcon; 83861bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy } 83961bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy 84061bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy final Bitmap icon = BitmapFactory.decodeResource(context.getResources(), resId); 84161bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy sNotificationIcons.put(resId, icon); 84261bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy 84361bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy return icon; 84461bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy } 84561bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy 846644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau private static Bitmap getDefaultWearableBg(Context context) { 847644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau Bitmap bg = sDefaultWearableBg.get(); 848644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (bg == null) { 849644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau bg = BitmapFactory.decodeResource(context.getResources(), R.drawable.bg_email); 850644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau sDefaultWearableBg = new WeakReference<Bitmap>(bg); 851644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 852644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau return bg; 853644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 854644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 855d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static void configureLatestEventInfoFromConversation(final Context context, 856d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Account account, final FolderPreferences folderPreferences, 85715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final NotificationCompat.Builder notification, 858439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau final NotificationCompat.WearableExtender wearableExtender, 859c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau final Map<Integer, NotificationBuilders> msgNotifications, 86015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final int summaryNotificationId, final Cursor conversationCursor, 861d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final PendingIntent clickIntent, final Intent notificationIntent, 862b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final int unreadCount, final int unseenCount, 863d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Folder folder, final long when) { 864d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Resources res = context.getResources(); 86526a20758413f0f8629ea71a0943304e3ae740165Tony Mantler final String notificationAccountDisplayName = account.getDisplayName(); 86626a20758413f0f8629ea71a0943304e3ae740165Tony Mantler final String notificationAccountEmail = account.getEmailAddress(); 867d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 86832fc8e41dde99760eb769a0d0c17f6c485a0b5cfScott Kennedy LogUtils.i(LOG_TAG, "Showing notification with unreadCount of %d and unseenCount of %d", 8692f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy unreadCount, unseenCount); 870d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 871d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String notificationTicker = null; 872d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 873d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Boolean indicating that this notification is for a non-inbox label. 874259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy final boolean isInbox = folder.folderUri.fullUri.equals(account.settings.defaultInbox); 875d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 876d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Notification label name for user label notifications. 877d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String notificationLabelName = isInbox ? null : folder.name; 878d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 879d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (unseenCount > 1) { 880d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Build the string that describes the number of new messages 881d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String newMessagesString = res.getString(R.string.new_messages, unseenCount); 882d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 883d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Use the default notification icon 884d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setLargeIcon( 88561bd0e84a3b0545a722bbfe931233b47afdf52e2Scott Kennedy getDefaultNotificationIcon(context, folder, true /* multiple new messages */)); 886d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 887d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // The ticker initially start as the new messages string. 888d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationTicker = newMessagesString; 889d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 890d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // The title of the notification is the new messages string 891d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setContentTitle(newMessagesString); 892d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 893c8bc5d10567d917c73e8fd286ea19a4c7f076d89Scott Kennedy // TODO(skennedy) Can we remove this check? 894d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (com.android.mail.utils.Utils.isRunningJellybeanOrLater()) { 895d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // For a new-style notification 896d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int maxNumDigestItems = context.getResources().getInteger( 897d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy R.integer.max_num_notification_digest_items); 898d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 899d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // The body of the notification is the account name, or the label name. 90026a20758413f0f8629ea71a0943304e3ae740165Tony Mantler notification.setSubText( 90126a20758413f0f8629ea71a0943304e3ae740165Tony Mantler isInbox ? notificationAccountDisplayName : notificationLabelName); 902d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 903d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationCompat.InboxStyle digest = 904d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new NotificationCompat.InboxStyle(notification); 905d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 90615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Group by account. 90715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau String notificationGroupKey = 90815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau account.uri.toString() + "/" + folder.folderUri.fullUri; 909439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau notification.setGroup(notificationGroupKey).setGroupSummary(true); 91015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 911644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau ConfigResult firstResult = null; 912d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy int numDigestItems = 0; 913d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy do { 914d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Conversation conversation = new Conversation(conversationCursor); 915d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 916d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!conversation.read) { 917d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy boolean multipleUnreadThread = false; 918d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // TODO(cwren) extract this pattern into a helper 919d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 920d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Cursor cursor = null; 921d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy MessageCursor messageCursor = null; 922d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy try { 923d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Uri.Builder uriBuilder = conversation.messageListUri.buildUpon(); 924d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy uriBuilder.appendQueryParameter( 925d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy UIProvider.LABEL_QUERY_PARAMETER, notificationLabelName); 926d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy cursor = context.getContentResolver().query(uriBuilder.build(), 927d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy UIProvider.MESSAGE_PROJECTION, null, null, null); 928d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy messageCursor = new MessageCursor(cursor); 929d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 930b18d75ad0651b8806773d2b79da35dc725390edbYu Ping Hu String from = ""; 931d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String fromAddress = ""; 932d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) { 933d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Message message = messageCursor.getMessage(); 934d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy fromAddress = message.getFrom(); 935b18d75ad0651b8806773d2b79da35dc725390edbYu Ping Hu if (fromAddress == null) { 936b18d75ad0651b8806773d2b79da35dc725390edbYu Ping Hu fromAddress = ""; 937b18d75ad0651b8806773d2b79da35dc725390edbYu Ping Hu } 938d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy from = getDisplayableSender(fromAddress); 939d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 940d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) { 941d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Message message = messageCursor.getMessage(); 942d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!message.read && 943d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy !fromAddress.contentEquals(message.getFrom())) { 944d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy multipleUnreadThread = true; 945d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy break; 946d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 947d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 948d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableStringBuilder sendersBuilder; 949d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (multipleUnreadThread) { 950d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int sendersLength = 951d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy res.getInteger(R.integer.swipe_senders_length); 952d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 953d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sendersBuilder = getStyledSenders(context, conversationCursor, 95426a20758413f0f8629ea71a0943304e3ae740165Tony Mantler sendersLength, notificationAccountEmail); 955d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 95612fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook sendersBuilder = 95712fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook new SpannableStringBuilder(getWrappedFromString(from)); 958d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 959d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final CharSequence digestLine = getSingleMessageInboxLine(context, 960d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sendersBuilder.toString(), 96189bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein ConversationItemView.filterTag(context, conversation.subject), 962edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler conversation.getSnippet()); 963d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy digest.addLine(digestLine); 964d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy numDigestItems++; 96515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 96615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Adding child notification for Wear. 96715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationCompat.Builder childNotif = 96815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau new NotificationCompat.Builder(context); 96915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau childNotif.setSmallIcon(R.drawable.stat_notify_email); 97015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau childNotif.setContentText(digestLine); 97105a18446a96fa8ea2ae077b482499abf67366e32Alan Lau Intent childNotificationIntent = createViewConversationIntent(context, 97205a18446a96fa8ea2ae077b482499abf67366e32Alan Lau account, folder, conversationCursor); 97305a18446a96fa8ea2ae077b482499abf67366e32Alan Lau PendingIntent childClickIntent = createClickPendingIntent(context, 97405a18446a96fa8ea2ae077b482499abf67366e32Alan Lau childNotificationIntent); 97505a18446a96fa8ea2ae077b482499abf67366e32Alan Lau childNotif.setContentIntent(childClickIntent); 97615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 977c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau // TODO: Use a stable sort key if possible, e.g. message post time 978c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau // + msgid hash 979c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau String groupSortKey = String.format("%010d", numDigestItems); 980439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau childNotif.setGroup(notificationGroupKey); 981439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau childNotif.setSortKey(groupSortKey); 982439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau 98315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau int childNotificationId = getNotificationId(summaryNotificationId, 98415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau conversation.hashCode()); 98515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 986439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau NotificationCompat.WearableExtender childWearExtender = 987439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau new NotificationCompat.WearableExtender(); 988439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau 989644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau ConfigResult result = configureNotifForOneConversation(context, account, 990439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau folderPreferences, childNotif, childWearExtender, 991644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau conversationCursor, notificationIntent, folder, when, res, 99215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationAccountDisplayName, notificationAccountEmail, 99315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau isInbox, notificationLabelName, childNotificationId); 994c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau msgNotifications.put(childNotificationId, 995439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau NotificationBuilders.of(childNotif, childWearExtender)); 996644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 997644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (firstResult == null) { 998644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau firstResult = result; 999644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 1000d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } finally { 1001d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (messageCursor != null) { 1002d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy messageCursor.close(); 1003d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1004d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (cursor != null) { 1005d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy cursor.close(); 1006d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1007d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1008d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1009d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } while (numDigestItems <= maxNumDigestItems && conversationCursor.moveToNext()); 1010644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1011644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (firstResult != null && firstResult.contactIconInfo != null) { 1012439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau wearableExtender.setBackground(firstResult.contactIconInfo.wearableBg); 1013644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } else { 1014644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau LogUtils.w(LOG_TAG, "First contact icon is null!"); 1015439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau wearableExtender.setBackground(getDefaultWearableBg(context)); 1016644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 1017d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 1018d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // The body of the notification is the account name, or the label name. 1019d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notification.setContentText( 102026a20758413f0f8629ea71a0943304e3ae740165Tony Mantler isInbox ? notificationAccountDisplayName : notificationLabelName); 1021d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1022d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 102315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // For notifications for a single new conversation, we want to get the information 102415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // from the conversation 1025d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1026d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Move the cursor to the most recent unread conversation 1027d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy seekToLatestUnreadConversation(conversationCursor); 1028d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1029644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau ConfigResult result = configureNotifForOneConversation(context, account, 1030439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau folderPreferences, notification, wearableExtender, conversationCursor, 103115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationIntent, folder, when, res, notificationAccountDisplayName, 103215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationAccountEmail, isInbox, notificationLabelName, 103315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau summaryNotificationId); 1034644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau notificationTicker = result.notificationTicker; 1035644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1036439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau wearableExtender.setBackground(result.contactIconInfo.wearableBg); 103715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1038d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 103915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Build the notification ticker 104015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (notificationLabelName != null && notificationTicker != null) { 104115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // This is a per label notification, format the ticker with that information 104215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationTicker = res.getString(R.string.label_notification_ticker, 104315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationLabelName, notificationTicker); 104415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1045d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 104615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (notificationTicker != null) { 104715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // If we didn't generate a notification ticker, it will default to account name 104815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setTicker(notificationTicker); 104915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 105015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 105115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Set the number in the notification 105215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (unreadCount > 1) { 105315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setNumber(unreadCount); 105415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1055d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 105615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setContentIntent(clickIntent); 105715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1058d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 105915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau /** 106015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau * Configure the notification for one conversation. When there are multiple conversations, 106115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau * this method is used to configure bundled notification for Android Wear. 106215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau */ 1063644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau private static ConfigResult configureNotifForOneConversation(Context context, 1064644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau Account account, FolderPreferences folderPreferences, 1065644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau NotificationCompat.Builder notification, 1066439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau NotificationCompat.WearableExtender wearExtender, Cursor conversationCursor, 106715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau Intent notificationIntent, Folder folder, long when, Resources res, 106815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau String notificationAccountDisplayName, String notificationAccountEmail, boolean isInbox, 106915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau String notificationLabelName, int notificationId) { 1070d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1071644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau ConfigResult result = new ConfigResult(); 1072d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 107315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final Conversation conversation = new Conversation(conversationCursor); 1074d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 107515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau Cursor cursor = null; 107615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau MessageCursor messageCursor = null; 107715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau boolean multipleUnseenThread = false; 107815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau String from = null; 107915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau try { 108015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final Uri uri = conversation.messageListUri.buildUpon().appendQueryParameter( 108115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau UIProvider.LABEL_QUERY_PARAMETER, folder.persistentId).build(); 108215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau cursor = context.getContentResolver().query(uri, UIProvider.MESSAGE_PROJECTION, 108315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau null, null, null); 108415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau messageCursor = new MessageCursor(cursor); 108515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Use the information from the last sender in the conversation that triggered 108615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // this notification. 108715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 108815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau String fromAddress = ""; 108915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) { 109015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final Message message = messageCursor.getMessage(); 109115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau fromAddress = message.getFrom(); 109215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau from = getDisplayableSender(fromAddress); 1093644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau result.contactIconInfo = getContactIcon(context, from, 1094644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau getSenderAddress(fromAddress), folder); 1095644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau notification.setLargeIcon(result.contactIconInfo.icon); 109615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1097d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 109815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Assume that the last message in this conversation is unread 109915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau int firstUnseenMessagePos = messageCursor.getPosition(); 110015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) { 110115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final Message message = messageCursor.getMessage(); 110215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final boolean unseen = !message.seen; 110315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (unseen) { 110415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau firstUnseenMessagePos = messageCursor.getPosition(); 110515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (!multipleUnseenThread 110615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau && !fromAddress.contentEquals(message.getFrom())) { 110715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau multipleUnseenThread = true; 110892b21746be07f31fdbe2aee510f9fcd8726a9cb3Paul Westbrook } 110915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 111015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1111d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 111289bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein final String subject = ConversationItemView.filterTag(context, conversation.subject); 111389bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein 111415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // TODO(skennedy) Can we remove this check? 111515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (Utils.isRunningJellybeanOrLater()) { 111615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // For a new-style notification 1117d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 111815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (multipleUnseenThread) { 111915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // The title of a single conversation is the list of senders. 112015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau int sendersLength = res.getInteger(R.integer.swipe_senders_length); 1121d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 112215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final SpannableStringBuilder sendersBuilder = getStyledSenders( 112315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau context, conversationCursor, sendersLength, 112415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationAccountEmail); 112515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 112615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setContentTitle(sendersBuilder); 112715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // For a single new conversation, the ticker is based on the sender's name. 1128644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau result.notificationTicker = sendersBuilder.toString(); 1129d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 113015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau from = getWrappedFromString(from); 113115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // The title of a single message the sender. 113215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setContentTitle(from); 113315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // For a single new conversation, the ticker is based on the sender's name. 1134644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau result.notificationTicker = from; 113515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1136d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 113715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // The notification content will be the subject of the conversation. 113889bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein notification.setContentText(getSingleMessageLittleText(context, subject)); 1139d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 114015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // The notification subtext will be the subject of the conversation for inbox 114115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // notifications, or will based on the the label name for user label 114215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // notifications. 114315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setSubText(isInbox ? 114415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notificationAccountDisplayName : notificationLabelName); 1145d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 114615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (multipleUnseenThread) { 114715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setLargeIcon( 114815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau getDefaultNotificationIcon(context, folder, true)); 1149d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 115015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final NotificationCompat.BigTextStyle bigText = 115115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau new NotificationCompat.BigTextStyle(notification); 115215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 115315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // Seek the message cursor to the first unread message 115415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final Message message; 115515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (messageCursor.moveToPosition(firstUnseenMessagePos)) { 115615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau message = messageCursor.getMessage(); 115789bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein bigText.bigText(getSingleMessageBigText(context, subject, message)); 115815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } else { 115915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau LogUtils.e(LOG_TAG, "Failed to load message"); 116015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau message = null; 1161d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 116215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 116315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (message != null) { 116415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final Set<String> notificationActions = 116515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau folderPreferences.getNotificationActions(account); 116615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 116715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationActionUtils.addNotificationActions(context, notificationIntent, 1168439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau notification, wearExtender, account, conversation, message, 116915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau folder, notificationId, when, notificationActions); 1170d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 117115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } else { 117215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // For an old-style notification 1173d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 117415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // The title of a single conversation notification is built from both the sender 117515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // and subject of the new message. 117689bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein notification.setContentTitle( 117789bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein getSingleMessageNotificationTitle(context, from, subject)); 1178d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 117915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // The notification content will be the subject of the conversation for inbox 118015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // notifications, or will based on the the label name for user label 118115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // notifications. 118215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau notification.setContentText( 118315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau isInbox ? notificationAccountDisplayName : notificationLabelName); 1184d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 118515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau // For a single new conversation, the ticker is based on the sender's name. 1186644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau result.notificationTicker = from; 118715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 118815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } finally { 118915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (messageCursor != null) { 119015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau messageCursor.close(); 119115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 119215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (cursor != null) { 119315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau cursor.close(); 119415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 1195d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1196644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau return result; 1197d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1198d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 119912fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook private static String getWrappedFromString(String from) { 120012fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook if (from == null) { 120112fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook LogUtils.e(LOG_TAG, "null from string in getWrappedFromString"); 120212fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook from = ""; 120312fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook } 1204963c7e4f178037acebddfd13e16fc627ef368c82Andrew Sapperstein from = sBidiFormatter.unicodeWrap(from); 120512fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook return from; 120612fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook } 120712fb02b96c7394172ad9798a9de25eeb7611aafdPaul Westbrook 1208d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static SpannableStringBuilder getStyledSenders(final Context context, 1209d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Cursor conversationCursor, final int maxLength, final String account) { 1210d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Conversation conversation = new Conversation(conversationCursor); 1211d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final com.android.mail.providers.ConversationInfo conversationInfo = 1212d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy conversation.conversationInfo; 1213d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final ArrayList<SpannableString> senders = new ArrayList<SpannableString>(); 1214d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (sNotificationUnreadStyleSpan == null) { 1215d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sNotificationUnreadStyleSpan = new TextAppearanceSpan( 1216d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy context, R.style.NotificationSendersUnreadTextAppearance); 1217d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sNotificationReadStyleSpan = 1218d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new TextAppearanceSpan(context, R.style.NotificationSendersReadTextAppearance); 1219d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1220d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy SendersView.format(context, conversationInfo, "", maxLength, senders, null, null, account, 122110ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux sNotificationUnreadStyleSpan, sNotificationReadStyleSpan, 122210ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux false /* showToHeader */, false /* resourceCachingRequired */); 1223d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1224d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return ellipsizeStyledSenders(context, senders); 1225d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1226d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1227d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static String sSendersSplitToken = null; 1228d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static String sElidedPaddingToken = null; 1229d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1230d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static SpannableStringBuilder ellipsizeStyledSenders(final Context context, 1231d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy ArrayList<SpannableString> styledSenders) { 1232d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (sSendersSplitToken == null) { 1233d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sSendersSplitToken = context.getString(R.string.senders_split_token); 1234d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sElidedPaddingToken = context.getString(R.string.elided_padding_token); 1235d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1236d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1237d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy SpannableStringBuilder builder = new SpannableStringBuilder(); 1238d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy SpannableString prevSender = null; 1239d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (SpannableString sender : styledSenders) { 1240d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (sender == null) { 12412f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.e(LOG_TAG, "null sender iterating over styledSenders"); 1242d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy continue; 1243d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1244d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy CharacterStyle[] spans = sender.getSpans(0, sender.length(), CharacterStyle.class); 1245d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (SendersView.sElidedString.equals(sender.toString())) { 1246d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy prevSender = sender; 1247d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sender = copyStyles(spans, sElidedPaddingToken + sender + sElidedPaddingToken); 1248d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else if (builder.length() > 0 1249d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy && (prevSender == null || !SendersView.sElidedString.equals(prevSender 1250d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy .toString()))) { 1251d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy prevSender = sender; 1252d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy sender = copyStyles(spans, sSendersSplitToken + sender); 1253d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 1254d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy prevSender = sender; 1255d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1256d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy builder.append(sender); 1257d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1258d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return builder; 1259d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1260d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1261d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static SpannableString copyStyles(CharacterStyle[] spans, CharSequence newText) { 1262d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy SpannableString s = new SpannableString(newText); 1263d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (spans != null && spans.length > 0) { 1264d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy s.setSpan(spans[0], 0, s.length(), 0); 1265d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1266d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return s; 1267d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1268d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1269d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1270d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Seeks the cursor to the position of the most recent unread conversation. If no unread 1271d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * conversation is found, the position of the cursor will be restored, and false will be 1272d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * returned. 1273d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1274d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static boolean seekToLatestUnreadConversation(final Cursor cursor) { 1275d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int initialPosition = cursor.getPosition(); 1276d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy do { 1277d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Conversation conversation = new Conversation(cursor); 1278d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!conversation.read) { 1279d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return true; 1280d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1281d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } while (cursor.moveToNext()); 1282d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1283d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Didn't find an unread conversation, reset the position. 1284d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy cursor.moveToPosition(initialPosition); 1285d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return false; 1286d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1287d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1288d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1289d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Sets the bigtext for a notification for a single new conversation 1290d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * 1291d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param context 1292d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param senders Sender of the new message that triggered the notification. 1293d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param subject Subject of the new message that triggered the notification 1294d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param snippet Snippet of the new message that triggered the notification 1295d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @return a {@link CharSequence} suitable for use in 1296d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * {@link android.support.v4.app.NotificationCompat.BigTextStyle} 1297d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1298d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static CharSequence getSingleMessageInboxLine(Context context, 1299d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String senders, String subject, String snippet) { 1300d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // TODO(cwren) finish this step toward commmon code with getSingleMessageBigText 1301d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1302d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String subjectSnippet = !TextUtils.isEmpty(subject) ? subject : snippet; 1303d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1304d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final TextAppearanceSpan notificationPrimarySpan = 1305d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new TextAppearanceSpan(context, R.style.NotificationPrimaryText); 1306d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1307d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (TextUtils.isEmpty(senders)) { 1308d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If the senders are empty, just use the subject/snippet. 1309d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return subjectSnippet; 1310d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else if (TextUtils.isEmpty(subjectSnippet)) { 1311d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If the subject/snippet is empty, just use the senders. 1312d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableString spannableString = new SpannableString(senders); 1313d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy spannableString.setSpan(notificationPrimarySpan, 0, senders.length(), 0); 1314d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1315d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return spannableString; 1316d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 1317d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String formatString = context.getResources().getString( 1318d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy R.string.multiple_new_message_notification_item); 1319d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final TextAppearanceSpan notificationSecondarySpan = 1320d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new TextAppearanceSpan(context, R.style.NotificationSecondaryText); 1321d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 13228173cf41d2acf48c7b45ae3e7868fc391aae0359Andrew Sapperstein // senders is already individually unicode wrapped so it does not need to be done here 1323f58109699c207a2eaeca38d557d9b9a7e7664decAndrew Sapperstein final String instantiatedString = String.format(formatString, 13248173cf41d2acf48c7b45ae3e7868fc391aae0359Andrew Sapperstein senders, 1325963c7e4f178037acebddfd13e16fc627ef368c82Andrew Sapperstein sBidiFormatter.unicodeWrap(subjectSnippet)); 1326d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1327d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableString spannableString = new SpannableString(instantiatedString); 1328d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1329d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean isOrderReversed = formatString.indexOf("%2$s") < 1330d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy formatString.indexOf("%1$s"); 1331d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int primaryOffset = 1332d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy (isOrderReversed ? instantiatedString.lastIndexOf(senders) : 1333d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy instantiatedString.indexOf(senders)); 1334d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int secondaryOffset = 1335d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy (isOrderReversed ? instantiatedString.lastIndexOf(subjectSnippet) : 1336d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy instantiatedString.indexOf(subjectSnippet)); 1337d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy spannableString.setSpan(notificationPrimarySpan, 1338d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy primaryOffset, primaryOffset + senders.length(), 0); 1339d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy spannableString.setSpan(notificationSecondarySpan, 1340d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy secondaryOffset, secondaryOffset + subjectSnippet.length(), 0); 1341d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return spannableString; 1342d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1343d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1344d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1345d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1346d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Sets the bigtext for a notification for a single new conversation 1347d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param context 1348d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param subject Subject of the new message that triggered the notification 1349f58109699c207a2eaeca38d557d9b9a7e7664decAndrew Sapperstein * @return a {@link CharSequence} suitable for use in 1350f58109699c207a2eaeca38d557d9b9a7e7664decAndrew Sapperstein * {@link NotificationCompat.Builder#setContentText} 1351d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1352d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static CharSequence getSingleMessageLittleText(Context context, String subject) { 1353d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final TextAppearanceSpan notificationSubjectSpan = new TextAppearanceSpan( 1354d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy context, R.style.NotificationPrimaryText); 1355d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1356d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableString spannableString = new SpannableString(subject); 1357d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy spannableString.setSpan(notificationSubjectSpan, 0, subject.length(), 0); 1358d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1359d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return spannableString; 1360d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1361d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1362d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1363d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Sets the bigtext for a notification for a single new conversation 1364d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * 1365d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param context 1366d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param subject Subject of the new message that triggered the notification 1367d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param message the {@link Message} to be displayed. 1368d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @return a {@link CharSequence} suitable for use in 1369d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * {@link android.support.v4.app.NotificationCompat.BigTextStyle} 1370d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1371d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static CharSequence getSingleMessageBigText(Context context, String subject, 1372d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final Message message) { 1373d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1374d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final TextAppearanceSpan notificationSubjectSpan = new TextAppearanceSpan( 1375d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy context, R.style.NotificationPrimaryText); 1376d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1377d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String snippet = getMessageBodyWithoutElidedText(message); 1378d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1379d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Change multiple newlines (with potential white space between), into a single new line 1380d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String collapsedSnippet = 1381d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy !TextUtils.isEmpty(snippet) ? snippet.replaceAll("\\n\\s+", "\n") : ""; 1382d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1383d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (TextUtils.isEmpty(subject)) { 1384d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If the subject is empty, just use the snippet. 1385d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return snippet; 1386d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else if (TextUtils.isEmpty(collapsedSnippet)) { 1387d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If the snippet is empty, just use the subject. 1388d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableString spannableString = new SpannableString(subject); 1389d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy spannableString.setSpan(notificationSubjectSpan, 0, subject.length(), 0); 1390d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1391d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return spannableString; 1392d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 1393d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String notificationBigTextFormat = context.getResources().getString( 1394d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy R.string.single_new_message_notification_big_text); 1395d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1396d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Localizers may change the order of the parameters, look at how the format 1397d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // string is structured. 1398d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean isSubjectFirst = notificationBigTextFormat.indexOf("%2$s") > 1399d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationBigTextFormat.indexOf("%1$s"); 1400d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String bigText = 1401d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String.format(notificationBigTextFormat, subject, collapsedSnippet); 1402d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableString spannableString = new SpannableString(bigText); 1403d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1404d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int subjectOffset = 1405d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy (isSubjectFirst ? bigText.indexOf(subject) : bigText.lastIndexOf(subject)); 1406d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy spannableString.setSpan(notificationSubjectSpan, 1407d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy subjectOffset, subjectOffset + subject.length(), 0); 1408d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1409d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return spannableString; 1410d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1411d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1412d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1413d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1414d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Gets the title for a notification for a single new conversation 1415d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param context 1416d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param sender Sender of the new message that triggered the notification. 1417d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @param subject Subject of the new message that triggered the notification 1418d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * @return a {@link CharSequence} suitable for use as a {@link Notification} title. 1419d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1420d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static CharSequence getSingleMessageNotificationTitle(Context context, 1421d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String sender, String subject) { 1422d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1423d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (TextUtils.isEmpty(subject)) { 1424d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If the subject is empty, just set the title to the sender's information. 1425d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return sender; 1426d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else { 1427d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String notificationTitleFormat = context.getResources().getString( 1428d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy R.string.single_new_message_notification_title); 1429d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1430d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Localizers may change the order of the parameters, look at how the format 1431d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // string is structured. 1432d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean isSubjectLast = notificationTitleFormat.indexOf("%2$s") > 1433d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationTitleFormat.indexOf("%1$s"); 1434d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final String titleString = String.format(notificationTitleFormat, sender, subject); 1435d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1436d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Format the string so the subject is using the secondaryText style 1437d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final SpannableString titleSpannable = new SpannableString(titleString); 1438d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1439d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Find the offset of the subject. 1440d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int subjectOffset = 1441d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy isSubjectLast ? titleString.lastIndexOf(subject) : titleString.indexOf(subject); 1442d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final TextAppearanceSpan notificationSubjectSpan = 1443d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new TextAppearanceSpan(context, R.style.NotificationSecondaryText); 1444d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy titleSpannable.setSpan(notificationSubjectSpan, 1445d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy subjectOffset, subjectOffset + subject.length(), 0); 1446d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return titleSpannable; 1447d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1448d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1449d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1450d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 14517f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy * Clears the notifications for the specified account/folder. 1452d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 14537f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy public static void clearFolderNotification(Context context, Account account, Folder folder, 14547f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy final boolean markSeen) { 145526a20758413f0f8629ea71a0943304e3ae740165Tony Mantler LogUtils.v(LOG_TAG, "Clearing all notifications for %s/%s", account.getEmailAddress(), 145626a20758413f0f8629ea71a0943304e3ae740165Tony Mantler folder.name); 1457d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 1458d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final NotificationKey key = new NotificationKey(account, folder); 1459d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.remove(key); 1460d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy notificationMap.saveNotificationMap(context); 1461d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 146215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final NotificationManagerCompat notificationManager = 146315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationManagerCompat.from(context); 1464b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler notificationManager.cancel(getNotificationId(account.getAccountManagerAccount(), folder)); 14657f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 146615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau cancelChildNotifications(key, notificationManager); 146715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 14687f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy if (markSeen) { 14697f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy markSeen(context, folder); 14707f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy } 14717f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy } 14727f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 14737f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy /** 14747f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy * Clears all notifications for the specified account. 14757f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy */ 1476b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler public static void clearAccountNotifications(final Context context, 1477b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final android.accounts.Account account) { 14782f97af9484bb3a226856c441943c5b0f32910155Scott Kennedy LogUtils.v(LOG_TAG, "Clearing all notifications for %s", account); 14797f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy final NotificationMap notificationMap = getNotificationMap(context); 14807f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 14817f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy // Find all NotificationKeys for this account 14827f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy final ImmutableList.Builder<NotificationKey> keyBuilder = ImmutableList.builder(); 14837f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 14847f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy for (final NotificationKey key : notificationMap.keySet()) { 1485b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler if (account.equals(key.account.getAccountManagerAccount())) { 14867f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy keyBuilder.add(key); 14877f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy } 14887f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy } 14897f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 14907f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy final List<NotificationKey> notificationKeys = keyBuilder.build(); 14917f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 149215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau final NotificationManagerCompat notificationManager = 149315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationManagerCompat.from(context); 14947f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 14957f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy for (final NotificationKey notificationKey : notificationKeys) { 14967f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy final Folder folder = notificationKey.folder; 14977f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy notificationManager.cancel(getNotificationId(account, folder)); 14987f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy notificationMap.remove(notificationKey); 149915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 150015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau cancelChildNotifications(notificationKey, notificationManager); 15017f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy } 15027f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy 15037f8aed6bea276d6069abbfbdb25263a9ec92a684Scott Kennedy notificationMap.saveNotificationMap(context); 1504d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1505d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 150615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau private static void cancelChildNotifications(NotificationKey key, 150715490239c82cdb4765e87e926c8c1945adedd1edAlan Lau NotificationManagerCompat nm) { 150815490239c82cdb4765e87e926c8c1945adedd1edAlan Lau Set<Integer> childNotifications = sChildNotificationsMap.get(key); 150915490239c82cdb4765e87e926c8c1945adedd1edAlan Lau if (childNotifications != null) { 151015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau for (Integer childNotification : childNotifications) { 151115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau nm.cancel(childNotification); 151215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 151315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau sChildNotificationsMap.remove(key); 151415490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 151515490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 151615490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 1517d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static ArrayList<Long> findContacts(Context context, Collection<String> addresses) { 1518d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy ArrayList<String> whereArgs = new ArrayList<String>(); 1519d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy StringBuilder whereBuilder = new StringBuilder(); 1520d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String[] questionMarks = new String[addresses.size()]; 1521d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1522d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy whereArgs.addAll(addresses); 1523d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Arrays.fill(questionMarks, "?"); 1524d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy whereBuilder.append(Email.DATA1 + " IN ("). 1525d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy append(TextUtils.join(",", questionMarks)). 1526d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy append(")"); 1527d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1528d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy ContentResolver resolver = context.getContentResolver(); 1529d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy Cursor c = resolver.query(Email.CONTENT_URI, 1530d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new String[]{Email.CONTACT_ID}, whereBuilder.toString(), 1531d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy whereArgs.toArray(new String[0]), null); 1532d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1533d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy ArrayList<Long> contactIds = new ArrayList<Long>(); 1534d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (c == null) { 1535d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return contactIds; 1536d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1537d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy try { 1538d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy while (c.moveToNext()) { 1539d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy contactIds.add(c.getLong(0)); 1540d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1541d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } finally { 1542d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy c.close(); 1543d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1544d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return contactIds; 1545d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1546d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1547644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau private static ContactIconInfo getContactIcon(final Context context, final String displayName, 1548c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final String senderAddress, final Folder folder) { 1549d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (senderAddress == null) { 1550d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return null; 1551d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1552c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 1553644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau ContactIconInfo contactIconInfo = new ContactIconInfo(); 1554c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 1555644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau final List<Long> contactIds = findContacts(context, Arrays.asList(senderAddress)); 1556c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 1557c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy // Get the ideal size for this icon. 1558c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final Resources res = context.getResources(); 1559c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final int idealIconHeight = 1560c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height); 1561c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final int idealIconWidth = 1562c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width); 1563d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1564d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (contactIds != null) { 15654046d0695e102368201bb2f3a268a4f85c8bc4dbScott Kennedy for (final long id : contactIds) { 1566644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau final Uri contactUri = ContentUris.withAppendedId( 1567644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau ContactsContract.Contacts.CONTENT_URI, id); 1568644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream( 1569644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau context.getContentResolver(), contactUri, true /*preferHighres*/); 1570d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1571644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (inputStream != null) { 1572d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy try { 1573644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau Bitmap source = BitmapFactory.decodeStream(inputStream); 1574644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (source != null) { 1575644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau // We should scale this image to fit the intended size 1576644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau contactIconInfo.icon = Bitmap.createScaledBitmap(source, idealIconWidth, 1577644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau idealIconHeight, true); 1578644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1579644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau int idealWearableBgWidth = res.getDimensionPixelSize( 1580644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau R.dimen.wearable_background_width); 1581644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau int idealWearableBgHeight = res.getDimensionPixelSize( 1582644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau R.dimen.wearable_background_height); 1583644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1584644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau contactIconInfo.wearableBg = Bitmap.createScaledBitmap(source, 1585644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau idealWearableBgWidth, idealWearableBgHeight, true); 1586644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 1587644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1588644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (contactIconInfo.icon != null) { 1589644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau break; 1590d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1591d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } finally { 1592644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau Closeables.closeQuietly(inputStream); 1593d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1594d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1595d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1596d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1597c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 1598644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (contactIconInfo.icon == null) { 1599c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy // Make a colorful tile! 1600c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy final Dimensions dimensions = new Dimensions(idealIconWidth, idealIconHeight, 1601c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy Dimensions.SCALE_ONE); 1602c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 1603644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau contactIconInfo.icon = new LetterTileProvider(context).getLetterTile(dimensions, 1604c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy displayName, senderAddress); 1605c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy } 1606c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy 1607644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (contactIconInfo.icon == null) { 1608c94c80c8b8c92c019f19f2e315dea1ff03ad573fScott Kennedy // Icon should be the default mail icon. 1609644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau contactIconInfo.icon = getDefaultNotificationIcon(context, folder, 1610644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau false /* single new message */); 1611d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1612644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1613644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau if (contactIconInfo.wearableBg == null) { 1614644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau contactIconInfo.wearableBg = getDefaultWearableBg(context); 1615644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 1616644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1617644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau return contactIconInfo; 1618d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1619d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1620d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static String getMessageBodyWithoutElidedText(final Message message) { 16215e7e88b9f5ac1f239753e13ffb3ee4b249722c9aScott Kennedy return getMessageBodyWithoutElidedText(message.getBodyAsHtml()); 1622d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1623d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1624d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static String getMessageBodyWithoutElidedText(String html) { 1625d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (TextUtils.isEmpty(html)) { 1626d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return ""; 1627d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1628d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Get the html "tree" for this message body 1629d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final HtmlTree htmlTree = com.android.mail.utils.Utils.getHtmlTree(html); 1630d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy htmlTree.setPlainTextConverterFactory(MESSAGE_CONVERTER_FACTORY); 1631d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1632d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return htmlTree.getPlainText(); 1633d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1634d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1635d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static void markSeen(final Context context, final Folder folder) { 1636259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy final Uri uri = folder.folderUri.fullUri; 1637d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1638d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final ContentValues values = new ContentValues(1); 1639d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy values.put(UIProvider.ConversationColumns.SEEN, 1); 1640d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1641d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy context.getContentResolver().update(uri, values, null, null); 1642d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1643d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1644d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1645d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Returns a displayable string representing 1646d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * the message sender. It has a preference toward showing the name, 1647d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * but will fall back to the address if that is all that is available. 1648d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1649d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static String getDisplayableSender(String sender) { 1650d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final EmailAddress address = EmailAddress.getEmailAddress(sender); 1651d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1652d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String displayableSender = address.getName(); 1653390cab939304705cc8adeb8e4810abf904fec22fScott Kennedy 1654390cab939304705cc8adeb8e4810abf904fec22fScott Kennedy if (!TextUtils.isEmpty(displayableSender)) { 1655821e578a71c7015646522e729600618f0ec16fc0Tony Mantler return Address.decodeAddressPersonal(displayableSender); 1656d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1657390cab939304705cc8adeb8e4810abf904fec22fScott Kennedy 1658390cab939304705cc8adeb8e4810abf904fec22fScott Kennedy // If that fails, default to the sender address. 1659390cab939304705cc8adeb8e4810abf904fec22fScott Kennedy displayableSender = address.getAddress(); 1660390cab939304705cc8adeb8e4810abf904fec22fScott Kennedy 1661d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If we were unable to tokenize a name or address, 1662d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // just use whatever was in the sender. 1663d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (TextUtils.isEmpty(displayableSender)) { 1664d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy displayableSender = sender; 1665d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1666d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return displayableSender; 1667d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1668d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1669d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1670d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Returns only the address portion of a message sender. 1671d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1672d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static String getSenderAddress(String sender) { 1673d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final EmailAddress address = EmailAddress.getEmailAddress(sender); 1674d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1675d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy String tokenizedAddress = address.getAddress(); 1676d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1677d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If we were unable to tokenize a name or address, 1678d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // just use whatever was in the sender. 1679d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (TextUtils.isEmpty(tokenizedAddress)) { 1680d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy tokenizedAddress = sender; 1681d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1682d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return tokenizedAddress; 1683d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1684d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1685b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler public static int getNotificationId(final android.accounts.Account account, 1686b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final Folder folder) { 1687d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return 1 ^ account.hashCode() ^ folder.hashCode(); 1688d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1689d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 169015490239c82cdb4765e87e926c8c1945adedd1edAlan Lau private static int getNotificationId(int summaryNotificationId, int childHashCode) { 169115490239c82cdb4765e87e926c8c1945adedd1edAlan Lau return summaryNotificationId ^ childHashCode; 169215490239c82cdb4765e87e926c8c1945adedd1edAlan Lau } 169315490239c82cdb4765e87e926c8c1945adedd1edAlan Lau 1694d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static class NotificationKey { 1695d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public final Account account; 1696d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public final Folder folder; 1697d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1698d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public NotificationKey(Account account, Folder folder) { 1699d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy this.account = account; 1700d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy this.folder = folder; 1701d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1702d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1703d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy @Override 1704d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public boolean equals(Object other) { 1705d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!(other instanceof NotificationKey)) { 1706d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return false; 1707d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1708d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy NotificationKey key = (NotificationKey) other; 1709b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler return account.getAccountManagerAccount().equals(key.account.getAccountManagerAccount()) 1710b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler && folder.equals(key.folder); 1711d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1712d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1713d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy @Override 1714d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public String toString() { 171526a20758413f0f8629ea71a0943304e3ae740165Tony Mantler return account.getDisplayName() + " " + folder.name; 1716d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1717d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1718d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy @Override 1719d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public int hashCode() { 1720b90a813b107d8f43236e2d3fe12f299d94c82ce8Tony Mantler final int accountHashCode = account.getAccountManagerAccount().hashCode(); 1721d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final int folderHashCode = folder.hashCode(); 1722d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return accountHashCode ^ folderHashCode; 1723d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1724d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1725d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1726d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1727d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * Contains the logic for converting the contents of one HtmlTree into 1728d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * plaintext. 1729d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1730d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static class MailMessagePlainTextConverter extends HtmlTree.DefaultPlainTextConverter { 1731d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Strings for parsing html message bodies 1732d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final String ELIDED_TEXT_ELEMENT_NAME = "div"; 1733d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final String ELIDED_TEXT_ELEMENT_ATTRIBUTE_NAME = "class"; 1734d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final String ELIDED_TEXT_ELEMENT_ATTRIBUTE_CLASS_VALUE = "elided-text"; 1735d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1736d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final HTML.Attribute ELIDED_TEXT_ATTRIBUTE = 1737d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy new HTML.Attribute(ELIDED_TEXT_ELEMENT_ATTRIBUTE_NAME, HTML.Attribute.NO_TYPE); 1738d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1739d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private static final HtmlDocument.Node ELIDED_TEXT_REPLACEMENT_NODE = 1740d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy HtmlDocument.createSelfTerminatingTag(HTML4.BR_ELEMENT, null, null, null); 1741d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1742d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy private int mEndNodeElidedTextBlock = -1; 1743d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1744d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy @Override 1745d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public void addNode(HtmlDocument.Node n, int nodeNum, int endNum) { 1746d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If we are in the middle of an elided text block, don't add this node 1747d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (nodeNum < mEndNodeElidedTextBlock) { 1748d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return; 1749d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } else if (nodeNum == mEndNodeElidedTextBlock) { 1750d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy super.addNode(ELIDED_TEXT_REPLACEMENT_NODE, nodeNum, endNum); 1751d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return; 1752d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1753d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1754d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If this tag starts another elided text block, we want to remember the end 1755d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (n instanceof HtmlDocument.Tag) { 1756d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy boolean foundElidedTextTag = false; 1757d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final HtmlDocument.Tag htmlTag = (HtmlDocument.Tag)n; 1758d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final HTML.Element htmlElement = htmlTag.getElement(); 1759d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (ELIDED_TEXT_ELEMENT_NAME.equals(htmlElement.getName())) { 1760d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Make sure that the class is what is expected 1761d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final List<HtmlDocument.TagAttribute> attributes = 1762d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy htmlTag.getAttributes(ELIDED_TEXT_ATTRIBUTE); 1763d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy for (HtmlDocument.TagAttribute attribute : attributes) { 1764d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (ELIDED_TEXT_ELEMENT_ATTRIBUTE_CLASS_VALUE.equals( 1765d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy attribute.getValue())) { 1766d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // Found an "elided-text" div. Remember information about this tag 1767d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy mEndNodeElidedTextBlock = endNum; 1768d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy foundElidedTextTag = true; 1769d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy break; 1770d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1771d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1772d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1773d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1774d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (foundElidedTextTag) { 1775d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy return; 1776d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1777d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1778d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1779c56b233013cf107c702ef9f61305282670ad804aScott Kennedy super.addNode(n, nodeNum, endNum); 1780d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1781d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1782d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1783d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy /** 1784d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * During account setup in Email, we may not have an inbox yet, so the notification setting had 1785d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * to be stored in {@link AccountPreferences}. If it is still there, we need to move it to the 1786d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy * {@link FolderPreferences} now. 1787d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy */ 1788d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy public static void moveNotificationSetting(final AccountPreferences accountPreferences, 1789d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final FolderPreferences folderPreferences) { 1790d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (accountPreferences.isDefaultInboxNotificationsEnabledSet()) { 1791d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy // If this setting has been changed some other way, don't overwrite it 1792d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy if (!folderPreferences.isNotificationsEnabledSet()) { 1793d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy final boolean notificationsEnabled = 1794d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy accountPreferences.getDefaultInboxNotificationsEnabled(); 1795d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1796d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy folderPreferences.setNotificationsEnabled(notificationsEnabled); 1797d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1798d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy 1799d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy accountPreferences.clearDefaultInboxNotificationsEnabled(); 1800d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1801d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy } 1802c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau 1803c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau private static class NotificationBuilders { 1804c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau public final NotificationCompat.Builder notifBuilder; 1805439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau public final NotificationCompat.WearableExtender wearableNotifBuilder; 1806c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau 1807c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau private NotificationBuilders(NotificationCompat.Builder notifBuilder, 1808439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau NotificationCompat.WearableExtender wearableNotifBuilder) { 1809c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau this.notifBuilder = notifBuilder; 1810c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau this.wearableNotifBuilder = wearableNotifBuilder; 1811c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau } 1812c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau 1813c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau public static NotificationBuilders of(NotificationCompat.Builder notifBuilder, 1814439aa5d7d9226de4682c3697c68c0703b8847518Alan Lau NotificationCompat.WearableExtender wearableNotifBuilder) { 1815c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau return new NotificationBuilders(notifBuilder, wearableNotifBuilder); 1816c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau } 1817c4288f2504ddf203bf558c7ae0143f36f842535bAlan Lau } 1818644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1819644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau private static class ConfigResult { 1820644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau public String notificationTicker; 1821644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau public ContactIconInfo contactIconInfo; 1822644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 1823644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau 1824644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau private static class ContactIconInfo { 1825644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau public Bitmap icon; 1826644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau public Bitmap wearableBg; 1827644ec1e2e65bedbbfff4dbb396d3e4f95d244c62Alan Lau } 1828d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy} 1829