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