Utils.java revision cc399081e9f39d6c3238791b7116e36de66e4ac7
17b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira/**
27b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * Copyright (c) 2011, Google Inc.
37b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira *
47b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
57b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * you may not use this file except in compliance with the License.
67b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * You may obtain a copy of the License at
77b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira *
87b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira *     http://www.apache.org/licenses/LICENSE-2.0
97b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira *
107b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * Unless required by applicable law or agreed to in writing, software
117b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
127b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * See the License for the specific language governing permissions and
147b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira * limitations under the License.
157b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira */
162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangpackage com.android.mail.utils;
187b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
1968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereiraimport android.app.SearchManager;
206f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.Context;
218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent;
2294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.content.pm.PackageManager.NameNotFoundException;
236f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.res.Resources;
24ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.database.Cursor;
256f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.graphics.Typeface;
268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.net.Uri;
279f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrookimport android.os.AsyncTask;
28e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrookimport android.os.Build;
29ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.os.Bundle;
3094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.provider.Browser;
313e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Html;
323e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spannable;
336f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableString;
346f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableStringBuilder;
353e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spanned;
363e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils;
373e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils.SimpleStringSplitter;
386f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.CharacterStyle;
396f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.ForegroundColorSpan;
406f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.StyleSpan;
41863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.Menu;
42863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.MenuItem;
43326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View;
44443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwalimport android.view.ViewParent;
45326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View.MeasureSpec;
46f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.view.ViewGroup;
478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebSettings;
488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebView;
498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
5030e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
51a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport com.android.mail.browse.ConversationCursor;
528a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account;
539ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereiraimport com.android.mail.providers.Conversation;
548a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder;
55ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.providers.UIProvider;
56e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrookimport com.android.mail.providers.UIProvider.EditSettingsExtras;
5795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlDocument;
5895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlParser;
5995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlTree;
6095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlTreeBuilder;
6110ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blankimport com.google.common.collect.Maps;
623e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
6392551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport org.json.JSONObject;
6492551d057965689f1952faeb06763f0762bc717fMindy Pereira
6594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport java.util.Locale;
663e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport java.util.Map;
677b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
686f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereirapublic class Utils {
696f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    /**
706f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     * longest extension we recognize is 4 characters (e.g. "html", "docx")
716f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     */
726f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    private static final int FILE_EXTENSION_MAX_CHARS = 4;
733e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    private static final Map<Integer, Integer> sPriorityToLength = Maps.newHashMap();
743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_ELIDED = "e";
753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_MESSAGES = "n";
763e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_DRAFTS = "d";
773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_LITERAL = "l";
783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SENDING = "s";
793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SEND_FAILED = "f";
803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final Character SENDER_LIST_SEPARATOR = '\n';
813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final SimpleStringSplitter sSenderListSplitter = new SimpleStringSplitter(
823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            SENDER_LIST_SEPARATOR);
833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static String[] sSenderFragments = new String[8];
848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
856349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    public static final String EXTRA_ACCOUNT = "account";
86e144980ef9c8828401922373ef0dc203281bff6ePaul Westbrook    public static final String EXTRA_ACCOUNT_URI = "accountUri";
8791d0b86db3287f1702913177d347dd42b7d13764Marc Blank    public static final String EXTRA_FOLDER_URI = "folderUri";
887418e4b9942f291b8de8bc7b1b72a7ef7130a8b6Mindy Pereira    public static final String EXTRA_COMPOSE_URI = "composeUri";
89963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_CONVERSATION = "conversationUri";
90963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_FOLDER = "folder";
917517e3b61b898a57f19be0671f70d58a82224643Andy Huang
927517e3b61b898a57f19be0671f70d58a82224643Andy Huang    /** Extra tag for debugging the blank fragment problem. */
937517e3b61b898a57f19be0671f70d58a82224643Andy Huang    public static final String VIEW_DEBUGGING_TAG = "MailBlankFragment";
947517e3b61b898a57f19be0671f70d58a82224643Andy Huang
958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /*
968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Notifies that changes happened. Certain UI components, e.g., widgets, can
978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * register for this {@link Intent} and update accordingly. However, this
988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * can be very broad and is NOT the preferred way of getting notification.
998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
1008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    // TODO: UI Provider has this notification URI?
10194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static final String ACTION_NOTIFY_DATASET_CHANGED =
10294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
10394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
10494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /** Parameter keys for context-aware help. */
10594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_HELP_LINK_PARAMETER_NAME = "p";
10694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
10794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_LINK_APP_VERSION = "version";
10894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static String sVersionCode = null;
10994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
110b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
1116349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
112e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    public static boolean isRunningJellybeanOrLater() {
113e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
114e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    }
115e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook
1162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Sets WebView in a restricted mode suitable for email use.
1182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param webView The WebView to restrict
1202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void restrictWebView(WebView webView) {
1228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        WebSettings webSettings = webView.getSettings();
1238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSavePassword(false);
1248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSaveFormData(false);
1258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setJavaScriptEnabled(true);
1268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSupportZoom(false);
1272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Format a plural string.
1312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param resource The identity of the resource, which must be a R.plurals
1332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param count The number of items.
1342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String formatPlural(Context context, int resource, int count) {
1362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence formatString = context.getResources().getQuantityText(resource, count);
1372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return String.format(formatString.toString(), count);
1382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return an ellipsized String that's at most maxCharacters long. If the
1422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         text passed is longer, it will be abbreviated. If it contains a
1432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix, the ellipses will be inserted in the middle and the
1442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix will be preserved.
1452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ellipsize(String text, int maxCharacters) {
1472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int length = text.length();
1482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (length < maxCharacters)
1492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return text;
1502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int realMax = Math.min(maxCharacters, length);
1522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Preserve the suffix if any
1532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int index = text.lastIndexOf(".");
1542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String extension = "\u2026"; // "...";
1552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (index >= 0) {
1562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            // Limit the suffix to dot + four characters
1572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (length - index <= FILE_EXTENSION_MAX_CHARS + 1) {
1582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                extension = extension + text.substring(index + 1);
1592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
1602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
1612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        realMax -= extension.length();
1622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (realMax < 0)
1632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            realMax = 0;
1642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return text.substring(0, realMax) + extension;
1652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1684ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * Ensures that the given string starts and ends with the double quote
1694ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * character. The string is not modified in any way except to add the double
1704ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * quote character to start and end if it's not already there. sample ->
1714ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * "sample" "sample" -> "sample" ""sample"" -> "sample"
1724ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * "sample"" -> "sample" sa"mp"le -> "sa"mp"le" "sa"mp"le" -> "sa"mp"le"
1734ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * (empty string) -> "" " -> ""
1744ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     */
1752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ensureQuotedString(String s) {
1762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (s == null) {
1772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return null;
1782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
1792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (!s.matches("^\".*\"$")) {
1802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return "\"" + s + "\"";
1812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
1822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return s;
1832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
1842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    // TODO: Move this to the UI Provider.
1872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sUnreadStyleSpan = null;
1882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sReadStyleSpan;
1892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sDraftsStyleSpan;
1902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sMeString;
1912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sDraftSingularString;
1922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sDraftPluralString;
1932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sSendingString;
1942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sSendFailedString;
1952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static int sMaxUnreadCount = -1;
1972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static String sUnreadText;
19855137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    private static int sDefaultFolderBackgroundColor = -1;
1992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void getStyledSenderSnippet(Context context, String senderInstructions,
2012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder,
2022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            int maxChars, boolean forceAllUnread, boolean forceAllRead, boolean allowDraft) {
2032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        Resources res = context.getResources();
2042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sUnreadStyleSpan == null) {
2052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sUnreadStyleSpan = new StyleSpan(Typeface.BOLD);
2062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sReadStyleSpan = new StyleSpan(Typeface.NORMAL);
2072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftsStyleSpan = new ForegroundColorSpan(res.getColor(R.color.drafts));
2082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sMeString = context.getText(R.string.me);
2102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftSingularString = res.getQuantityText(R.plurals.draft, 1);
2112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftPluralString = res.getQuantityText(R.plurals.draft, 2);
2122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableString sendingString = new SpannableString(context.getText(R.string.sending));
2132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sendingString.setSpan(CharacterStyle.wrap(sDraftsStyleSpan), 0, sendingString.length(),
2142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    0);
2152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sSendingString = sendingString;
2162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sSendFailedString = context.getText(R.string.send_failed);
2172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        getSenderSnippet(senderInstructions, senderBuilder, statusBuilder, maxChars,
2202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadStyleSpan, sReadStyleSpan, sDraftsStyleSpan, sMeString,
2212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sDraftSingularString, sDraftPluralString, sSendingString, sSendFailedString,
2222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                forceAllUnread, forceAllRead, allowDraft);
2232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
2263e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Uses sender instructions to build a formatted string.
2273e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2283e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Sender list instructions contain compact information about the sender
2293e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * list. Most work that can be done without knowing how much room will be
2303e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * availble for the sender list is done when creating the instructions.
2313e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2323e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * The instructions string consists of tokens separated by
2333e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * SENDER_LIST_SEPARATOR. Here are the tokens, one per line:
2343e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <ul>
2353e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>n</tt></li>
2363e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the number of non-draft messages in the conversation</li>
2373e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>d</tt</li>
2383e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the number of drafts in the conversation</li>
2393e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>l</tt></li>
2403e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>literal html to be included in the output</em></li>
2413e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>s</tt> indicates that the message is sending (in the outbox
2423e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * without errors)</li>
2433e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>f</tt> indicates that the message failed to send (in the outbox
2443e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * with errors)</li>
2453e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>for each message</em>
2463e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <ul>
2473e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, 0 for read, 1 for unread</li>
2483e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the priority of the message. Zero is the most important
2493e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </li>
2503e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>text</em>, the sender text or blank for messages from 'me'</li>
2513e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </ul>
2523e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </li>
2533e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>e</tt> to indicate that one or more messages have been elided</li>
2543e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2553e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * The instructions indicate how many messages and drafts are in the
2563e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * conversation and then describe the most important messages in order,
2573e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * indicating the priority of each message and whether the message is
2583e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * unread.
259f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
2603e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param instructions instructions as described above
2613e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param senderBuilder the SpannableStringBuilder to append to for sender
2623e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            information
2633e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param statusBuilder the SpannableStringBuilder to append to for status
2643e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param maxChars the number of characters available to display the text
2653e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param unreadStyle the CharacterStyle for unread messages, or null
2663e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftsStyle the CharacterStyle for draft messages, or null
2673e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param sendingString the string to use when there are messages scheduled
2683e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            to be sent
2693e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param sendFailedString the string to use when there are messages that
2703e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            mailed to send
2713e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param meString the string to use for messages sent by this user
2723e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftString the string to use for "Draft"
2733e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftPluralString the string to use for "Drafts"
2743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     */
2752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static synchronized void getSenderSnippet(String instructions,
2762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder,
2772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            int maxChars, CharacterStyle unreadStyle, CharacterStyle readStyle,
2782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharacterStyle draftsStyle, CharSequence meString, CharSequence draftString,
2792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharSequence draftPluralString, CharSequence sendingString,
2802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharSequence sendFailedString, boolean forceAllUnread, boolean forceAllRead,
2812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            boolean allowDraft) {
2822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        assert !(forceAllUnread && forceAllRead);
2832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        boolean unreadStatusIsForced = forceAllUnread || forceAllRead;
2842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        boolean forcedUnreadStatus = forceAllUnread;
2852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Measure each fragment. It's ok to iterate over the entire set of
2872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // fragments because it is
2882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // never a long list, even if there are many senders.
2892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        final Map<Integer, Integer> priorityToLength = sPriorityToLength;
2902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        priorityToLength.clear();
2912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int maxFoundPriority = Integer.MIN_VALUE;
2932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numMessages = 0;
2942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numDrafts = 0;
2952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence draftsFragment = "";
2962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence sendingFragment = "";
2972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence sendFailedFragment = "";
2982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        sSenderListSplitter.setString(instructions);
3002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numFragments = 0;
3012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String[] fragments = sSenderFragments;
3022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int currentSize = fragments.length;
3032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        while (sSenderListSplitter.hasNext()) {
3042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fragments[numFragments++] = sSenderListSplitter.next();
3052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (numFragments == currentSize) {
3062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sSenderFragments = new String[2 * currentSize];
3072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                System.arraycopy(fragments, 0, sSenderFragments, 0, currentSize);
3082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                currentSize *= 2;
3092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fragments = sSenderFragments;
3102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        for (int i = 0; i < numFragments;) {
3142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            String fragment0 = fragments[i++];
3152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if ("".equals(fragment0)) {
3162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // This should be the final fragment.
3172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) {
3182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // ignore
3192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) {
3202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numMessages = Integer.valueOf(fragments[i++]);
3212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) {
3222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String numDraftsString = fragments[i++];
3232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numDrafts = Integer.parseInt(numDraftsString);
3242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                draftsFragment = numDrafts == 1 ? draftString : draftPluralString + " ("
3252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        + numDraftsString + ")";
3262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_LITERAL.equals(fragment0)) {
3272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                senderBuilder.append(Html.fromHtml(fragments[i++]));
3282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                return;
3292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) {
3302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sendingFragment = sendingString;
3312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) {
3322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sendFailedFragment = sendFailedString;
3332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else {
3342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String priorityString = fragments[i++];
3352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                CharSequence nameString = fragments[i++];
3362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (nameString.length() == 0)
3372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = meString;
3382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                int priority = Integer.parseInt(priorityString);
3392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                priorityToLength.put(priority, nameString.length());
3402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                maxFoundPriority = Math.max(maxFoundPriority, priority);
3412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String numMessagesFragment = (numMessages != 0) ? " \u00A0"
3442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                + Integer.toString(numMessages + numDrafts) : "";
3452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Don't allocate fixedFragment unless we need it
3472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        SpannableStringBuilder fixedFragment = null;
3482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int fixedFragmentLength = 0;
3492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (draftsFragment.length() != 0 && allowDraft) {
3502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
3512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
3522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(draftsFragment);
3542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (draftsStyle != null) {
3552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.setSpan(CharacterStyle.wrap(draftsStyle), 0, fixedFragment.length(),
3562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
3572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sendingFragment.length() != 0) {
3602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
3612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
3622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment.length() != 0)
3642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.append(", ");
3652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(sendingFragment);
3662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sendFailedFragment.length() != 0) {
3682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
3692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
3702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment.length() != 0)
3722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.append(", ");
3732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(sendFailedFragment);
3742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (fixedFragment != null) {
3772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragmentLength = fixedFragment.length();
3782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        maxChars -= fixedFragmentLength;
3802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int maxPriorityToInclude = -1; // inclusive
3822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numCharsUsed = numMessagesFragment.length();
3832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numSendersUsed = 0;
3842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        while (maxPriorityToInclude < maxFoundPriority) {
3852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (priorityToLength.containsKey(maxPriorityToInclude + 1)) {
3862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                int length = numCharsUsed + priorityToLength.get(maxPriorityToInclude + 1);
3872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (numCharsUsed > 0)
3882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    length += 2;
3892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // We must show at least two senders if they exist. If we don't
3902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // have space for both
3912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // then we will truncate names.
3922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (length > maxChars && numSendersUsed >= 2) {
3932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    break;
3942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
3952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numCharsUsed = length;
3962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numSendersUsed++;
3972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            maxPriorityToInclude++;
3992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numCharsToRemovePerWord = 0;
4022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (numCharsUsed > maxChars) {
4032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            numCharsToRemovePerWord = (numCharsUsed - maxChars) / numSendersUsed;
4042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String lastFragment = null;
4072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharacterStyle lastStyle = null;
4082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        for (int i = 0; i < numFragments;) {
4092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            String fragment0 = fragments[i++];
4102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if ("".equals(fragment0)) {
4112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // This should be the final fragment.
4122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) {
4132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (lastFragment != null) {
4142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    senderBuilder.append(" ");
4162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    addStyledFragment(senderBuilder, "..", lastStyle, true);
4172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    senderBuilder.append(" ");
4182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                lastFragment = null;
4202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) {
4212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                i++;
4222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) {
4232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                i++;
4242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) {
4252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) {
4262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else {
4272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final String unreadString = fragment0;
4282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final String priorityString = fragments[i++];
4292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String nameString = fragments[i++];
4302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (nameString.length() == 0) {
4312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = meString.toString();
4322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                } else {
4332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = Html.fromHtml(nameString).toString();
4342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (numCharsToRemovePerWord != 0) {
4362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = nameString.substring(0,
4372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                            Math.max(nameString.length() - numCharsToRemovePerWord, 0));
4382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final boolean unread = unreadStatusIsForced ? forcedUnreadStatus : Integer
4402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        .parseInt(unreadString) != 0;
4412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final int priority = Integer.parseInt(priorityString);
4422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (priority <= maxPriorityToInclude) {
4432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    if (lastFragment != null && !lastFragment.equals(nameString)) {
4442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, lastFragment.concat(","), lastStyle,
4452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                                false);
4462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    }
4482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastFragment = nameString;
4492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastStyle = unread ? unreadStyle : readStyle;
4502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                } else {
4512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    if (lastFragment != null) {
4522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // Adjacent spans can cause the TextView in Gmail widget
4542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // confused and leads to weird behavior on scrolling.
4552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // Our workaround here is to separate the spans by
4562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // spaces.
4572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, "..", lastStyle, true);
4592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    }
4612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastFragment = null;
4622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
4642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (lastFragment != null) {
4662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        senderBuilder.append(numMessagesFragment);
4692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (fixedFragmentLength != 0) {
4702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            statusBuilder.append(fixedFragment);
4712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
4736f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira
4743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    /**
4753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Adds a fragment with given style to a string builder.
476f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
4773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param builder the current string builder
4783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param fragment the fragment to be added
4793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param style the style of the fragment
4803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param withSpaces whether to add the whole fragment or to divide it into
4813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            smaller ones
4823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     */
4833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    private static void addStyledFragment(SpannableStringBuilder builder, String fragment,
4843e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            CharacterStyle style, boolean withSpaces) {
4853e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        if (withSpaces) {
4863e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            int pos = builder.length();
4873e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            builder.append(fragment);
4883e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            builder.setSpan(CharacterStyle.wrap(style), pos, builder.length(),
4893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
4903e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        } else {
4913e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            int start = 0;
4923e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            while (true) {
4933e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                int pos = fragment.substring(start).indexOf(' ');
4943e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                if (pos == -1) {
4953e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    addStyledFragment(builder, fragment.substring(start), style, true);
4963e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    break;
4973e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                } else {
4983e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    pos += start;
4993e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    if (start < pos) {
5003e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        addStyledFragment(builder, fragment.substring(start, pos), style, true);
5013e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        builder.append(' ');
5023e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    }
5033e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    start = pos + 1;
5043e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    if (start >= fragment.length()) {
5053e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        break;
5063e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    }
5073e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                }
5083e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            }
5093e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        }
5103e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    }
5113e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
5122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
5132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Returns a boolean indicating whether the table UI should be shown.
5142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static boolean useTabletUI(Context context) {
5162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return context.getResources().getInteger(R.integer.use_tablet_ui) != 0;
5172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
5192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
52095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns displayable text from the provided HTML string.
52195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @param htmlText HTML string
52295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @return Plain text string representation of the specified Html string
52395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
52495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static String convertHtmlToPlainText(String htmlText) {
525cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder()).getPlainText();
526cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
527cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
528cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    public static String convertHtmlToPlainText(String htmlText, HtmlParser parser,
529cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
530cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, parser, builder).getPlainText();
53195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
53295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
53395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
53495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns a {@link HtmlTree} representation of the specified HTML string.
53595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
53695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static HtmlTree getHtmlTree(String htmlText) {
537cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder());
538cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
539cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
540cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    /**
541cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     * Returns a {@link HtmlTree} representation of the specified HTML string.
542cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     */
543cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    public static HtmlTree getHtmlTree(String htmlText, HtmlParser parser,
544cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
54595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlDocument doc = parser.parse(htmlText);
54695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        doc.accept(builder);
54795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
54895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        return builder.getTree();
54995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
55095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
55195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
5522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Perform a simulated measure pass on the given child view, assuming the
5532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * child has a ViewGroup parent and that it should be laid out within that
5542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * parent with a matching width but variable height. Code largely lifted
5552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * from AnimatedAdapter.measureChildHeight().
556f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
5572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param child a child view that has already been placed within its parent
5582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *            ViewGroup
5592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param parent the parent ViewGroup of child
5602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return measured height of the child in px
5612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static int measureViewHeight(View child, ViewGroup parent) {
5632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int parentWSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY);
5642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int wSpec = ViewGroup.getChildMeasureSpec(parentWSpec,
5652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                parent.getPaddingLeft() + parent.getPaddingRight(),
5662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                ViewGroup.LayoutParams.MATCH_PARENT);
5672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int hSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
5682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        child.measure(wSpec, hSpec);
5692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return child.getMeasuredHeight();
5702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
571326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
57246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
57346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Encode the string in HTML.
57446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
57546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param removeEmptyDoubleQuotes If true, also remove any occurrence of ""
57646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *            found in the string
57746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
5782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static Object cleanUpString(String string, boolean removeEmptyDoubleQuotes) {
5792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return !TextUtils.isEmpty(string) ? TextUtils.htmlEncode(removeEmptyDoubleQuotes ? string
5802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                .replace("\"\"", "") : string) : "";
5812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
5832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
5842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Get the correct display string for the unread count of a folder.
5852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String getUnreadCountString(Context context, int unreadCount) {
5872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String unreadCountString;
5882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        Resources resources = context.getResources();
5892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sMaxUnreadCount == -1) {
5902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount);
5912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
5922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (unreadCount > sMaxUnreadCount) {
5932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (sUnreadText == null) {
5942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadText = resources.getString(R.string.widget_large_unread_count);
5952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
5962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = String.format(sUnreadText, sMaxUnreadCount);
5972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else if (unreadCount <= 0) {
5982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = "";
5992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
6002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = String.valueOf(unreadCount);
6012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
6022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return unreadCountString;
6032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
60428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira
60528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    /**
60628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     * Get text matching the last sync status.
60728beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     */
60828beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    public static CharSequence getSyncStatusText(Context context, int status) {
60917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final String[] errors = context.getResources().getStringArray(R.array.sync_status);
61028beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        if (status >= errors.length) {
61128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira            return "";
61228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        }
61328beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        return errors[status];
61428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    }
6158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
6168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
6179ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to show a conversation.
6189ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @param conversation Conversation to open.
619161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param folder
620161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param account
6219ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @return
6229ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     */
623161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira    public static Intent createViewConversationIntent(Conversation conversation, Folder folder,
624161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira            Account account) {
6259ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
6267425d482040c5c2badb466ce840b67396c73f118Paul Westbrook        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
6277425d482040c5c2badb466ce840b67396c73f118Paul Westbrook                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
628898cd38018ff4331e79262451f464efa9dc5d43eMindy Pereira        intent.setDataAndType(conversation.uri, account.mimeType);
6295ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
6307b6d03db55338cbf9717896f99eb20d02bf371e4Mindy Pereira        intent.putExtra(Utils.EXTRA_FOLDER, Folder.toString(folder));
631963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira        intent.putExtra(Utils.EXTRA_CONVERSATION, conversation);
6329ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
6339ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
6349ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
6359ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
6369ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to open a folder.
637c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira     *
638daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @param folder Folder to open.
6390c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
640daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @return
641daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     */
64296a7f7aa2de33b1722009a9ee77d2b7b18a8e3e9Mindy Pereira    public static Intent createViewFolderIntent(Folder folder, Account account) {
6431672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (folder == null || account == null) {
6441672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            LogUtils.wtf(
6451672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal                    LOG_TAG, "Utils.createViewFolderIntent(%s,%s): Bad input", folder, account);
6461672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return null;
6471672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
6489ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
6495ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
6505ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
6515ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setDataAndType(folder.uri, account.mimeType);
6525ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
6531672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        intent.putExtra(Utils.EXTRA_FOLDER, Folder.toString(folder));
6549ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
6559ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
6569ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
6579ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
6580c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * Creates an intent to open the default inbox for the given account.
6590c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     *
6600c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
6610c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @return
6620c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     */
6630c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    public static Intent createViewInboxIntent(Account account) {
6640c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        if (account == null) {
6650c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            LogUtils.wtf(LOG_TAG, "Utils.createViewInboxIntent(%s): Bad input", account);
6660c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            return null;
6670c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        }
6680c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        final Intent intent = new Intent(Intent.ACTION_VIEW);
6690c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
6700c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
6710c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setDataAndType(account.settings.defaultInbox, account.mimeType);
6720c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
6730c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        return intent;
6740c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    }
6750c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal
6760c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    /**
67794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to show context-aware Gmail help.
67894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
67994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context to be used to open the help.
68094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param fromWhere Information about the activity the user was in
68194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * when they requested help.
68294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
683498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook    public static void showHelp(Context context, Account account, String fromWhere) {
684c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook        final String urlString = (account != null && account.helpIntentUri != null) ?
685c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook                account.helpIntentUri.toString() : null;
686c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook        if (TextUtils.isEmpty(urlString) ) {
687498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            LogUtils.e(LOG_TAG, "unable to show help for account: %s", account);
688498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            return;
689498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        }
690c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook        final Uri uri = addParamsToUrl(context, urlString);
69194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Uri.Builder builder = uri.buildUpon();
69294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        // Add the activity specific information parameter.
69330745b6e9c4d280ac578d1186fbc0cec9bd2a838Paul Westbrook        if (!TextUtils.isEmpty(fromWhere)) {
69494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            builder = builder.appendQueryParameter(SMART_HELP_LINK_PARAMETER_NAME, fromWhere);
69594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
69694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
69717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        openUrl(context, builder.build(), null);
69894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
69994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
70094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
70194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to open a link in a browser.
70294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
70394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context
70494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param uri Uri to open.
70594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
70617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook    private static void openUrl(Context context, Uri uri, Bundle optionalExtras) {
70794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if(uri == null || TextUtils.isEmpty(uri.toString())) {
70894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            LogUtils.wtf(LOG_TAG, "invalid url in Utils.openUrl(): %s", uri);
70994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            return;
71094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
71117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
71217beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        // Fill in any of extras that have been requested.
71317beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        if (optionalExtras != null) {
71417beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            intent.putExtras(optionalExtras);
71517beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        }
71694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
71717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook
71894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        context.startActivity(intent);
71994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
72094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
72194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
72294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static Uri addParamsToUrl(Context context, String url) {
72394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        url = replaceLocale(url);
72494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Uri.Builder builder = Uri.parse(url).buildUpon();
72594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        final String versionCode = getVersionCode(context);
72694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (versionCode != null) {
72794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            builder = builder.appendQueryParameter(SMART_LINK_APP_VERSION, versionCode);
72894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
72994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
73094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return builder.build();
73194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
73294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
73394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
73494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Replaces the language/country of the device into the given string.  The pattern "%locale%"
73594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * will be replaced with the <language_code>_<country_code> value.
73694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
73794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param str the string to replace the language/country within
73894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
73994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @return the string with replacement
74094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
74194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static String replaceLocale(String str) {
74294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        // Substitute locale if present in string
74394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (str.contains("%locale%")) {
74494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            Locale locale = Locale.getDefault();
74594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            String tmp = locale.getLanguage() + "_" + locale.getCountry().toLowerCase();
74694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            str = str.replace("%locale%", tmp);
74794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
74894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return str;
74994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
75094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
75194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
75294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Returns the version code for the package, or null if it cannot be retrieved.
75394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
75494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static String getVersionCode(Context context) {
75594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (sVersionCode == null) {
75694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            try {
75794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                sVersionCode = String.valueOf(context.getPackageManager()
75894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        .getPackageInfo(context.getPackageName(), 0 /* flags */)
75994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        .versionCode);
76094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            } catch (NameNotFoundException e) {
76194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                LogUtils.e(Utils.LOG_TAG, "Error finding package %s",
76294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        context.getApplicationInfo().packageName);
76394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            }
76494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
76594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return sVersionCode;
76694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
7671f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira
7681f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    /**
7691f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     * Show the settings screen for the supplied account.
7701f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     */
7711f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    public static void showSettings(Context context, Account account) {
77261400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        if (account == null) {
77361400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
77461400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            return;
77561400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        }
7761f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
7771f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        context.startActivity(settingsIntent);
7781f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    }
77968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira
78068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    /**
781e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     * Show the settings screen for the supplied account.
782e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     */
783e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     public static void showFolderSettings(Context context, Account account, Folder folder) {
7843580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         if (account == null || folder == null) {
7853580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook             LogUtils.e(LOG_TAG, "Invalid attempt to show folder settings. account: %s folder: %s",
7863580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook                     account, folder);
7873580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook             return;
7883580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         }
7893580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
790e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
7913580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
7923580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_FOLDER, folder);
793e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
7943580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         context.startActivity(settingsIntent);
795e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    }
796e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
797e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    /**
79818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     * Show the settings screen for managing all folders.
79918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     */
80018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     public static void showManageFolder(Context context, Account account) {
80118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         if (account == null) {
80218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             LogUtils.e(LOG_TAG, "Invalid attempt to the manage folders screen with null account");
80318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             return;
80418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         }
80518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
80618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
80718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
80818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_MANAGE_FOLDERS, true);
80918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
81018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         context.startActivity(settingsIntent);
81118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    }
81218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
81318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    /**
814fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Show the feedback screen for the supplied account.
815fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
81617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook    public static void sendFeedback(Context context, Account account, boolean reportingProblem) {
817ff2b4f2d920b42d43bd0ccf969fea0e751c8b29aAndy Huang        if (account != null && account.sendFeedbackIntentUri != null) {
81817beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            final Bundle optionalExtras = new Bundle(1);
81917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            optionalExtras.putBoolean(
82017beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook                    UIProvider.SendFeedbackExtras.EXTRA_REPORTING_PROBLEM, reportingProblem);
82117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            openUrl(context, account.sendFeedbackIntentUri, optionalExtras);
8223ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
823fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
824fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
825fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
82668f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * Retrieves the mailbox search query associated with an intent (or null if not available),
82768f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * doing proper sanitizing (e.g. trims whitespace).
82868f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     */
82968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    public static String mailSearchQueryForIntent(Intent intent) {
83068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        String query = intent.getStringExtra(SearchManager.QUERY);
83168f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        return TextUtils.isEmpty(query) ? null : query.trim();
83268f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira   }
83388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
83488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    /**
83588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * Split out a filename's extension and return it.
83688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @param filename a file name
83788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @return the file extension (max of 5 chars including period, like ".docx"), or null
83888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     */
83996f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira    public static String getFileExtension(String filename) {
84088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        String extension = null;
84196f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira        int index = !TextUtils.isEmpty(filename) ? filename.lastIndexOf('.') : -1;
84288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        // Limit the suffix to dot + four characters
84388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        if (index >= 0 && filename.length() - index <= FILE_EXTENSION_MAX_CHARS + 1) {
84488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang            extension = filename.substring(index);
84588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        }
84688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        return extension;
84788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    }
84888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
84988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
85088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Intent#normalizeMimeType(String)} for pre-J)
85188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
85288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Normalize a MIME data type.
85388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
85488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized MIME type has white-space trimmed,
85588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * content-type parameters removed, and is lower-case.
85688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the type with Android best practices for
85788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
85888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
85988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "text/plain; charset=utf-8" becomes "text/plain".
86088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "text/x-vCard" becomes "text/x-vcard".
86188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
86288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All MIME types received from outside Android (such as user input,
86388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
86488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
86588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
86688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @param type MIME data type to normalize
86788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized MIME data type, or null if the input was null
86888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link #setType}
86988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link #setTypeAndNormalize}
87088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
87188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static String normalizeMimeType(String type) {
87288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (type == null) {
87388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           return null;
87488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
87588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
87688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       type = type.trim().toLowerCase(Locale.US);
87788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
87888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       final int semicolonIndex = type.indexOf(';');
87988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (semicolonIndex != -1) {
88088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           type = type.substring(0, semicolonIndex);
88188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
88288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return type;
88388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
88488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
88588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
88688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Uri#normalize()} for pre-J)
88788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
88888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Return a normalized representation of this Uri.
88988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
89088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized Uri has a lowercase scheme component.
89188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the Uri with Android best practices for
89288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
89388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
89488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "HTTP://www.android.com" becomes
89588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "http://www.android.com"
89688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
89788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All URIs received from outside Android (such as user input,
89888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
89988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
90088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
90188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p class="note">This method does <em>not</em> validate bad URI's,
90288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or 'fix' poorly formatted URI's - so do not use it for input validation.
90388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * A Uri will always be returned, even if the Uri is badly formatted to
90488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * begin with and a scheme component cannot be found.
90588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
90688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized Uri (never null)
90788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link android.content.Intent#setData}
90888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link #setNormalizedData}
90988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
91088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Uri normalizeUri(Uri uri) {
91188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String scheme = uri.getScheme();
91288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme == null) return uri;  // give up
91388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String lowerScheme = scheme.toLowerCase(Locale.US);
91488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme.equals(lowerScheme)) return uri;  // no change
91588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
91688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return uri.buildUpon().scheme(lowerScheme).build();
91788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
91888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
91988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Intent setIntentTypeAndNormalize(Intent intent, String type) {
92088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return intent.setType(normalizeMimeType(type));
92188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
92288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
92388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Intent setIntentDataAndTypeAndNormalize(Intent intent, Uri data, String type) {
92488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return intent.setDataAndType(normalizeUri(data), normalizeMimeType(type));
92588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
92688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
927b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   public static int getTransparentColor(int color) {
928b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira       return 0x00ffffff & color;
929b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   }
930863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira
931863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    public static void setMenuItemVisibility(Menu menu, int itemId, boolean shouldShow) {
932863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        final MenuItem item = menu.findItem(itemId);
933863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        if (item == null) {
934863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira            return;
935863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        }
936863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        item.setVisible(shouldShow);
937863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    }
93892551d057965689f1952faeb06763f0762bc717fMindy Pereira
93992551d057965689f1952faeb06763f0762bc717fMindy Pereira    /**
94092551d057965689f1952faeb06763f0762bc717fMindy Pereira     * Parse a string (possibly null or empty) into a URI. If the string is null
94192551d057965689f1952faeb06763f0762bc717fMindy Pereira     * or empty, null is returned back. Otherwise an empty URI is returned.
94292551d057965689f1952faeb06763f0762bc717fMindy Pereira     *
94392551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @param uri
94492551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
94592551d057965689f1952faeb06763f0762bc717fMindy Pereira     */
94692551d057965689f1952faeb06763f0762bc717fMindy Pereira    public static Uri getValidUri(String uri) {
947b378d64bab3c7517794ad7e2aee1d06c074e99eeMindy Pereira        if (TextUtils.isEmpty(uri) || uri == JSONObject.NULL)
94892551d057965689f1952faeb06763f0762bc717fMindy Pereira            return Uri.EMPTY;
94992551d057965689f1952faeb06763f0762bc717fMindy Pereira        return Uri.parse(uri);
95092551d057965689f1952faeb06763f0762bc717fMindy Pereira    }
95110ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank
95210ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    public static boolean isEmpty(Uri uri) {
95310ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank        return uri == null || uri.equals(Uri.EMPTY);
95410ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    }
955ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
95669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
95769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Executes an out-of-band command on the cursor.
95869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param cursor
95969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param request Bundle with all keys and values set for the command.
96069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param key The string value against which we will check for success or failure
96169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @return true if the operation was a success.
96269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
96369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    private static boolean executeConversationCursorCommand(
96469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, Bundle request, String key) {
96569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle response = cursor.respond(request);
96669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final String result = response.getString(key,
967ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_FAILED);
968ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
969ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK.equals(result);
970ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
971ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
972ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
973ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * Commands a cursor representing a set of conversations to disable any network requests it may
974ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * do as clients move through the cursor.
975ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
976ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
977ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @return true iff the provider supports network requests and they were previously enabled
978ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
979ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    public static boolean disableConversationCursorNetworkAccess(Cursor cursor) {
98069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle request = new Bundle();
98169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_ALLOW_NETWORK_ACCESS;
98269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        request.putBoolean(key, false);
98369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        return executeConversationCursorCommand(cursor, request, key);
984ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
985ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
986ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
987ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * Commands a cursor representing a set of conversations to [re-]enable any network requests it
988ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * may do as clients move through the cursor.
989ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
990ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
991ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @return true iff the provider supports network requests and they are successfully enabled
992ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
993ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    public static boolean enableConversationCursorNetworkAccess(Cursor cursor) {
99469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle request = new Bundle();
99569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_ALLOW_NETWORK_ACCESS;
99669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        request.putBoolean(key, true);
99769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        return executeConversationCursorCommand(cursor, request, key);
998ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
9999f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
10009f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
10019f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * Commands a cursor representing a set of conversations to set its visibility state.
10029f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     *
10039f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * @param cursor a conversation cursor
100469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param visible true if the conversation list is visible, false otherwise.
100569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param isFirstSeen true if you want to notify the cursor that this conversation list was seen
100669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        for the first time: the user launched the app into it, or the user switched from some
100769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        other folder into it.
10089f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
100969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    public static void setConversationCursorVisibility(
101069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, boolean visible, boolean isFirstSeen) {
101169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        new MarkConversationCursorVisibleTask(cursor, visible, isFirstSeen).execute();
10129f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
10139f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
10149f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
101569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Async task for  marking conversations "seen" and informing the cursor that the folder was
101669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * seen for the first time by the UI.
10179f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
10189f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    private static class MarkConversationCursorVisibleTask extends AsyncTask<Void, Void, Void> {
10199f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final Cursor mCursor;
10209f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final boolean mVisible;
102169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        private final boolean mIsFirstSeen;
102269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal
102369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        /**
102469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * Create a new task with the given cursor, with the given visibility and
102569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *
102669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param cursor
102769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isVisible true if the conversation list is visible, false otherwise.
102869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isFirstSeen true if the folder was shown for the first time: either the user has
102969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *        just switched to it, or the user started the app in this folder.
103069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         */
103169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        public MarkConversationCursorVisibleTask(
103269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                Cursor cursor, boolean isVisible, boolean isFirstSeen) {
10339f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            mCursor = cursor;
103469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mVisible = isVisible;
103569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mIsFirstSeen = isFirstSeen;
10369f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
10379f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
10389f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        @Override
10399f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        protected Void doInBackground(Void... params) {
104069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mCursor == null) {
104169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                return null;
104269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            }
104369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final Bundle request = new Bundle();
104469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mIsFirstSeen) {
104569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                request.putBoolean(
104669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                        UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER, true);
10479f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
104869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY;
104969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            request.putBoolean(key, mVisible);
105069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            executeConversationCursorCommand(mCursor, request, key);
10519f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            return null;
10529f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
10539f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
10549fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira
1055a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
1056a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    /**
1057a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * This utility method returns the conversation ID at the current cursor position.
1058a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * @return the conversation id at the cursor.
1059a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     */
1060a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    public static long getConversationId(ConversationCursor cursor) {
1061d781c02b91162afa91ba7d7477f40b7eec2aacedPaul Westbrook        return cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
1062a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    }
1063a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
10649fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    /**
10659fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     * @return whether to show two pane or single pane search results.
10669fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     */
10679fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    public static boolean showTwoPaneSearchResults(Context context) {
10689fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira        return context.getResources().getBoolean(R.bool.show_two_pane_search_results);
10699fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    }
1070a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira
1071a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    /**
1072a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     * Return whether menus should show the disabled archive menu item or just
1073a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     * remove it when archive is not available.
1074a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     */
1075a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    public static boolean shouldShowDisabledArchiveIcon(Context context) {
1076a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira        return context.getResources().getBoolean(R.bool.show_disabled_archive_menu_item);
1077a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    }
107855137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira
107955137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    public static int getDefaultFolderBackgroundColor(Context context) {
108055137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        if (sDefaultFolderBackgroundColor == -1) {
108155137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira            sDefaultFolderBackgroundColor = context.getResources().getColor(
108255137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira                    R.color.default_folder_background_color);
108355137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        }
108455137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        return sDefaultFolderBackgroundColor;
108555137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    }
1086443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal
1087a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    public static void fixSubTreeLayoutIfOrphaned(View v, String tag) {
1088a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        if (isLayoutSubTreeOrphaned(v)) {
1089a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang            markDirtyTillRoot(tag, v);
1090a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        }
1091a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    }
1092a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang
1093a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    /**
1094601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * Returns the count that should be shown for the specified folder.  This method should be used
1095601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * when the UI wants to display an "unread" count.  For most labels, the returned value will be
1096601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * the unread count, but for some folder types (outbox, drafts, trash) this will return the
1097601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * total count.
1098601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     */
1099601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook    public static int getFolderUnreadDisplayCount(Folder folder) {
1100601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        final int count;
1101601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        if (folder != null) {
1102601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook            switch (folder.type) {
1103601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                case UIProvider.FolderType.DRAFT:
1104601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                case UIProvider.FolderType.TRASH:
1105601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                case UIProvider.FolderType.OUTBOX:
1106601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                    count = folder.totalCount;
1107601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                    break;
1108601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                default:
1109601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                    count = folder.unreadCount;
1110601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook                    break;
1111601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook            }
1112601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        } else {
1113601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook            count = 0;
1114601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        }
1115601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        return count;
1116601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook    }
1117601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook
1118601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook    /**
1119a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     * An orphaned subtree is one where child views have requested layout, but at least one
1120a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     * ancestor is not marked for layout. In this scenario, any future layout requests on the root
1121a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     * will ignore the orphaned subtree, and we have to force the issue.
1122a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     * <p>
1123a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     * Note: this might be okay if it's true while a layout pass is in progress. Callers should
1124a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     * only trust this return value if not currently in layout.
1125a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     *
1126a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang     */
1127a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    public static boolean isLayoutSubTreeOrphaned(View v) {
1128a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        if (v == null) {
1129a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang            return false;
1130a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        }
1131a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang
1132a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        final boolean isLayoutRequested = v.isLayoutRequested();
1133a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        ViewParent parent = v.getParent();
1134a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        while (isLayoutRequested && parent != null) {
1135a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang            if (!parent.isLayoutRequested()) {
1136a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang                return true;
1137a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang            }
1138a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang            parent = parent.getParent();
1139a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        }
1140a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang        return false;
1141a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    }
1142a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang
1143443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal    /**
1144443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal     * Hacky method to allow invalidating views all the way up the hierarchy.
1145443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal     */
1146443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal    public static void markDirtyTillRoot(String message, View v) {
1147d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//        LogUtils.d(VIEW_DEBUGGING_TAG, "%s: markingDirtyTillRoot", message);
1148d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//        v.invalidate();
1149d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//        ViewParent parent = v.getParent();
1150d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//        while (parent != null) {
1151d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//            parent.requestLayout();
1152d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//            parent = parent.getParent();
1153d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang//        }
1154443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal    }
1155443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal
11567517e3b61b898a57f19be0671f70d58a82224643Andy Huang    public static void checkRequestLayout(View v) {
11577517e3b61b898a57f19be0671f70d58a82224643Andy Huang        boolean inLayout = false;
11587517e3b61b898a57f19be0671f70d58a82224643Andy Huang        final View root = v.getRootView();
11597517e3b61b898a57f19be0671f70d58a82224643Andy Huang
1160d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang        if (root == null || v.isLayoutRequested()) {
11617517e3b61b898a57f19be0671f70d58a82224643Andy Huang            return;
11627517e3b61b898a57f19be0671f70d58a82224643Andy Huang        }
11637517e3b61b898a57f19be0671f70d58a82224643Andy Huang
11647517e3b61b898a57f19be0671f70d58a82224643Andy Huang        final Error e = new Error();
11657517e3b61b898a57f19be0671f70d58a82224643Andy Huang        for (StackTraceElement ste : e.getStackTrace()) {
11667517e3b61b898a57f19be0671f70d58a82224643Andy Huang            if ("android.view.ViewGroup".equals(ste.getClassName())
11677517e3b61b898a57f19be0671f70d58a82224643Andy Huang                    && "layout".equals(ste.getMethodName())) {
11687517e3b61b898a57f19be0671f70d58a82224643Andy Huang                inLayout = true;
11697517e3b61b898a57f19be0671f70d58a82224643Andy Huang                break;
11707517e3b61b898a57f19be0671f70d58a82224643Andy Huang            }
11717517e3b61b898a57f19be0671f70d58a82224643Andy Huang        }
11727517e3b61b898a57f19be0671f70d58a82224643Andy Huang        if (inLayout && !v.isLayoutRequested()) {
1173bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal            LogUtils.i(VIEW_DEBUGGING_TAG,
11747517e3b61b898a57f19be0671f70d58a82224643Andy Huang                    e, "WARNING: in requestLayout during layout pass, view=%s", v);
11757517e3b61b898a57f19be0671f70d58a82224643Andy Huang        }
11767517e3b61b898a57f19be0671f70d58a82224643Andy Huang    }
11777517e3b61b898a57f19be0671f70d58a82224643Andy Huang
11787517e3b61b898a57f19be0671f70d58a82224643Andy Huang    /**
11797517e3b61b898a57f19be0671f70d58a82224643Andy Huang     * Logs extra information about the views to help find the problem with blank fragments.
11807517e3b61b898a57f19be0671f70d58a82224643Andy Huang     * To turn on this debugging, enable the "MailBlankFragment" tag with
11817517e3b61b898a57f19be0671f70d58a82224643Andy Huang     * adb shell setprop log.tag.MailBlankFragment VERBOSE
11827517e3b61b898a57f19be0671f70d58a82224643Andy Huang     * @param message
11837517e3b61b898a57f19be0671f70d58a82224643Andy Huang     * @param v
11847517e3b61b898a57f19be0671f70d58a82224643Andy Huang     */
11857517e3b61b898a57f19be0671f70d58a82224643Andy Huang    public static void dumpLayoutRequests(String message, View v) {
1186bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal        LogUtils.d(VIEW_DEBUGGING_TAG, "dumpLayoutRequests: %s", message);
11877517e3b61b898a57f19be0671f70d58a82224643Andy Huang
11887517e3b61b898a57f19be0671f70d58a82224643Andy Huang        while (v != null) {
1189bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal            LogUtils.d(VIEW_DEBUGGING_TAG,
11907517e3b61b898a57f19be0671f70d58a82224643Andy Huang                    "view item: %s mw/mh=%d/%d w/h=%d/%d layoutRequested=%s vis=%s id=0x%x",
1191d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang                    v.getClass().getSimpleName(), v.getMeasuredWidth(), v.getMeasuredHeight(),
1192d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang                    v.getWidth(), v.getHeight(), v.isLayoutRequested(), v.getVisibility(),
1193d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang                    v.getId());
11947517e3b61b898a57f19be0671f70d58a82224643Andy Huang
11957517e3b61b898a57f19be0671f70d58a82224643Andy Huang            ViewParent vp = v.getParent();
11967517e3b61b898a57f19be0671f70d58a82224643Andy Huang            if (vp instanceof ViewGroup) {
11977517e3b61b898a57f19be0671f70d58a82224643Andy Huang                v = (ViewGroup) vp;
11987517e3b61b898a57f19be0671f70d58a82224643Andy Huang            } else {
11997517e3b61b898a57f19be0671f70d58a82224643Andy Huang                if (vp != null) {
12007517e3b61b898a57f19be0671f70d58a82224643Andy Huang                    // this is the root. can't really get access to this guy
1201bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal                    LogUtils.d(VIEW_DEBUGGING_TAG,
12027517e3b61b898a57f19be0671f70d58a82224643Andy Huang                            "view item: (ViewRootImpl) isLayoutRequested=%s\n",
12037517e3b61b898a57f19be0671f70d58a82224643Andy Huang                            vp.isLayoutRequested());
12047517e3b61b898a57f19be0671f70d58a82224643Andy Huang                }
12057517e3b61b898a57f19be0671f70d58a82224643Andy Huang                v = null;
12067517e3b61b898a57f19be0671f70d58a82224643Andy Huang            }
12077517e3b61b898a57f19be0671f70d58a82224643Andy Huang        }
12087517e3b61b898a57f19be0671f70d58a82224643Andy Huang    }
12097b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira}
1210