Utils.java revision 5ad02918f663bc52522a2505de985df9ef5ea347
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;
44326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View.MeasureSpec;
45f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.view.ViewGroup;
468b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebSettings;
478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebView;
488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
4930e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
508a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account;
519ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereiraimport com.android.mail.providers.Conversation;
528a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder;
53ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.providers.UIProvider;
54e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrookimport com.android.mail.providers.UIProvider.EditSettingsExtras;
5595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlDocument;
5695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlParser;
5795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlTree;
5895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlTreeBuilder;
5910ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blankimport com.google.common.collect.Maps;
603e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
6192551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport org.json.JSONObject;
6292551d057965689f1952faeb06763f0762bc717fMindy Pereira
6394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport java.util.Locale;
643e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport java.util.Map;
657b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
666f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereirapublic class Utils {
676f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    /**
686f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     * longest extension we recognize is 4 characters (e.g. "html", "docx")
696f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     */
706f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    private static final int FILE_EXTENSION_MAX_CHARS = 4;
713e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    private static final Map<Integer, Integer> sPriorityToLength = Maps.newHashMap();
723e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_ELIDED = "e";
733e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_MESSAGES = "n";
743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_DRAFTS = "d";
753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_LITERAL = "l";
763e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SENDING = "s";
773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SEND_FAILED = "f";
783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final Character SENDER_LIST_SEPARATOR = '\n';
793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final SimpleStringSplitter sSenderListSplitter = new SimpleStringSplitter(
803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            SENDER_LIST_SEPARATOR);
813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static String[] sSenderFragments = new String[8];
828b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
836349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    public static final String EXTRA_ACCOUNT = "account";
84e144980ef9c8828401922373ef0dc203281bff6ePaul Westbrook    public static final String EXTRA_ACCOUNT_URI = "accountUri";
8591d0b86db3287f1702913177d347dd42b7d13764Marc Blank    public static final String EXTRA_FOLDER_URI = "folderUri";
867418e4b9942f291b8de8bc7b1b72a7ef7130a8b6Mindy Pereira    public static final String EXTRA_COMPOSE_URI = "composeUri";
87963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_CONVERSATION = "conversationUri";
88963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_FOLDER = "folder";
898a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /*
908a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Notifies that changes happened. Certain UI components, e.g., widgets, can
918a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * register for this {@link Intent} and update accordingly. However, this
928a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * can be very broad and is NOT the preferred way of getting notification.
938a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
948a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    // TODO: UI Provider has this notification URI?
9594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static final String ACTION_NOTIFY_DATASET_CHANGED =
9694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
9794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
9894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /** Parameter keys for context-aware help. */
9994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_HELP_LINK_PARAMETER_NAME = "p";
10094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
10194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_LINK_APP_VERSION = "version";
10294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static String sVersionCode = null;
10394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
104b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
1056349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
106e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    public static boolean isRunningJellybeanOrLater() {
107e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
108e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    }
109e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook
1102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Sets WebView in a restricted mode suitable for email use.
1122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param webView The WebView to restrict
1142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void restrictWebView(WebView webView) {
1168b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        WebSettings webSettings = webView.getSettings();
1178b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSavePassword(false);
1188b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSaveFormData(false);
1198b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setJavaScriptEnabled(true);
1208b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSupportZoom(false);
1212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Format a plural string.
1252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param resource The identity of the resource, which must be a R.plurals
1272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param count The number of items.
1282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String formatPlural(Context context, int resource, int count) {
1302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence formatString = context.getResources().getQuantityText(resource, count);
1312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return String.format(formatString.toString(), count);
1322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return an ellipsized String that's at most maxCharacters long. If the
1362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         text passed is longer, it will be abbreviated. If it contains a
1372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix, the ellipses will be inserted in the middle and the
1382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix will be preserved.
1392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ellipsize(String text, int maxCharacters) {
1412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int length = text.length();
1422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (length < maxCharacters)
1432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return text;
1442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int realMax = Math.min(maxCharacters, length);
1462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Preserve the suffix if any
1472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int index = text.lastIndexOf(".");
1482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String extension = "\u2026"; // "...";
1492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (index >= 0) {
1502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            // Limit the suffix to dot + four characters
1512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (length - index <= FILE_EXTENSION_MAX_CHARS + 1) {
1522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                extension = extension + text.substring(index + 1);
1532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
1542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
1552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        realMax -= extension.length();
1562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (realMax < 0)
1572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            realMax = 0;
1582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return text.substring(0, realMax) + extension;
1592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1624ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * Ensures that the given string starts and ends with the double quote
1634ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * character. The string is not modified in any way except to add the double
1644ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * quote character to start and end if it's not already there. sample ->
1654ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * "sample" "sample" -> "sample" ""sample"" -> "sample"
1664ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * "sample"" -> "sample" sa"mp"le -> "sa"mp"le" "sa"mp"le" -> "sa"mp"le"
1674ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * (empty string) -> "" " -> ""
1684ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     */
1692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ensureQuotedString(String s) {
1702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (s == null) {
1712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return null;
1722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
1732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (!s.matches("^\".*\"$")) {
1742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return "\"" + s + "\"";
1752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
1762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return s;
1772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
1782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    // TODO: Move this to the UI Provider.
1812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sUnreadStyleSpan = null;
1822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sReadStyleSpan;
1832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sDraftsStyleSpan;
1842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sMeString;
1852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sDraftSingularString;
1862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sDraftPluralString;
1872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sSendingString;
1882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sSendFailedString;
1892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static int sMaxUnreadCount = -1;
1912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static String sUnreadText;
1922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void getStyledSenderSnippet(Context context, String senderInstructions,
1942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder,
1952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            int maxChars, boolean forceAllUnread, boolean forceAllRead, boolean allowDraft) {
1962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        Resources res = context.getResources();
1972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sUnreadStyleSpan == null) {
1982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sUnreadStyleSpan = new StyleSpan(Typeface.BOLD);
1992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sReadStyleSpan = new StyleSpan(Typeface.NORMAL);
2002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftsStyleSpan = new ForegroundColorSpan(res.getColor(R.color.drafts));
2012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sMeString = context.getText(R.string.me);
2032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftSingularString = res.getQuantityText(R.plurals.draft, 1);
2042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftPluralString = res.getQuantityText(R.plurals.draft, 2);
2052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableString sendingString = new SpannableString(context.getText(R.string.sending));
2062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sendingString.setSpan(CharacterStyle.wrap(sDraftsStyleSpan), 0, sendingString.length(),
2072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    0);
2082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sSendingString = sendingString;
2092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sSendFailedString = context.getText(R.string.send_failed);
2102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        getSenderSnippet(senderInstructions, senderBuilder, statusBuilder, maxChars,
2132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadStyleSpan, sReadStyleSpan, sDraftsStyleSpan, sMeString,
2142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sDraftSingularString, sDraftPluralString, sSendingString, sSendFailedString,
2152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                forceAllUnread, forceAllRead, allowDraft);
2162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
2193e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Uses sender instructions to build a formatted string.
2203e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2213e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Sender list instructions contain compact information about the sender
2223e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * list. Most work that can be done without knowing how much room will be
2233e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * availble for the sender list is done when creating the instructions.
2243e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2253e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * The instructions string consists of tokens separated by
2263e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * SENDER_LIST_SEPARATOR. Here are the tokens, one per line:
2273e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <ul>
2283e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>n</tt></li>
2293e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the number of non-draft messages in the conversation</li>
2303e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>d</tt</li>
2313e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the number of drafts in the conversation</li>
2323e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>l</tt></li>
2333e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>literal html to be included in the output</em></li>
2343e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>s</tt> indicates that the message is sending (in the outbox
2353e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * without errors)</li>
2363e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>f</tt> indicates that the message failed to send (in the outbox
2373e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * with errors)</li>
2383e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>for each message</em>
2393e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <ul>
2403e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, 0 for read, 1 for unread</li>
2413e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the priority of the message. Zero is the most important
2423e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </li>
2433e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>text</em>, the sender text or blank for messages from 'me'</li>
2443e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </ul>
2453e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </li>
2463e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>e</tt> to indicate that one or more messages have been elided</li>
2473e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2483e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * The instructions indicate how many messages and drafts are in the
2493e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * conversation and then describe the most important messages in order,
2503e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * indicating the priority of each message and whether the message is
2513e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * unread.
252f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
2533e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param instructions instructions as described above
2543e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param senderBuilder the SpannableStringBuilder to append to for sender
2553e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            information
2563e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param statusBuilder the SpannableStringBuilder to append to for status
2573e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param maxChars the number of characters available to display the text
2583e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param unreadStyle the CharacterStyle for unread messages, or null
2593e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftsStyle the CharacterStyle for draft messages, or null
2603e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param sendingString the string to use when there are messages scheduled
2613e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            to be sent
2623e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param sendFailedString the string to use when there are messages that
2633e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            mailed to send
2643e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param meString the string to use for messages sent by this user
2653e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftString the string to use for "Draft"
2663e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftPluralString the string to use for "Drafts"
2673e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     */
2682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static synchronized void getSenderSnippet(String instructions,
2692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder,
2702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            int maxChars, CharacterStyle unreadStyle, CharacterStyle readStyle,
2712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharacterStyle draftsStyle, CharSequence meString, CharSequence draftString,
2722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharSequence draftPluralString, CharSequence sendingString,
2732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharSequence sendFailedString, boolean forceAllUnread, boolean forceAllRead,
2742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            boolean allowDraft) {
2752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        assert !(forceAllUnread && forceAllRead);
2762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        boolean unreadStatusIsForced = forceAllUnread || forceAllRead;
2772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        boolean forcedUnreadStatus = forceAllUnread;
2782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Measure each fragment. It's ok to iterate over the entire set of
2802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // fragments because it is
2812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // never a long list, even if there are many senders.
2822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        final Map<Integer, Integer> priorityToLength = sPriorityToLength;
2832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        priorityToLength.clear();
2842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int maxFoundPriority = Integer.MIN_VALUE;
2862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numMessages = 0;
2872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numDrafts = 0;
2882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence draftsFragment = "";
2892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence sendingFragment = "";
2902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence sendFailedFragment = "";
2912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        sSenderListSplitter.setString(instructions);
2932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numFragments = 0;
2942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String[] fragments = sSenderFragments;
2952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int currentSize = fragments.length;
2962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        while (sSenderListSplitter.hasNext()) {
2972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fragments[numFragments++] = sSenderListSplitter.next();
2982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (numFragments == currentSize) {
2992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sSenderFragments = new String[2 * currentSize];
3002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                System.arraycopy(fragments, 0, sSenderFragments, 0, currentSize);
3012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                currentSize *= 2;
3022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fragments = sSenderFragments;
3032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        for (int i = 0; i < numFragments;) {
3072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            String fragment0 = fragments[i++];
3082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if ("".equals(fragment0)) {
3092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // This should be the final fragment.
3102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) {
3112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // ignore
3122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) {
3132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numMessages = Integer.valueOf(fragments[i++]);
3142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) {
3152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String numDraftsString = fragments[i++];
3162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numDrafts = Integer.parseInt(numDraftsString);
3172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                draftsFragment = numDrafts == 1 ? draftString : draftPluralString + " ("
3182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        + numDraftsString + ")";
3192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_LITERAL.equals(fragment0)) {
3202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                senderBuilder.append(Html.fromHtml(fragments[i++]));
3212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                return;
3222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) {
3232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sendingFragment = sendingString;
3242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) {
3252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sendFailedFragment = sendFailedString;
3262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else {
3272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String priorityString = fragments[i++];
3282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                CharSequence nameString = fragments[i++];
3292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (nameString.length() == 0)
3302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = meString;
3312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                int priority = Integer.parseInt(priorityString);
3322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                priorityToLength.put(priority, nameString.length());
3332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                maxFoundPriority = Math.max(maxFoundPriority, priority);
3342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String numMessagesFragment = (numMessages != 0) ? " \u00A0"
3372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                + Integer.toString(numMessages + numDrafts) : "";
3382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Don't allocate fixedFragment unless we need it
3402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        SpannableStringBuilder fixedFragment = null;
3412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int fixedFragmentLength = 0;
3422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (draftsFragment.length() != 0 && allowDraft) {
3432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
3442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
3452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(draftsFragment);
3472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (draftsStyle != null) {
3482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.setSpan(CharacterStyle.wrap(draftsStyle), 0, fixedFragment.length(),
3492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
3502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sendingFragment.length() != 0) {
3532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
3542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
3552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment.length() != 0)
3572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.append(", ");
3582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(sendingFragment);
3592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sendFailedFragment.length() != 0) {
3612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
3622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
3632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment.length() != 0)
3652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.append(", ");
3662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(sendFailedFragment);
3672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (fixedFragment != null) {
3702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragmentLength = fixedFragment.length();
3712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        maxChars -= fixedFragmentLength;
3732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int maxPriorityToInclude = -1; // inclusive
3752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numCharsUsed = numMessagesFragment.length();
3762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numSendersUsed = 0;
3772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        while (maxPriorityToInclude < maxFoundPriority) {
3782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (priorityToLength.containsKey(maxPriorityToInclude + 1)) {
3792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                int length = numCharsUsed + priorityToLength.get(maxPriorityToInclude + 1);
3802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (numCharsUsed > 0)
3812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    length += 2;
3822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // We must show at least two senders if they exist. If we don't
3832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // have space for both
3842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // then we will truncate names.
3852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (length > maxChars && numSendersUsed >= 2) {
3862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    break;
3872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
3882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numCharsUsed = length;
3892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numSendersUsed++;
3902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            maxPriorityToInclude++;
3922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numCharsToRemovePerWord = 0;
3952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (numCharsUsed > maxChars) {
3962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            numCharsToRemovePerWord = (numCharsUsed - maxChars) / numSendersUsed;
3972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String lastFragment = null;
4002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharacterStyle lastStyle = null;
4012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        for (int i = 0; i < numFragments;) {
4022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            String fragment0 = fragments[i++];
4032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if ("".equals(fragment0)) {
4042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // This should be the final fragment.
4052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) {
4062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (lastFragment != null) {
4072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    senderBuilder.append(" ");
4092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    addStyledFragment(senderBuilder, "..", lastStyle, true);
4102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    senderBuilder.append(" ");
4112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                lastFragment = null;
4132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) {
4142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                i++;
4152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) {
4162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                i++;
4172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) {
4182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) {
4192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else {
4202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final String unreadString = fragment0;
4212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final String priorityString = fragments[i++];
4222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String nameString = fragments[i++];
4232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (nameString.length() == 0) {
4242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = meString.toString();
4252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                } else {
4262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = Html.fromHtml(nameString).toString();
4272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (numCharsToRemovePerWord != 0) {
4292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = nameString.substring(0,
4302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                            Math.max(nameString.length() - numCharsToRemovePerWord, 0));
4312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final boolean unread = unreadStatusIsForced ? forcedUnreadStatus : Integer
4332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        .parseInt(unreadString) != 0;
4342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final int priority = Integer.parseInt(priorityString);
4352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (priority <= maxPriorityToInclude) {
4362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    if (lastFragment != null && !lastFragment.equals(nameString)) {
4372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, lastFragment.concat(","), lastStyle,
4382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                                false);
4392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    }
4412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastFragment = nameString;
4422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastStyle = unread ? unreadStyle : readStyle;
4432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                } else {
4442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    if (lastFragment != null) {
4452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // Adjacent spans can cause the TextView in Gmail widget
4472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // confused and leads to weird behavior on scrolling.
4482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // Our workaround here is to separate the spans by
4492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // spaces.
4502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, "..", lastStyle, true);
4522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    }
4542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastFragment = null;
4552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
4572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (lastFragment != null) {
4592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        senderBuilder.append(numMessagesFragment);
4622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (fixedFragmentLength != 0) {
4632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            statusBuilder.append(fixedFragment);
4642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
4666f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira
4673e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    /**
4683e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Adds a fragment with given style to a string builder.
469f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
4703e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param builder the current string builder
4713e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param fragment the fragment to be added
4723e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param style the style of the fragment
4733e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param withSpaces whether to add the whole fragment or to divide it into
4743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            smaller ones
4753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     */
4763e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    private static void addStyledFragment(SpannableStringBuilder builder, String fragment,
4773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            CharacterStyle style, boolean withSpaces) {
4783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        if (withSpaces) {
4793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            int pos = builder.length();
4803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            builder.append(fragment);
4813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            builder.setSpan(CharacterStyle.wrap(style), pos, builder.length(),
4823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
4833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        } else {
4843e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            int start = 0;
4853e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            while (true) {
4863e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                int pos = fragment.substring(start).indexOf(' ');
4873e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                if (pos == -1) {
4883e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    addStyledFragment(builder, fragment.substring(start), style, true);
4893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    break;
4903e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                } else {
4913e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    pos += start;
4923e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    if (start < pos) {
4933e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        addStyledFragment(builder, fragment.substring(start, pos), style, true);
4943e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        builder.append(' ');
4953e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    }
4963e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    start = pos + 1;
4973e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    if (start >= fragment.length()) {
4983e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        break;
4993e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    }
5003e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                }
5013e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            }
5023e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        }
5033e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    }
5043e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
5052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
5062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Returns a boolean indicating whether the table UI should be shown.
5072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static boolean useTabletUI(Context context) {
5092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return context.getResources().getInteger(R.integer.use_tablet_ui) != 0;
5102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
5122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
51395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns displayable text from the provided HTML string.
51495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     *
51595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @param htmlText HTML string
51695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @return Plain text string representation of the specified Html string
51795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
51895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static String convertHtmlToPlainText(String htmlText) {
51995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        return getHtmlTree(htmlText).getPlainText();
52095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
52195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
52295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
52395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns a {@link HtmlTree} representation of the specified HTML string.
52495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
52595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static HtmlTree getHtmlTree(String htmlText) {
52695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlParser parser = new HtmlParser();
52795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlDocument doc = parser.parse(htmlText);
52895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlTreeBuilder builder = new HtmlTreeBuilder();
52995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        doc.accept(builder);
53095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
53195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        return builder.getTree();
53295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
53395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
53495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
53595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
5362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Perform a simulated measure pass on the given child view, assuming the
5372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * child has a ViewGroup parent and that it should be laid out within that
5382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * parent with a matching width but variable height. Code largely lifted
5392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * from AnimatedAdapter.measureChildHeight().
540f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
5412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param child a child view that has already been placed within its parent
5422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *            ViewGroup
5432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param parent the parent ViewGroup of child
5442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return measured height of the child in px
5452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static int measureViewHeight(View child, ViewGroup parent) {
5472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int parentWSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY);
5482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int wSpec = ViewGroup.getChildMeasureSpec(parentWSpec,
5492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                parent.getPaddingLeft() + parent.getPaddingRight(),
5502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                ViewGroup.LayoutParams.MATCH_PARENT);
5512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int hSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
5522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        child.measure(wSpec, hSpec);
5532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return child.getMeasuredHeight();
5542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
555326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
55646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
55746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Encode the string in HTML.
55846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
55946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param removeEmptyDoubleQuotes If true, also remove any occurrence of ""
56046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *            found in the string
56146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
5622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static Object cleanUpString(String string, boolean removeEmptyDoubleQuotes) {
5632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return !TextUtils.isEmpty(string) ? TextUtils.htmlEncode(removeEmptyDoubleQuotes ? string
5642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                .replace("\"\"", "") : string) : "";
5652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
5672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
5682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Returns comma seperated strings as an array.
5692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String[] splitCommaSeparatedString(String str) {
5712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return TextUtils.isEmpty(str) ? new String[0] : TextUtils.split(str, ",");
5722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
5742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
5752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Get the correct display string for the unread count of a folder.
5762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
5772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String getUnreadCountString(Context context, int unreadCount) {
5782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String unreadCountString;
5792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        Resources resources = context.getResources();
5802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sMaxUnreadCount == -1) {
5812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount);
5822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
5832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (unreadCount > sMaxUnreadCount) {
5842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (sUnreadText == null) {
5852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadText = resources.getString(R.string.widget_large_unread_count);
5862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
5872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = String.format(sUnreadText, sMaxUnreadCount);
5882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else if (unreadCount <= 0) {
5892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = "";
5902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
5912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = String.valueOf(unreadCount);
5922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
5932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return unreadCountString;
5942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
59528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira
59628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    /**
59728beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     * Get text matching the last sync status.
59828beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     */
59928beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    public static CharSequence getSyncStatusText(Context context, int status) {
60028beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        String[] errors = context.getResources().getStringArray(R.array.sync_status);
60128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        if (status >= errors.length) {
60228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira            return "";
60328beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        }
60428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        return errors[status];
60528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    }
6068a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
6078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
6089ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to show a conversation.
6099ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @param conversation Conversation to open.
610161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param folder
611161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param account
6129ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @return
6139ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     */
614161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira    public static Intent createViewConversationIntent(Conversation conversation, Folder folder,
615161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira            Account account) {
6169ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
6177425d482040c5c2badb466ce840b67396c73f118Paul Westbrook        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
6187425d482040c5c2badb466ce840b67396c73f118Paul Westbrook                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
619898cd38018ff4331e79262451f464efa9dc5d43eMindy Pereira        intent.setDataAndType(conversation.uri, account.mimeType);
6205ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
6215ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_FOLDER, folder.serialize());
622963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira        intent.putExtra(Utils.EXTRA_CONVERSATION, conversation);
6239ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
6249ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
6259ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
6269ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
6279ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to open a folder.
628c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira     *
629daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @param folderUri Folder uri.
630daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @param account
631daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @param folder Folder to open.
632daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @param pendingIntent If this will be used as a pending intent we need to
633daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     *            send strings not parcelables.
634daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @return
635daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     */
6365ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira    public static Intent createViewFolderIntent(Folder folder, Account account,
637daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            boolean pendingIntent) {
6389ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
6395ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
6405ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
6415ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setDataAndType(folder.uri, account.mimeType);
6425ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
6435ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        if (folder != null) {
6445ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira            intent.putExtra(Utils.EXTRA_FOLDER, folder.serialize());
645c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira        }
6469ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
6479ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
6489ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
6499ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
65094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to show context-aware Gmail help.
65194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
65294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context to be used to open the help.
65394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param fromWhere Information about the activity the user was in
65494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * when they requested help.
65594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
656498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook    public static void showHelp(Context context, Account account, String fromWhere) {
657498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        if (account == null || account.helpIntentUri == null) {
658498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            LogUtils.e(LOG_TAG, "unable to show help for account: %s", account);
659498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            return;
660498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        }
661498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        final Uri uri = addParamsToUrl(context, account.helpIntentUri.toString());
66294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Uri.Builder builder = uri.buildUpon();
66394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        // Add the activity specific information parameter.
66494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (fromWhere != null) {
66594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            builder = builder.appendQueryParameter(SMART_HELP_LINK_PARAMETER_NAME, fromWhere);
66694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
66794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
66894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        openUrl(context, builder.build());
66994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
67094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
67194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
67294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to open a link in a browser.
67394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
67494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context
67594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param uri Uri to open.
67694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
67794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static void openUrl(Context context, Uri uri) {
67894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if(uri == null || TextUtils.isEmpty(uri.toString())) {
67994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            LogUtils.wtf(LOG_TAG, "invalid url in Utils.openUrl(): %s", uri);
68094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            return;
68194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
68294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
68394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
68494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        context.startActivity(intent);
68594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
68694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
68794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
68894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static Uri addParamsToUrl(Context context, String url) {
68994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        url = replaceLocale(url);
69094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Uri.Builder builder = Uri.parse(url).buildUpon();
69194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        final String versionCode = getVersionCode(context);
69294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (versionCode != null) {
69394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            builder = builder.appendQueryParameter(SMART_LINK_APP_VERSION, versionCode);
69494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
69594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
69694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return builder.build();
69794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
69894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
69994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
70094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Replaces the language/country of the device into the given string.  The pattern "%locale%"
70194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * will be replaced with the <language_code>_<country_code> value.
70294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
70394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param str the string to replace the language/country within
70494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
70594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @return the string with replacement
70694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
70794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static String replaceLocale(String str) {
70894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        // Substitute locale if present in string
70994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (str.contains("%locale%")) {
71094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            Locale locale = Locale.getDefault();
71194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            String tmp = locale.getLanguage() + "_" + locale.getCountry().toLowerCase();
71294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            str = str.replace("%locale%", tmp);
71394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
71494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return str;
71594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
71694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
71794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
71894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Returns the version code for the package, or null if it cannot be retrieved.
71994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
72094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static String getVersionCode(Context context) {
72194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (sVersionCode == null) {
72294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            try {
72394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                sVersionCode = String.valueOf(context.getPackageManager()
72494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        .getPackageInfo(context.getPackageName(), 0 /* flags */)
72594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        .versionCode);
72694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            } catch (NameNotFoundException e) {
72794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                LogUtils.e(Utils.LOG_TAG, "Error finding package %s",
72894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        context.getApplicationInfo().packageName);
72994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            }
73094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
73194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return sVersionCode;
73294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
7331f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira
7341f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    /**
7351f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     * Show the settings screen for the supplied account.
7361f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     */
7371f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    public static void showSettings(Context context, Account account) {
73861400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        if (account == null) {
73961400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
74061400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            return;
74161400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        }
7421f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
7431f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        context.startActivity(settingsIntent);
7441f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    }
74568f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira
74668f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    /**
747e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     * Show the settings screen for the supplied account.
748e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     */
749e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     public static void showFolderSettings(Context context, Account account, Folder folder) {
7503580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         if (account == null || folder == null) {
7513580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook             LogUtils.e(LOG_TAG, "Invalid attempt to show folder settings. account: %s folder: %s",
7523580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook                     account, folder);
7533580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook             return;
7543580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         }
7553580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
756e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
7573580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
7583580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_FOLDER, folder);
759e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
7603580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook         context.startActivity(settingsIntent);
761e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    }
762e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
763e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    /**
76418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     * Show the settings screen for managing all folders.
76518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     */
76618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     public static void showManageFolder(Context context, Account account) {
76718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         if (account == null) {
76818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             LogUtils.e(LOG_TAG, "Invalid attempt to the manage folders screen with null account");
76918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             return;
77018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         }
77118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
77218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
77318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
77418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_MANAGE_FOLDERS, true);
77518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
77618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         context.startActivity(settingsIntent);
77718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    }
77818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
77918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    /**
780fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Show the feedback screen for the supplied account.
781fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
782fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    public static void sendFeedback(Context context, Account account) {
783ff2b4f2d920b42d43bd0ccf969fea0e751c8b29aAndy Huang        if (account != null && account.sendFeedbackIntentUri != null) {
7843ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira            openUrl(context, account.sendFeedbackIntentUri);
7853ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
786fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
787fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
788fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
78968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * Retrieves the mailbox search query associated with an intent (or null if not available),
79068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * doing proper sanitizing (e.g. trims whitespace).
79168f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     */
79268f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    public static String mailSearchQueryForIntent(Intent intent) {
79368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        String query = intent.getStringExtra(SearchManager.QUERY);
79468f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        return TextUtils.isEmpty(query) ? null : query.trim();
79568f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira   }
79688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
79788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    /**
79888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * Split out a filename's extension and return it.
79988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @param filename a file name
80088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @return the file extension (max of 5 chars including period, like ".docx"), or null
80188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     */
80296f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira    public static String getFileExtension(String filename) {
80388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        String extension = null;
80496f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira        int index = !TextUtils.isEmpty(filename) ? filename.lastIndexOf('.') : -1;
80588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        // Limit the suffix to dot + four characters
80688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        if (index >= 0 && filename.length() - index <= FILE_EXTENSION_MAX_CHARS + 1) {
80788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang            extension = filename.substring(index);
80888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        }
80988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        return extension;
81088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    }
81188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
81288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
81388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Intent#normalizeMimeType(String)} for pre-J)
81488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
81588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Normalize a MIME data type.
81688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
81788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized MIME type has white-space trimmed,
81888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * content-type parameters removed, and is lower-case.
81988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the type with Android best practices for
82088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
82188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
82288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "text/plain; charset=utf-8" becomes "text/plain".
82388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "text/x-vCard" becomes "text/x-vcard".
82488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
82588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All MIME types received from outside Android (such as user input,
82688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
82788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
82888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
82988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @param type MIME data type to normalize
83088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized MIME data type, or null if the input was null
83188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link #setType}
83288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link #setTypeAndNormalize}
83388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
83488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static String normalizeMimeType(String type) {
83588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (type == null) {
83688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           return null;
83788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
83888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
83988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       type = type.trim().toLowerCase(Locale.US);
84088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
84188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       final int semicolonIndex = type.indexOf(';');
84288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (semicolonIndex != -1) {
84388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           type = type.substring(0, semicolonIndex);
84488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
84588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return type;
84688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
84788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
84888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
84988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Uri#normalize()} for pre-J)
85088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
85188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Return a normalized representation of this Uri.
85288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
85388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized Uri has a lowercase scheme component.
85488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the Uri with Android best practices for
85588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
85688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
85788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "HTTP://www.android.com" becomes
85888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "http://www.android.com"
85988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
86088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All URIs received from outside Android (such as user input,
86188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
86288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
86388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
86488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p class="note">This method does <em>not</em> validate bad URI's,
86588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or 'fix' poorly formatted URI's - so do not use it for input validation.
86688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * A Uri will always be returned, even if the Uri is badly formatted to
86788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * begin with and a scheme component cannot be found.
86888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
86988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized Uri (never null)
87088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link android.content.Intent#setData}
87188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link #setNormalizedData}
87288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
87388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Uri normalizeUri(Uri uri) {
87488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String scheme = uri.getScheme();
87588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme == null) return uri;  // give up
87688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String lowerScheme = scheme.toLowerCase(Locale.US);
87788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme.equals(lowerScheme)) return uri;  // no change
87888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
87988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return uri.buildUpon().scheme(lowerScheme).build();
88088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
88188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
88288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Intent setIntentTypeAndNormalize(Intent intent, String type) {
88388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return intent.setType(normalizeMimeType(type));
88488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
88588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
88688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Intent setIntentDataAndTypeAndNormalize(Intent intent, Uri data, String type) {
88788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return intent.setDataAndType(normalizeUri(data), normalizeMimeType(type));
88888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
88988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
890b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   public static int getTransparentColor(int color) {
891b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira       return 0x00ffffff & color;
892b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   }
893863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira
894863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    public static void setMenuItemVisibility(Menu menu, int itemId, boolean shouldShow) {
895863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        final MenuItem item = menu.findItem(itemId);
896863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        if (item == null) {
897863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira            return;
898863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        }
899863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        item.setVisible(shouldShow);
900863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    }
90192551d057965689f1952faeb06763f0762bc717fMindy Pereira
90292551d057965689f1952faeb06763f0762bc717fMindy Pereira    /**
90392551d057965689f1952faeb06763f0762bc717fMindy Pereira     * Parse a string (possibly null or empty) into a URI. If the string is null
90492551d057965689f1952faeb06763f0762bc717fMindy Pereira     * or empty, null is returned back. Otherwise an empty URI is returned.
90592551d057965689f1952faeb06763f0762bc717fMindy Pereira     *
90692551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @param uri
90792551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
90892551d057965689f1952faeb06763f0762bc717fMindy Pereira     */
90992551d057965689f1952faeb06763f0762bc717fMindy Pereira    public static Uri getValidUri(String uri) {
910df886e7ba9eefd9a261e7cc95a5999fe0fde5351Marc Blank        if (uri == null || uri == JSONObject.NULL)
91192551d057965689f1952faeb06763f0762bc717fMindy Pereira            return Uri.EMPTY;
91292551d057965689f1952faeb06763f0762bc717fMindy Pereira        return Uri.parse(uri);
91392551d057965689f1952faeb06763f0762bc717fMindy Pereira    }
91410ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank
91510ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    public static boolean isEmpty(Uri uri) {
91610ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank        return uri == null || uri.equals(Uri.EMPTY);
91710ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    }
918ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
919ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    public static boolean executeConversationCursorCommand(Cursor cursor, String commandKey,
920ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang            boolean value) {
921ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        final Bundle params = new Bundle();
922ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        params.putBoolean(commandKey, value);
923ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        final Bundle response = cursor.respond(params);
924ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        final String result = response.getString(commandKey,
925ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_FAILED);
926ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
927ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK.equals(result);
928ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
929ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
930ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
931ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * Commands a cursor representing a set of conversations to disable any network requests it may
932ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * do as clients move through the cursor.
933ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
934ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
935ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @return true iff the provider supports network requests and they were previously enabled
936ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
937ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    public static boolean disableConversationCursorNetworkAccess(Cursor cursor) {
938ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return executeConversationCursorCommand(cursor,
939ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_KEY_ALLOW_NETWORK_ACCESS, false);
940ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
941ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
942ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
943ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * Commands a cursor representing a set of conversations to [re-]enable any network requests it
944ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * may do as clients move through the cursor.
945ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
946ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
947ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @return true iff the provider supports network requests and they are successfully enabled
948ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
949ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    public static boolean enableConversationCursorNetworkAccess(Cursor cursor) {
950ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return executeConversationCursorCommand(cursor,
951ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_KEY_ALLOW_NETWORK_ACCESS, true);
952ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
9539f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
9549f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
9559f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * Commands a cursor representing a set of conversations to set its visibility state.
9569f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     *
9579f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * @param cursor a conversation cursor
9589f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
9599f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    public static void setConversationCursorVisibility(Cursor cursor, boolean visible) {
9609f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        new MarkConversationCursorVisibleTask(cursor, visible).execute();
9619f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
9629f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
9639f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
9649f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * Async task for  marking conversations "seen"
9659f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
9669f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    private static class MarkConversationCursorVisibleTask extends AsyncTask<Void, Void, Void> {
9679f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final Cursor mCursor;
9689f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final boolean mVisible;
9699f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
9709f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        public MarkConversationCursorVisibleTask(Cursor cursor, boolean visible) {
9719f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            mCursor = cursor;
9729f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            mVisible = visible;
9739f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
9749f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
9759f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        @Override
9769f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        protected Void doInBackground(Void... params) {
9779f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            if (mCursor != null) {
9789f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook                executeConversationCursorCommand(mCursor,
9799f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook                        UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY, mVisible);
9809f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
9819f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            return null;
9829f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
9839f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
9849fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira
9859fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    /**
9869fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     * @return whether to show two pane or single pane search results.
9879fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     */
9889fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    public static boolean showTwoPaneSearchResults(Context context) {
9899fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira        return context.getResources().getBoolean(R.bool.show_two_pane_search_results);
9909fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    }
9917b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira}
992