Utils.java revision b573c5c0c05deec139985b883a12869dc6cac5a0
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
195c1692a5faeab220881a17a3427a8986ef874403Andrew Sappersteinimport com.android.mail.providers.Address;
201bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HtmlDocument;
211bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HtmlParser;
221bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HtmlTree;
231bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.html.parser.HtmlTreeBuilder;
24f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedyimport com.google.common.collect.Maps;
25f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy
263d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yangimport android.app.ActivityManager;
27bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport android.app.Fragment;
2868f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereiraimport android.app.SearchManager;
296f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.Context;
308a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent;
3109400efa442422299acf21abe20e3470f9d965abScott Kennedyimport android.content.pm.PackageInfo;
3294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.content.pm.PackageManager.NameNotFoundException;
336f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.res.Resources;
34747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Weiimport android.content.res.TypedArray;
35ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.database.Cursor;
3683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport android.graphics.Bitmap;
376f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.graphics.Typeface;
388a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.net.Uri;
399f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrookimport android.os.AsyncTask;
40e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrookimport android.os.Build;
41ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.os.Bundle;
4294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.provider.Browser;
433e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spannable;
446f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableString;
456f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableStringBuilder;
463e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spanned;
473e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils;
483e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils.SimpleStringSplitter;
496f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.CharacterStyle;
506f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.ForegroundColorSpan;
516f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.StyleSpan;
52570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yangimport android.text.style.TextAppearanceSpan;
53747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Weiimport android.util.TypedValue;
54863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.Menu;
55863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.MenuItem;
56326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View;
57326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View.MeasureSpec;
58f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.view.ViewGroup;
5918264b956bedb710e94e809a7fa30ad096021c10Andy Huangimport android.view.ViewGroup.MarginLayoutParams;
6083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport android.view.Window;
618b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebSettings;
628b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebView;
638b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
6430e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
65a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport com.android.mail.browse.ConversationCursor;
66f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedyimport com.android.mail.compose.ComposeActivity;
67243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huangimport com.android.mail.perf.SimpleTimer;
688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account;
699ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereiraimport com.android.mail.providers.Conversation;
708a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder;
71ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.providers.UIProvider;
72e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrookimport com.android.mail.providers.UIProvider.EditSettingsExtras;
7383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport com.android.mail.ui.FeedbackEnabledActivity;
741591414ed6dc829852edd219c39ae430314ff538Scott Kennedyimport com.android.mail.ui.ViewMode;
753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
7692551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport org.json.JSONObject;
7792551d057965689f1952faeb06763f0762bc717fMindy Pereira
78bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.FileDescriptor;
79bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.PrintWriter;
80bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.StringWriter;
8194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport java.util.Locale;
823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport java.util.Map;
837b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
846f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereirapublic class Utils {
856f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    /**
866f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     * longest extension we recognize is 4 characters (e.g. "html", "docx")
876f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     */
886f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    private static final int FILE_EXTENSION_MAX_CHARS = 4;
893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    private static final Map<Integer, Integer> sPriorityToLength = Maps.newHashMap();
903e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_ELIDED = "e";
913e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_MESSAGES = "n";
923e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_DRAFTS = "d";
933e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_LITERAL = "l";
943e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SENDING = "s";
953e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SEND_FAILED = "f";
963e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final Character SENDER_LIST_SEPARATOR = '\n';
973e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final SimpleStringSplitter sSenderListSplitter = new SimpleStringSplitter(
983e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            SENDER_LIST_SEPARATOR);
993e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static String[] sSenderFragments = new String[8];
1008b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
1016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    public static final String EXTRA_ACCOUNT = "account";
102e144980ef9c8828401922373ef0dc203281bff6ePaul Westbrook    public static final String EXTRA_ACCOUNT_URI = "accountUri";
10391d0b86db3287f1702913177d347dd42b7d13764Marc Blank    public static final String EXTRA_FOLDER_URI = "folderUri";
10448cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy    public static final String EXTRA_FOLDER = "folder";
1057418e4b9942f291b8de8bc7b1b72a7ef7130a8b6Mindy Pereira    public static final String EXTRA_COMPOSE_URI = "composeUri";
106963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_CONVERSATION = "conversationUri";
1074fe0af81874976a1995191321e35c844b2229811Andy Huang    public static final String EXTRA_FROM_NOTIFICATION = "notification";
1087517e3b61b898a57f19be0671f70d58a82224643Andy Huang
109aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    private static final String MAILTO_SCHEME = "mailto";
110aeb799480408485a607fcdd356b759964a587c15Paul Westbrook
1117517e3b61b898a57f19be0671f70d58a82224643Andy Huang    /** Extra tag for debugging the blank fragment problem. */
1127517e3b61b898a57f19be0671f70d58a82224643Andy Huang    public static final String VIEW_DEBUGGING_TAG = "MailBlankFragment";
1137517e3b61b898a57f19be0671f70d58a82224643Andy Huang
1148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /*
1158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Notifies that changes happened. Certain UI components, e.g., widgets, can
1168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * register for this {@link Intent} and update accordingly. However, this
1178a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * can be very broad and is NOT the preferred way of getting notification.
1188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
1198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    // TODO: UI Provider has this notification URI?
12094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static final String ACTION_NOTIFY_DATASET_CHANGED =
12194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
12294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
12394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /** Parameter keys for context-aware help. */
12494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_HELP_LINK_PARAMETER_NAME = "p";
12594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
12694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_LINK_APP_VERSION = "version";
127fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy    private static int sVersionCode = -1;
12894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
12983e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    private static final int SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH = 600;
13083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook
13109400efa442422299acf21abe20e3470f9d965abScott Kennedy    private static final String APP_VERSION_QUERY_PARAMETER = "appVersion";
132eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy    private static final String FOLDER_URI_QUERY_PARAMETER = "folderUri";
13309400efa442422299acf21abe20e3470f9d965abScott Kennedy
134b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
1356349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
136243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang    public static final boolean ENABLE_CONV_LOAD_TIMER = false;
137243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang    public static final SimpleTimer sConvLoadTimer =
138243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang            new SimpleTimer(ENABLE_CONV_LOAD_TIMER).withSessionName("ConvLoadTimer");
139243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang
140747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    private static final int[] STYLE_ATTR = new int[] {android.R.attr.background};
141747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei
142e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    public static boolean isRunningJellybeanOrLater() {
143e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
144e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    }
145e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook
1464ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein    public static boolean isRunningKitkatOrLater() {
147a3f66e52c4eede8b340e1ae5f9b461e1d551ad2fAndrew Sapperstein        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
1484ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein    }
1494ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein
1502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1513d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     * @return Whether we are running on a low memory device.  This is used to disable certain
1523d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     * memory intensive features in the app.
1533d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     */
1543d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang    public static boolean isLowRamDevice(Context context) {
155a3f66e52c4eede8b340e1ae5f9b461e1d551ad2fAndrew Sapperstein        if (isRunningKitkatOrLater()) {
1563d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang            final ActivityManager am = (ActivityManager) context.getSystemService(
1573d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang                    Context.ACTIVITY_SERVICE);
1583d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang            return am.isLowRamDevice();
1593d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang        } else {
1603d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang            return false;
1613d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang        }
1623d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang    }
1633d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang
1643d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang    /**
1652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Sets WebView in a restricted mode suitable for email use.
1662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param webView The WebView to restrict
1682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void restrictWebView(WebView webView) {
1708b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        WebSettings webSettings = webView.getSettings();
1718b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSavePassword(false);
1728b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSaveFormData(false);
173fbe7839cee778f1493dab3219b2078518b35f22eAndrew Sapperstein        webSettings.setJavaScriptEnabled(false);
1748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSupportZoom(false);
1752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Format a plural string.
1792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param resource The identity of the resource, which must be a R.plurals
1812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param count The number of items.
1822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String formatPlural(Context context, int resource, int count) {
184041676628fb10321ef75680af536a580d607625fPaul Westbrook        final CharSequence formatString = context.getResources().getQuantityText(resource, count);
1852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return String.format(formatString.toString(), count);
1862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return an ellipsized String that's at most maxCharacters long. If the
1902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         text passed is longer, it will be abbreviated. If it contains a
1912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix, the ellipses will be inserted in the middle and the
1922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix will be preserved.
1932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ellipsize(String text, int maxCharacters) {
1952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int length = text.length();
1962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (length < maxCharacters)
1972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return text;
1982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int realMax = Math.min(maxCharacters, length);
2002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Preserve the suffix if any
2012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int index = text.lastIndexOf(".");
2022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String extension = "\u2026"; // "...";
2032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (index >= 0) {
2042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            // Limit the suffix to dot + four characters
2052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (length - index <= FILE_EXTENSION_MAX_CHARS + 1) {
2062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                extension = extension + text.substring(index + 1);
2072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
2082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        realMax -= extension.length();
2102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (realMax < 0)
2112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            realMax = 0;
2122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return text.substring(0, realMax) + extension;
2132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
2164ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * Ensures that the given string starts and ends with the double quote
2174ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * character. The string is not modified in any way except to add the double
2184ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * quote character to start and end if it's not already there. sample ->
2194ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * "sample" "sample" -> "sample" ""sample"" -> "sample"
2204ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * "sample"" -> "sample" sa"mp"le -> "sa"mp"le" "sa"mp"le" -> "sa"mp"le"
2214ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     * (empty string) -> "" " -> ""
2224ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira     */
2232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ensureQuotedString(String s) {
2242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (s == null) {
2252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return null;
2262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (!s.matches("^\".*\"$")) {
2282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return "\"" + s + "\"";
2292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
2302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return s;
2312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    // TODO: Move this to the UI Provider.
2352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sUnreadStyleSpan = null;
2362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sReadStyleSpan;
2372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharacterStyle sDraftsStyleSpan;
2382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sMeString;
2392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sDraftSingularString;
2402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sDraftPluralString;
2412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sSendingString;
2422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static CharSequence sSendFailedString;
2432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static int sMaxUnreadCount = -1;
245041676628fb10321ef75680af536a580d607625fPaul Westbrook    private static final CharacterStyle ACTION_BAR_UNREAD_STYLE = new StyleSpan(Typeface.BOLD);
2462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static String sUnreadText;
24755137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    private static int sDefaultFolderBackgroundColor = -1;
2484271bf0350442b594729d3074530bc0d8deac2a2mindyp    private static int sUseFolderListFragmentTransition = -1;
2492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void getStyledSenderSnippet(Context context, String senderInstructions,
2512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder,
2522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            int maxChars, boolean forceAllUnread, boolean forceAllRead, boolean allowDraft) {
2532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        Resources res = context.getResources();
2542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sUnreadStyleSpan == null) {
2552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sUnreadStyleSpan = new StyleSpan(Typeface.BOLD);
2562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sReadStyleSpan = new StyleSpan(Typeface.NORMAL);
2572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftsStyleSpan = new ForegroundColorSpan(res.getColor(R.color.drafts));
2582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
259d4de4aa5c73a9e68e1887c2b3de233acd0c51761Paul Westbrook            sMeString = context.getText(R.string.me_subject_pronun);
2602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftSingularString = res.getQuantityText(R.plurals.draft, 1);
2612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sDraftPluralString = res.getQuantityText(R.plurals.draft, 2);
2622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableString sendingString = new SpannableString(context.getText(R.string.sending));
2632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sendingString.setSpan(CharacterStyle.wrap(sDraftsStyleSpan), 0, sendingString.length(),
2642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    0);
2652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sSendingString = sendingString;
2662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sSendFailedString = context.getText(R.string.send_failed);
2672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        getSenderSnippet(senderInstructions, senderBuilder, statusBuilder, maxChars,
2702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadStyleSpan, sReadStyleSpan, sDraftsStyleSpan, sMeString,
2712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sDraftSingularString, sDraftPluralString, sSendingString, sSendFailedString,
2722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                forceAllUnread, forceAllRead, allowDraft);
2732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
2763e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Uses sender instructions to build a formatted string.
2773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Sender list instructions contain compact information about the sender
2793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * list. Most work that can be done without knowing how much room will be
2803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * availble for the sender list is done when creating the instructions.
2813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
2823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * The instructions string consists of tokens separated by
2833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * SENDER_LIST_SEPARATOR. Here are the tokens, one per line:
2843e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <ul>
2853e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>n</tt></li>
2863e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the number of non-draft messages in the conversation</li>
2873e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>d</tt</li>
2883e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the number of drafts in the conversation</li>
2893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>l</tt></li>
2903e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>literal html to be included in the output</em></li>
2913e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>s</tt> indicates that the message is sending (in the outbox
2923e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * without errors)</li>
2933e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>f</tt> indicates that the message failed to send (in the outbox
2943e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * with errors)</li>
2953e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>for each message</em>
2963e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <ul>
2973e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, 0 for read, 1 for unread</li>
2983e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>int</em>, the priority of the message. Zero is the most important
2993e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </li>
3003e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><em>text</em>, the sender text or blank for messages from 'me'</li>
3013e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </ul>
3023e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * </li>
3033e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <li><tt>e</tt> to indicate that one or more messages have been elided</li>
3043e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * <p>
3053e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * The instructions indicate how many messages and drafts are in the
3063e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * conversation and then describe the most important messages in order,
3073e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * indicating the priority of each message and whether the message is
3083e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * unread.
309f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
3103e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param instructions instructions as described above
3113e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param senderBuilder the SpannableStringBuilder to append to for sender
3123e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            information
3133e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param statusBuilder the SpannableStringBuilder to append to for status
3143e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param maxChars the number of characters available to display the text
3153e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param unreadStyle the CharacterStyle for unread messages, or null
3163e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftsStyle the CharacterStyle for draft messages, or null
3173e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param sendingString the string to use when there are messages scheduled
3183e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            to be sent
3193e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param sendFailedString the string to use when there are messages that
3203e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            mailed to send
3213e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param meString the string to use for messages sent by this user
3223e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftString the string to use for "Draft"
3233e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param draftPluralString the string to use for "Drafts"
3243e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     */
3252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static synchronized void getSenderSnippet(String instructions,
3262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder,
3272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            int maxChars, CharacterStyle unreadStyle, CharacterStyle readStyle,
3282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharacterStyle draftsStyle, CharSequence meString, CharSequence draftString,
3292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharSequence draftPluralString, CharSequence sendingString,
3302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            CharSequence sendFailedString, boolean forceAllUnread, boolean forceAllRead,
3312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            boolean allowDraft) {
3322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        assert !(forceAllUnread && forceAllRead);
3332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        boolean unreadStatusIsForced = forceAllUnread || forceAllRead;
3342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        boolean forcedUnreadStatus = forceAllUnread;
3352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Measure each fragment. It's ok to iterate over the entire set of
3372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // fragments because it is
3382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // never a long list, even if there are many senders.
3392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        final Map<Integer, Integer> priorityToLength = sPriorityToLength;
3402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        priorityToLength.clear();
3412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int maxFoundPriority = Integer.MIN_VALUE;
3432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numMessages = 0;
3442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numDrafts = 0;
3452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence draftsFragment = "";
3462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence sendingFragment = "";
3472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharSequence sendFailedFragment = "";
3482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        sSenderListSplitter.setString(instructions);
3502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numFragments = 0;
3512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String[] fragments = sSenderFragments;
3522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int currentSize = fragments.length;
3532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        while (sSenderListSplitter.hasNext()) {
3542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fragments[numFragments++] = sSenderListSplitter.next();
3552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (numFragments == currentSize) {
3562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sSenderFragments = new String[2 * currentSize];
3572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                System.arraycopy(fragments, 0, sSenderFragments, 0, currentSize);
3582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                currentSize *= 2;
3592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fragments = sSenderFragments;
3602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        for (int i = 0; i < numFragments;) {
3642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            String fragment0 = fragments[i++];
3652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if ("".equals(fragment0)) {
3662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // This should be the final fragment.
3672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) {
3682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // ignore
3692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) {
3702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numMessages = Integer.valueOf(fragments[i++]);
3712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) {
3722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String numDraftsString = fragments[i++];
3732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numDrafts = Integer.parseInt(numDraftsString);
3742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                draftsFragment = numDrafts == 1 ? draftString : draftPluralString + " ("
3752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        + numDraftsString + ")";
3762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_LITERAL.equals(fragment0)) {
377c59dd8270215f97d39697dae9e69976bcfe3642bmindyp                senderBuilder.append(Utils.convertHtmlToPlainText(fragments[i++]));
3782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                return;
3792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) {
3802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sendingFragment = sendingString;
3812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) {
3822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sendFailedFragment = sendFailedString;
3832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else {
3842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String priorityString = fragments[i++];
3852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                CharSequence nameString = fragments[i++];
3862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (nameString.length() == 0)
3872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = meString;
3882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                int priority = Integer.parseInt(priorityString);
3892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                priorityToLength.put(priority, nameString.length());
3902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                maxFoundPriority = Math.max(maxFoundPriority, priority);
3912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
3922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String numMessagesFragment = (numMessages != 0) ? " \u00A0"
3942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                + Integer.toString(numMessages + numDrafts) : "";
3952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Don't allocate fixedFragment unless we need it
3972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        SpannableStringBuilder fixedFragment = null;
3982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int fixedFragmentLength = 0;
3992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (draftsFragment.length() != 0 && allowDraft) {
4003b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy            fixedFragment = new SpannableStringBuilder();
4012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(draftsFragment);
4022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (draftsStyle != null) {
4032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.setSpan(CharacterStyle.wrap(draftsStyle), 0, fixedFragment.length(),
4042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
4052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
4062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sendingFragment.length() != 0) {
4082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
4092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
4102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
4112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment.length() != 0)
4122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.append(", ");
4132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(sendingFragment);
4142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sendFailedFragment.length() != 0) {
4162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment == null) {
4172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment = new SpannableStringBuilder();
4182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
4192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (fixedFragment.length() != 0)
4202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                fixedFragment.append(", ");
4212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragment.append(sendFailedFragment);
4222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (fixedFragment != null) {
4252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            fixedFragmentLength = fixedFragment.length();
4262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        maxChars -= fixedFragmentLength;
4282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int maxPriorityToInclude = -1; // inclusive
4302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numCharsUsed = numMessagesFragment.length();
4312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numSendersUsed = 0;
4322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        while (maxPriorityToInclude < maxFoundPriority) {
4332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (priorityToLength.containsKey(maxPriorityToInclude + 1)) {
4342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                int length = numCharsUsed + priorityToLength.get(maxPriorityToInclude + 1);
4352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (numCharsUsed > 0)
4362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    length += 2;
4372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // We must show at least two senders if they exist. If we don't
4382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // have space for both
4392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // then we will truncate names.
4402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (length > maxChars && numSendersUsed >= 2) {
4412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    break;
4422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numCharsUsed = length;
4442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                numSendersUsed++;
4452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
4462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            maxPriorityToInclude++;
4472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int numCharsToRemovePerWord = 0;
4502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (numCharsUsed > maxChars) {
4512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            numCharsToRemovePerWord = (numCharsUsed - maxChars) / numSendersUsed;
4522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String lastFragment = null;
4552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        CharacterStyle lastStyle = null;
4562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        for (int i = 0; i < numFragments;) {
4572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            String fragment0 = fragments[i++];
4582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if ("".equals(fragment0)) {
4592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                // This should be the final fragment.
4602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) {
4612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (lastFragment != null) {
4622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
4632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    senderBuilder.append(" ");
4642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    addStyledFragment(senderBuilder, "..", lastStyle, true);
4652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    senderBuilder.append(" ");
4662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                lastFragment = null;
4682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) {
4692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                i++;
4702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) {
4712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                i++;
4722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) {
4732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) {
4742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            } else {
4752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final String unreadString = fragment0;
4762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final String priorityString = fragments[i++];
4772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                String nameString = fragments[i++];
4782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (nameString.length() == 0) {
4792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = meString.toString();
4802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                } else {
481c59dd8270215f97d39697dae9e69976bcfe3642bmindyp                    nameString = Utils.convertHtmlToPlainText(nameString).toString();
4822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (numCharsToRemovePerWord != 0) {
4842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    nameString = nameString.substring(0,
4852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                            Math.max(nameString.length() - numCharsToRemovePerWord, 0));
4862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
4872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final boolean unread = unreadStatusIsForced ? forcedUnreadStatus : Integer
4882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        .parseInt(unreadString) != 0;
4892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                final int priority = Integer.parseInt(priorityString);
4902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                if (priority <= maxPriorityToInclude) {
4912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    if (lastFragment != null && !lastFragment.equals(nameString)) {
4922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, lastFragment.concat(","), lastStyle,
4932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                                false);
4942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
4952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    }
4962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastFragment = nameString;
4972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastStyle = unread ? unreadStyle : readStyle;
4982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                } else {
4992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    if (lastFragment != null) {
5002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
5012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // Adjacent spans can cause the TextView in Gmail widget
5022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // confused and leads to weird behavior on scrolling.
5032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // Our workaround here is to separate the spans by
5042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        // spaces.
5052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
5062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        addStyledFragment(senderBuilder, "..", lastStyle, true);
5072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                        senderBuilder.append(" ");
5082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    }
5092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                    lastFragment = null;
5102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                }
5112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
5122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
5132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (lastFragment != null) {
5142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            addStyledFragment(senderBuilder, lastFragment, lastStyle, false);
5152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
5162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        senderBuilder.append(numMessagesFragment);
5172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (fixedFragmentLength != 0) {
5182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            statusBuilder.append(fixedFragment);
5192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
5202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5216f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira
5223e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    /**
5233e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * Adds a fragment with given style to a string builder.
524f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
5253e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param builder the current string builder
5263e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param fragment the fragment to be added
5273e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param style the style of the fragment
5283e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     * @param withSpaces whether to add the whole fragment or to divide it into
5293e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     *            smaller ones
5303e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira     */
5313e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    private static void addStyledFragment(SpannableStringBuilder builder, String fragment,
5323e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            CharacterStyle style, boolean withSpaces) {
5333e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        if (withSpaces) {
5343e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            int pos = builder.length();
5353e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            builder.append(fragment);
5363e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            builder.setSpan(CharacterStyle.wrap(style), pos, builder.length(),
5373e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
5383e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        } else {
5393e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            int start = 0;
5403e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            while (true) {
5413e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                int pos = fragment.substring(start).indexOf(' ');
5423e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                if (pos == -1) {
5433e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    addStyledFragment(builder, fragment.substring(start), style, true);
5443e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    break;
5453e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                } else {
5463e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    pos += start;
5473e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    if (start < pos) {
5483e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        addStyledFragment(builder, fragment.substring(start, pos), style, true);
5493e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        builder.append(' ');
5503e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    }
5513e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    start = pos + 1;
5523e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    if (start >= fragment.length()) {
5533e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                        break;
5543e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                    }
5553e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira                }
5563e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira            }
5573e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira        }
5583e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    }
5593e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
5602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
5612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Returns a boolean indicating whether the table UI should be shown.
5622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
563bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    public static boolean useTabletUI(Resources res) {
56451b6f566984a94f146b4d13b3157bb036bbb82bcAndrew Sapperstein        return res.getBoolean(R.bool.use_tablet_ui);
5652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
5662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
5674271bf0350442b594729d3074530bc0d8deac2a2mindyp    /**
5681591414ed6dc829852edd219c39ae430314ff538Scott Kennedy     * @return <code>true</code> if the right edge effect should be displayed on list items
5691591414ed6dc829852edd219c39ae430314ff538Scott Kennedy     */
5701591414ed6dc829852edd219c39ae430314ff538Scott Kennedy    public static boolean getDisplayListRightEdgeEffect(final boolean tabletDevice,
5711591414ed6dc829852edd219c39ae430314ff538Scott Kennedy            final boolean listCollapsible, final int viewMode) {
5721591414ed6dc829852edd219c39ae430314ff538Scott Kennedy        return tabletDevice && !listCollapsible
5731591414ed6dc829852edd219c39ae430314ff538Scott Kennedy                && (ViewMode.isConversationMode(viewMode) || ViewMode.isAdMode(viewMode));
5741591414ed6dc829852edd219c39ae430314ff538Scott Kennedy    }
5751591414ed6dc829852edd219c39ae430314ff538Scott Kennedy
5761591414ed6dc829852edd219c39ae430314ff538Scott Kennedy    /**
5774271bf0350442b594729d3074530bc0d8deac2a2mindyp     * Returns a boolean indicating whether or not we should animate in the
5784271bf0350442b594729d3074530bc0d8deac2a2mindyp     * folder list fragment.
5794271bf0350442b594729d3074530bc0d8deac2a2mindyp     */
5804271bf0350442b594729d3074530bc0d8deac2a2mindyp    public static boolean useFolderListFragmentTransition(Context context) {
5814271bf0350442b594729d3074530bc0d8deac2a2mindyp        if (sUseFolderListFragmentTransition == -1) {
5824271bf0350442b594729d3074530bc0d8deac2a2mindyp            sUseFolderListFragmentTransition  = context.getResources().getInteger(
5834271bf0350442b594729d3074530bc0d8deac2a2mindyp                    R.integer.use_folder_list_fragment_transition);
5844271bf0350442b594729d3074530bc0d8deac2a2mindyp        }
5854271bf0350442b594729d3074530bc0d8deac2a2mindyp        return sUseFolderListFragmentTransition != 0;
5864271bf0350442b594729d3074530bc0d8deac2a2mindyp    }
5874271bf0350442b594729d3074530bc0d8deac2a2mindyp
5882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
58995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns displayable text from the provided HTML string.
59095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @param htmlText HTML string
59195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @return Plain text string representation of the specified Html string
59295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
59395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static String convertHtmlToPlainText(String htmlText) {
5949cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        if (TextUtils.isEmpty(htmlText)) {
5959cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook            return "";
5969cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        }
597cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder()).getPlainText();
598cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
599cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
600cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    public static String convertHtmlToPlainText(String htmlText, HtmlParser parser,
601cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
6029cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        if (TextUtils.isEmpty(htmlText)) {
6039cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook            return "";
6049cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        }
605cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, parser, builder).getPlainText();
60695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
60795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
60895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
60995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns a {@link HtmlTree} representation of the specified HTML string.
61095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
61195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static HtmlTree getHtmlTree(String htmlText) {
612cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder());
613cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
614cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
615cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    /**
616cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     * Returns a {@link HtmlTree} representation of the specified HTML string.
617cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     */
6189cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook    private static HtmlTree getHtmlTree(String htmlText, HtmlParser parser,
619cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
62095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlDocument doc = parser.parse(htmlText);
62195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        doc.accept(builder);
62295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
62395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        return builder.getTree();
62495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
62595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
62695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
6272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Perform a simulated measure pass on the given child view, assuming the
6282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * child has a ViewGroup parent and that it should be laid out within that
6292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * parent with a matching width but variable height. Code largely lifted
6302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * from AnimatedAdapter.measureChildHeight().
631f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
6322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param child a child view that has already been placed within its parent
6332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *            ViewGroup
6342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param parent the parent ViewGroup of child
6352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return measured height of the child in px
6362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
6372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static int measureViewHeight(View child, ViewGroup parent) {
63818264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final ViewGroup.LayoutParams lp = child.getLayoutParams();
63918264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int childSideMargin;
64018264b956bedb710e94e809a7fa30ad096021c10Andy Huang        if (lp instanceof MarginLayoutParams) {
64118264b956bedb710e94e809a7fa30ad096021c10Andy Huang            final MarginLayoutParams mlp = (MarginLayoutParams) lp;
64218264b956bedb710e94e809a7fa30ad096021c10Andy Huang            childSideMargin = mlp.leftMargin + mlp.rightMargin;
64318264b956bedb710e94e809a7fa30ad096021c10Andy Huang        } else {
64418264b956bedb710e94e809a7fa30ad096021c10Andy Huang            childSideMargin = 0;
64518264b956bedb710e94e809a7fa30ad096021c10Andy Huang        }
64618264b956bedb710e94e809a7fa30ad096021c10Andy Huang
64718264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int parentWSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY);
64818264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int wSpec = ViewGroup.getChildMeasureSpec(parentWSpec,
64918264b956bedb710e94e809a7fa30ad096021c10Andy Huang                parent.getPaddingLeft() + parent.getPaddingRight() + childSideMargin,
6502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                ViewGroup.LayoutParams.MATCH_PARENT);
65118264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int hSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
6522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        child.measure(wSpec, hSpec);
6532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return child.getMeasuredHeight();
6542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
655326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
65646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
65746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Encode the string in HTML.
65846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
65946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param removeEmptyDoubleQuotes If true, also remove any occurrence of ""
66046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *            found in the string
66146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
6622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static Object cleanUpString(String string, boolean removeEmptyDoubleQuotes) {
6632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return !TextUtils.isEmpty(string) ? TextUtils.htmlEncode(removeEmptyDoubleQuotes ? string
6642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                .replace("\"\"", "") : string) : "";
6652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
6662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
6672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
6682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Get the correct display string for the unread count of a folder.
6692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
6702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String getUnreadCountString(Context context, int unreadCount) {
67175d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final String unreadCountString;
67275d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final Resources resources = context.getResources();
6732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sMaxUnreadCount == -1) {
6742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount);
6752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
6762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (unreadCount > sMaxUnreadCount) {
6772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (sUnreadText == null) {
6782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadText = resources.getString(R.string.widget_large_unread_count);
6792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
680d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            // Localize "999+" according to the device language
6812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = String.format(sUnreadText, sMaxUnreadCount);
6822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else if (unreadCount <= 0) {
6832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = "";
6842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
685d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            // Localize unread count according to the device language
686d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            unreadCountString = String.format("%d", unreadCount);
6872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
6882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return unreadCountString;
6892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
69028beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira
69128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    /**
69275d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal     * Get the correct display string for the unread count in the actionbar.
69375d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal     */
694041676628fb10321ef75680af536a580d607625fPaul Westbrook    public static CharSequence getUnreadMessageString(Context context, int unreadCount) {
695041676628fb10321ef75680af536a580d607625fPaul Westbrook        final SpannableString message;
69675d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final Resources resources = context.getResources();
69775d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        if (sMaxUnreadCount == -1) {
69875d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal            sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount);
69975d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        }
70075d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        if (unreadCount > sMaxUnreadCount) {
701b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler            message = new SpannableString(
702b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler                    resources.getString(R.string.actionbar_large_unread_count, sMaxUnreadCount));
70375d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        } else {
704b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler             message = new SpannableString(resources.getQuantityString(
705b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler                     R.plurals.actionbar_unread_messages, unreadCount, unreadCount));
70675d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        }
707041676628fb10321ef75680af536a580d607625fPaul Westbrook
708041676628fb10321ef75680af536a580d607625fPaul Westbrook        message.setSpan(CharacterStyle.wrap(ACTION_BAR_UNREAD_STYLE), 0,
709041676628fb10321ef75680af536a580d607625fPaul Westbrook                message.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
710041676628fb10321ef75680af536a580d607625fPaul Westbrook
71175d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        return message;
71275d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal    }
71375d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal
71475d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal    /**
71528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     * Get text matching the last sync status.
71628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     */
71741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal    public static CharSequence getSyncStatusText(Context context, int packedStatus) {
71817beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final String[] errors = context.getResources().getStringArray(R.array.sync_status);
71941b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final int status = packedStatus & 0x0f;
72028beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        if (status >= errors.length) {
72128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira            return "";
72228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        }
72328beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        return errors[status];
72428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    }
7258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
7268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
7279ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to show a conversation.
7289ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @param conversation Conversation to open.
7295c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein     * @param folderUri
730161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param account
7319ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @return
7329ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     */
73309400efa442422299acf21abe20e3470f9d965abScott Kennedy    public static Intent createViewConversationIntent(final Context context,
734b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            Conversation conversation, final Uri folderUri, Account account) {
7359ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
73609400efa442422299acf21abe20e3470f9d965abScott Kennedy        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
73709400efa442422299acf21abe20e3470f9d965abScott Kennedy                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
738eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        final Uri versionedUri = appendVersionQueryParameter(context, conversation.uri);
739eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        // We need the URI to be unique, even if it's for the same message, so append the folder URI
740eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        final Uri uniqueUri = versionedUri.buildUpon().appendQueryParameter(
741eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy                FOLDER_URI_QUERY_PARAMETER, folderUri.toString()).build();
742eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        intent.setDataAndType(uniqueUri, account.mimeType);
7435ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
744b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(Utils.EXTRA_FOLDER_URI, folderUri);
745963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira        intent.putExtra(Utils.EXTRA_CONVERSATION, conversation);
7469ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
7479ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
7489ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
7499ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
7509ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to open a folder.
751c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira     *
7525c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein     * @param folderUri Folder to open.
7530c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
754daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @return
755daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     */
756b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy    public static Intent createViewFolderIntent(final Context context, final Uri folderUri,
75709400efa442422299acf21abe20e3470f9d965abScott Kennedy            Account account) {
758b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        if (folderUri == null || account == null) {
759b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            LogUtils.wtf(LOG_TAG, "Utils.createViewFolderIntent(%s,%s): Bad input", folderUri,
760b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                    account);
7611672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return null;
7621672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
7639ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
7645ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
7655ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
766b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.setDataAndType(appendVersionQueryParameter(context, folderUri), account.mimeType);
7675ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
768b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(Utils.EXTRA_FOLDER_URI, folderUri);
7699ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
7709ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
7719ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
7729ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
7730c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * Creates an intent to open the default inbox for the given account.
7740c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     *
7750c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
7760c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @return
7770c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     */
7780c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    public static Intent createViewInboxIntent(Account account) {
7790c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        if (account == null) {
7800c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            LogUtils.wtf(LOG_TAG, "Utils.createViewInboxIntent(%s): Bad input", account);
7810c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            return null;
7820c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        }
7830c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        final Intent intent = new Intent(Intent.ACTION_VIEW);
7840c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
7850c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
7860c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setDataAndType(account.settings.defaultInbox, account.mimeType);
7870c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
7880c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        return intent;
7890c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    }
7900c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal
7910c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    /**
79294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to show context-aware Gmail help.
79394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
79494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context to be used to open the help.
79594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param fromWhere Information about the activity the user was in
79694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * when they requested help.
79794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
798498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook    public static void showHelp(Context context, Account account, String fromWhere) {
799c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook        final String urlString = (account != null && account.helpIntentUri != null) ?
800c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook                account.helpIntentUri.toString() : null;
801c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook        if (TextUtils.isEmpty(urlString) ) {
802498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            LogUtils.e(LOG_TAG, "unable to show help for account: %s", account);
803498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            return;
804498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        }
805c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook        final Uri uri = addParamsToUrl(context, urlString);
80694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Uri.Builder builder = uri.buildUpon();
80794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        // Add the activity specific information parameter.
80830745b6e9c4d280ac578d1186fbc0cec9bd2a838Paul Westbrook        if (!TextUtils.isEmpty(fromWhere)) {
80994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            builder = builder.appendQueryParameter(SMART_HELP_LINK_PARAMETER_NAME, fromWhere);
81094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
81194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
81217beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        openUrl(context, builder.build(), null);
81394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
81494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
81594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
81694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to open a link in a browser.
81794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
81894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context
81994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param uri Uri to open.
82094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
82117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook    private static void openUrl(Context context, Uri uri, Bundle optionalExtras) {
82294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if(uri == null || TextUtils.isEmpty(uri.toString())) {
82394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            LogUtils.wtf(LOG_TAG, "invalid url in Utils.openUrl(): %s", uri);
82494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            return;
82594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
82617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
82717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        // Fill in any of extras that have been requested.
82817beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        if (optionalExtras != null) {
82917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            intent.putExtras(optionalExtras);
83017beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        }
83194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
8326f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
83317beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook
83494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        context.startActivity(intent);
83594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
83694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
83794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
83894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static Uri addParamsToUrl(Context context, String url) {
83994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        url = replaceLocale(url);
84094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        Uri.Builder builder = Uri.parse(url).buildUpon();
841fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy        final int versionCode = getVersionCode(context);
842fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy        if (versionCode != -1) {
843fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy            builder = builder.appendQueryParameter(SMART_LINK_APP_VERSION,
844fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy                    String.valueOf(versionCode));
84594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
84694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
84794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return builder.build();
84894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
84994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
85094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
85194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Replaces the language/country of the device into the given string.  The pattern "%locale%"
85294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * will be replaced with the <language_code>_<country_code> value.
85394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
85494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param str the string to replace the language/country within
85594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
85694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @return the string with replacement
85794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
85894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static String replaceLocale(String str) {
85994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        // Substitute locale if present in string
86094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if (str.contains("%locale%")) {
86194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            Locale locale = Locale.getDefault();
86294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            String tmp = locale.getLanguage() + "_" + locale.getCountry().toLowerCase();
86394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            str = str.replace("%locale%", tmp);
86494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
86594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return str;
86694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
86794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
86894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
869fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy     * Returns the version code for the package, or -1 if it cannot be retrieved.
87094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
871fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy    public static int getVersionCode(Context context) {
872fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy        if (sVersionCode == -1) {
87394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            try {
874fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy                sVersionCode =
875fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy                        context.getPackageManager().getPackageInfo(context.getPackageName(),
876fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy                                0 /* flags */).versionCode;
87794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            } catch (NameNotFoundException e) {
87894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                LogUtils.e(Utils.LOG_TAG, "Error finding package %s",
87994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook                        context.getApplicationInfo().packageName);
88094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            }
88194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
88294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        return sVersionCode;
88394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
8841f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira
8851f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    /**
8860e627fd594f3809e2bede76379a3348267185196Alice Yang     * Show the top level settings screen for the supplied account.
8871f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     */
8881f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    public static void showSettings(Context context, Account account) {
88961400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        if (account == null) {
89061400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
89161400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            return;
89261400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        }
8931f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
8946f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
8950e627fd594f3809e2bede76379a3348267185196Alice Yang        context.startActivity(settingsIntent);
8960e627fd594f3809e2bede76379a3348267185196Alice Yang    }
8970e627fd594f3809e2bede76379a3348267185196Alice Yang
8980e627fd594f3809e2bede76379a3348267185196Alice Yang    /**
8990e627fd594f3809e2bede76379a3348267185196Alice Yang     * Show the account level settings screen for the supplied account.
9000e627fd594f3809e2bede76379a3348267185196Alice Yang     */
9010e627fd594f3809e2bede76379a3348267185196Alice Yang    public static void showAccountSettings(Context context, Account account) {
9020e627fd594f3809e2bede76379a3348267185196Alice Yang        if (account == null) {
9030e627fd594f3809e2bede76379a3348267185196Alice Yang            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
9040e627fd594f3809e2bede76379a3348267185196Alice Yang            return;
9050e627fd594f3809e2bede76379a3348267185196Alice Yang        }
9060e627fd594f3809e2bede76379a3348267185196Alice Yang        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT,
9070e627fd594f3809e2bede76379a3348267185196Alice Yang                appendVersionQueryParameter(context, account.settingsIntentUri));
9080e627fd594f3809e2bede76379a3348267185196Alice Yang
9090e627fd594f3809e2bede76379a3348267185196Alice Yang        settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
9100e627fd594f3809e2bede76379a3348267185196Alice Yang        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
9111f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        context.startActivity(settingsIntent);
9121f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    }
91368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira
91468f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    /**
915e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     * Show the settings screen for the supplied account.
916e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     */
917e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     public static void showFolderSettings(Context context, Account account, Folder folder) {
91809400efa442422299acf21abe20e3470f9d965abScott Kennedy        if (account == null || folder == null) {
91909400efa442422299acf21abe20e3470f9d965abScott Kennedy            LogUtils.e(LOG_TAG, "Invalid attempt to show folder settings. account: %s folder: %s",
92009400efa442422299acf21abe20e3470f9d965abScott Kennedy                    account, folder);
92109400efa442422299acf21abe20e3470f9d965abScott Kennedy            return;
92209400efa442422299acf21abe20e3470f9d965abScott Kennedy        }
92309400efa442422299acf21abe20e3470f9d965abScott Kennedy        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT,
92409400efa442422299acf21abe20e3470f9d965abScott Kennedy                appendVersionQueryParameter(context, account.settingsIntentUri));
925e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
92609400efa442422299acf21abe20e3470f9d965abScott Kennedy        settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
92709400efa442422299acf21abe20e3470f9d965abScott Kennedy        settingsIntent.putExtra(EditSettingsExtras.EXTRA_FOLDER, folder);
92809400efa442422299acf21abe20e3470f9d965abScott Kennedy        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
92909400efa442422299acf21abe20e3470f9d965abScott Kennedy        context.startActivity(settingsIntent);
930e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    }
931e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
932e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    /**
93318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     * Show the settings screen for managing all folders.
93418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     */
93518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     public static void showManageFolder(Context context, Account account) {
93618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         if (account == null) {
93718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             LogUtils.e(LOG_TAG, "Invalid attempt to the manage folders screen with null account");
93818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             return;
93918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         }
94018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
94118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
94218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
94318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_MANAGE_FOLDERS, true);
9446f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook         settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
94518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         context.startActivity(settingsIntent);
94618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    }
94718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
94818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    /**
949fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Show the feedback screen for the supplied account.
950fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
95183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    public static void sendFeedback(FeedbackEnabledActivity activity, Account account,
9520a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook                                    boolean reportingProblem) {
9530a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        if (activity != null && account != null) {
9540a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook            sendFeedback(activity, account.sendFeedbackIntentUri, reportingProblem);
9550a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        }
9560a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook    }
9570a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook    public static void sendFeedback(FeedbackEnabledActivity activity, Uri feedbackIntentUri,
95883e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            boolean reportingProblem) {
9590a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        if (activity != null &&  !isEmpty(feedbackIntentUri)) {
96083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            final Bundle optionalExtras = new Bundle(2);
96117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            optionalExtras.putBoolean(
96217beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook                    UIProvider.SendFeedbackExtras.EXTRA_REPORTING_PROBLEM, reportingProblem);
96383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            final Bitmap screenBitmap =  getReducedSizeBitmap(activity);
96483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            if (screenBitmap != null) {
96583e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                optionalExtras.putParcelable(
96683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                        UIProvider.SendFeedbackExtras.EXTRA_SCREEN_SHOT, screenBitmap);
96783e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            }
9680a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook            openUrl(activity.getActivityContext(), feedbackIntentUri, optionalExtras);
9693ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
970fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
971fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
9720a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook
97383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    public static Bitmap getReducedSizeBitmap(FeedbackEnabledActivity activity) {
97483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final Window activityWindow = activity.getWindow();
97583e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final View currentView = activityWindow != null ? activityWindow.getDecorView() : null;
97683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final View rootView = currentView != null ? currentView.getRootView() : null;
97783e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        if (rootView != null) {
97883e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            rootView.setDrawingCacheEnabled(true);
979c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            final Bitmap drawingCache = rootView.getDrawingCache();
980c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            // Null check to avoid NPE discovered from monkey crash:
981c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            if (drawingCache != null) {
982d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                try {
983d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    final Bitmap originalBitmap = drawingCache.copy(Bitmap.Config.RGB_565, false);
984d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double originalHeight = originalBitmap.getHeight();
985d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double originalWidth = originalBitmap.getWidth();
986d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    int newHeight = SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH;
987d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    int newWidth = SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH;
988d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double scaleX, scaleY;
989d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    scaleX = newWidth  / originalWidth;
990d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    scaleY = newHeight / originalHeight;
991d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    final double scale = Math.min(scaleX, scaleY);
992d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    newWidth = (int)Math.round(originalWidth * scale);
993d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    newHeight = (int)Math.round(originalHeight * scale);
994d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    return Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true);
995d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                } catch (OutOfMemoryError e) {
996d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    LogUtils.e(LOG_TAG, e, "OOME when attempting to scale screenshot");
997d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                }
998c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            }
99983e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        }
100083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        return null;
100183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    }
100283e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook
1003fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
100468f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * Retrieves the mailbox search query associated with an intent (or null if not available),
100568f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * doing proper sanitizing (e.g. trims whitespace).
100668f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     */
100768f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    public static String mailSearchQueryForIntent(Intent intent) {
100868f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        String query = intent.getStringExtra(SearchManager.QUERY);
100968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        return TextUtils.isEmpty(query) ? null : query.trim();
101068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira   }
101188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
101288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    /**
101388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * Split out a filename's extension and return it.
101488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @param filename a file name
101588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @return the file extension (max of 5 chars including period, like ".docx"), or null
101688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     */
101796f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira    public static String getFileExtension(String filename) {
101888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        String extension = null;
101996f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira        int index = !TextUtils.isEmpty(filename) ? filename.lastIndexOf('.') : -1;
102088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        // Limit the suffix to dot + four characters
102188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        if (index >= 0 && filename.length() - index <= FILE_EXTENSION_MAX_CHARS + 1) {
102288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang            extension = filename.substring(index);
102388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        }
102488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        return extension;
102588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    }
102688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
102788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
102888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Intent#normalizeMimeType(String)} for pre-J)
102988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
10308750066b10f80e2a8080016973b3296d76d18266Mark Wei    * Normalize a MIME data type.
103188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
103288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized MIME type has white-space trimmed,
103388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * content-type parameters removed, and is lower-case.
103488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the type with Android best practices for
103588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
103688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
103788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "text/plain; charset=utf-8" becomes "text/plain".
103888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "text/x-vCard" becomes "text/x-vcard".
103988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
104088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All MIME types received from outside Android (such as user input,
104188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
104288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
104388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
104488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @param type MIME data type to normalize
104588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized MIME data type, or null if the input was null
10465c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setType}
10475c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setTypeAndNormalize}
104888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
10498750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static String normalizeMimeType(String type) {
105088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (type == null) {
105188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           return null;
105288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
105388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
105488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       type = type.trim().toLowerCase(Locale.US);
105588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
105688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       final int semicolonIndex = type.indexOf(';');
105788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (semicolonIndex != -1) {
105888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           type = type.substring(0, semicolonIndex);
105988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
10608750066b10f80e2a8080016973b3296d76d18266Mark Wei       return type;
106188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
106288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
106388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
10645c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * (copied from {@link android.net.Uri#normalizeScheme()} for pre-J)
106588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
106688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Return a normalized representation of this Uri.
106788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
106888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized Uri has a lowercase scheme component.
106988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the Uri with Android best practices for
107088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
107188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
107288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "HTTP://www.android.com" becomes
107388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "http://www.android.com"
107488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
107588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All URIs received from outside Android (such as user input,
107688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
107788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
107888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
107988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p class="note">This method does <em>not</em> validate bad URI's,
108088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or 'fix' poorly formatted URI's - so do not use it for input validation.
108188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * A Uri will always be returned, even if the Uri is badly formatted to
108288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * begin with and a scheme component cannot be found.
108388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
108488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized Uri (never null)
108588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link android.content.Intent#setData}
10865c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setNormalizedData}
108788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
108888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Uri normalizeUri(Uri uri) {
108988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String scheme = uri.getScheme();
109088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme == null) return uri;  // give up
109188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String lowerScheme = scheme.toLowerCase(Locale.US);
109288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme.equals(lowerScheme)) return uri;  // no change
109388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
109488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return uri.buildUpon().scheme(lowerScheme).build();
109588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
109688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
10978750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static Intent setIntentTypeAndNormalize(Intent intent, String type) {
10988750066b10f80e2a8080016973b3296d76d18266Mark Wei       return intent.setType(normalizeMimeType(type));
109988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
110088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
11018750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static Intent setIntentDataAndTypeAndNormalize(Intent intent, Uri data, String type) {
11028750066b10f80e2a8080016973b3296d76d18266Mark Wei       return intent.setDataAndType(normalizeUri(data), normalizeMimeType(type));
110388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
110488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
1105b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   public static int getTransparentColor(int color) {
1106b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira       return 0x00ffffff & color;
1107b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   }
1108863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira
1109863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    public static void setMenuItemVisibility(Menu menu, int itemId, boolean shouldShow) {
1110863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        final MenuItem item = menu.findItem(itemId);
1111863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        if (item == null) {
1112863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira            return;
1113863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        }
1114863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        item.setVisible(shouldShow);
1115863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    }
111692551d057965689f1952faeb06763f0762bc717fMindy Pereira
111792551d057965689f1952faeb06763f0762bc717fMindy Pereira    /**
111892551d057965689f1952faeb06763f0762bc717fMindy Pereira     * Parse a string (possibly null or empty) into a URI. If the string is null
111992551d057965689f1952faeb06763f0762bc717fMindy Pereira     * or empty, null is returned back. Otherwise an empty URI is returned.
112092551d057965689f1952faeb06763f0762bc717fMindy Pereira     *
112192551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @param uri
112292551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
112392551d057965689f1952faeb06763f0762bc717fMindy Pereira     */
112492551d057965689f1952faeb06763f0762bc717fMindy Pereira    public static Uri getValidUri(String uri) {
1125b378d64bab3c7517794ad7e2aee1d06c074e99eeMindy Pereira        if (TextUtils.isEmpty(uri) || uri == JSONObject.NULL)
112692551d057965689f1952faeb06763f0762bc717fMindy Pereira            return Uri.EMPTY;
112792551d057965689f1952faeb06763f0762bc717fMindy Pereira        return Uri.parse(uri);
112892551d057965689f1952faeb06763f0762bc717fMindy Pereira    }
112910ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank
113010ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    public static boolean isEmpty(Uri uri) {
113110ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank        return uri == null || uri.equals(Uri.EMPTY);
113210ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    }
1133ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
1134bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang    public static String dumpFragment(Fragment f) {
1135bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        final StringWriter sw = new StringWriter();
1136bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        f.dump("", new FileDescriptor(), new PrintWriter(sw), new String[0]);
1137bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        return sw.toString();
1138bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang    }
1139bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang
1140fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    public static void dumpViewTree(ViewGroup root) {
1141fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        dumpViewTree(root, "");
1142fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    }
1143fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang
1144fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    private static void dumpViewTree(ViewGroup g, String prefix) {
1145fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        LogUtils.i(LOG_TAG, "%sVIEWGROUP: %s childCount=%s", prefix, g, g.getChildCount());
1146fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        final String childPrefix = prefix + "  ";
1147fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        for (int i = 0; i < g.getChildCount(); i++) {
1148fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            final View child = g.getChildAt(i);
1149fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            if (child instanceof ViewGroup) {
1150fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang                dumpViewTree((ViewGroup) child, childPrefix);
1151fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            } else {
1152fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang                LogUtils.i(LOG_TAG, "%sCHILD #%s: %s", childPrefix, i, child);
1153fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            }
1154fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        }
1155fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    }
1156fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang
115769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
115869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Executes an out-of-band command on the cursor.
115969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param cursor
116069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param request Bundle with all keys and values set for the command.
116169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param key The string value against which we will check for success or failure
116269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @return true if the operation was a success.
116369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
116469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    private static boolean executeConversationCursorCommand(
116569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, Bundle request, String key) {
116669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle response = cursor.respond(request);
116769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final String result = response.getString(key,
1168ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_FAILED);
1169ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
1170ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK.equals(result);
1171ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
1172ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
1173ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
1174983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * Commands a cursor representing a set of conversations to indicate that an item is being shown
1175983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * in the UI.
1176ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
1177ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
1178983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * @param position position of the item being shown.
1179ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
1180983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook    public static boolean notifyCursorUIPositionChange(Cursor cursor, int position) {
118169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle request = new Bundle();
1182983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook        final String key =
1183983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook                UIProvider.ConversationCursorCommand.COMMAND_NOTIFY_CURSOR_UI_POSITION_CHANGE;
1184983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook        request.putInt(key, position);
118569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        return executeConversationCursorCommand(cursor, request, key);
1186ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
11879f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
11889f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
11899f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * Commands a cursor representing a set of conversations to set its visibility state.
11909f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     *
11919f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * @param cursor a conversation cursor
119269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param visible true if the conversation list is visible, false otherwise.
119369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param isFirstSeen true if you want to notify the cursor that this conversation list was seen
119469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        for the first time: the user launched the app into it, or the user switched from some
119569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        other folder into it.
11969f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
119769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    public static void setConversationCursorVisibility(
119869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, boolean visible, boolean isFirstSeen) {
119969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        new MarkConversationCursorVisibleTask(cursor, visible, isFirstSeen).execute();
12009f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
12019f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
12029f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
120369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Async task for  marking conversations "seen" and informing the cursor that the folder was
120469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * seen for the first time by the UI.
12059f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
12069f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    private static class MarkConversationCursorVisibleTask extends AsyncTask<Void, Void, Void> {
12079f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final Cursor mCursor;
12089f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final boolean mVisible;
120969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        private final boolean mIsFirstSeen;
121069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal
121169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        /**
121269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * Create a new task with the given cursor, with the given visibility and
121369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *
121469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param cursor
121569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isVisible true if the conversation list is visible, false otherwise.
121669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isFirstSeen true if the folder was shown for the first time: either the user has
121769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *        just switched to it, or the user started the app in this folder.
121869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         */
121969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        public MarkConversationCursorVisibleTask(
122069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                Cursor cursor, boolean isVisible, boolean isFirstSeen) {
12219f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            mCursor = cursor;
122269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mVisible = isVisible;
122369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mIsFirstSeen = isFirstSeen;
12249f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
12259f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
12269f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        @Override
12279f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        protected Void doInBackground(Void... params) {
122869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mCursor == null) {
122969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                return null;
123069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            }
123169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final Bundle request = new Bundle();
123269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mIsFirstSeen) {
123369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                request.putBoolean(
123469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                        UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER, true);
12359f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
123669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY;
123769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            request.putBoolean(key, mVisible);
123869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            executeConversationCursorCommand(mCursor, request, key);
12399f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            return null;
12409f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
12419f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
12429fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira
1243a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
1244a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    /**
1245a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * This utility method returns the conversation ID at the current cursor position.
1246a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * @return the conversation id at the cursor.
1247a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     */
1248a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    public static long getConversationId(ConversationCursor cursor) {
1249d781c02b91162afa91ba7d7477f40b7eec2aacedPaul Westbrook        return cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
1250a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    }
1251a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
12529fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    /**
12530a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook     * This utility method returns the conversation Uri at the current cursor position.
12540a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook     * @return the conversation id at the cursor.
12550a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook     */
12560a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook    public static String getConversationUri(ConversationCursor cursor) {
12570a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook        return cursor.getString(UIProvider.CONVERSATION_URI_COLUMN);
12580a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook    }
12590a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook
12600a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook    /**
12619fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     * @return whether to show two pane or single pane search results.
12629fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     */
12639fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    public static boolean showTwoPaneSearchResults(Context context) {
12649fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira        return context.getResources().getBoolean(R.bool.show_two_pane_search_results);
12659fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    }
1266a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira
1267a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    /**
1268bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     * Sets the layer type of a view to hardware if the view is attached and hardware acceleration
1269bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     * is enabled. Does nothing otherwise.
1270bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     */
1271bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    public static void enableHardwareLayer(View v) {
1272bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang        if (v != null && v.isHardwareAccelerated()) {
1273bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
1274bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang            v.buildLayer();
1275bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang        }
1276bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    }
1277bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang
1278bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    /**
1279a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     * Return whether menus should show the disabled archive menu item or just
1280a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     * remove it when archive is not available.
1281a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     */
1282a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    public static boolean shouldShowDisabledArchiveIcon(Context context) {
1283a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira        return context.getResources().getBoolean(R.bool.show_disabled_archive_menu_item);
1284a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    }
128555137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira
128655137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    public static int getDefaultFolderBackgroundColor(Context context) {
128755137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        if (sDefaultFolderBackgroundColor == -1) {
128855137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira            sDefaultFolderBackgroundColor = context.getResources().getColor(
128955137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira                    R.color.default_folder_background_color);
129055137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        }
129155137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        return sDefaultFolderBackgroundColor;
129255137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    }
1293443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal
1294a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    /**
1295601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * Returns the count that should be shown for the specified folder.  This method should be used
1296601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * when the UI wants to display an "unread" count.  For most labels, the returned value will be
1297601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * the unread count, but for some folder types (outbox, drafts, trash) this will return the
1298601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * total count.
1299601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     */
13008c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy    public static int getFolderUnreadDisplayCount(final Folder folder) {
1301601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        if (folder != null) {
1302f18b1173c9624d1e0a8fa37ed15a63e9fe4ccdc8Vikram Aggarwal            if (folder.isUnreadCountHidden()) {
13038c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                return folder.totalCount;
13048c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy            } else {
13058c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                return folder.unreadCount;
1306601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook            }
1307601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        }
13088c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy        return 0;
1309601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook    }
1310601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook
1311f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    /**
1312f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     * @return an intent which, if launched, will reply to the conversation
1313f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     */
1314f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    public static Intent createReplyIntent(final Context context, final Account account,
1315f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy            final Uri messageUri, final boolean isReplyAll) {
1316f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        final Intent intent =
1317f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy                ComposeActivity.createReplyIntent(context, account, messageUri, isReplyAll);
1318f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        return intent;
1319f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    }
1320f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy
1321f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    /**
1322f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     * @return an intent which, if launched, will forward the conversation
1323f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     */
1324f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    public static Intent createForwardIntent(
1325f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy            final Context context, final Account account, final Uri messageUri) {
1326f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        final Intent intent = ComposeActivity.createForwardIntent(context, account, messageUri);
1327f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        return intent;
1328f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    }
132909400efa442422299acf21abe20e3470f9d965abScott Kennedy
133009400efa442422299acf21abe20e3470f9d965abScott Kennedy    public static Uri appendVersionQueryParameter(final Context context, final Uri uri) {
133109400efa442422299acf21abe20e3470f9d965abScott Kennedy        int appVersion = 0;
133209400efa442422299acf21abe20e3470f9d965abScott Kennedy
133309400efa442422299acf21abe20e3470f9d965abScott Kennedy        try {
133409400efa442422299acf21abe20e3470f9d965abScott Kennedy            final PackageInfo packageInfo =
133509400efa442422299acf21abe20e3470f9d965abScott Kennedy                    context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
133609400efa442422299acf21abe20e3470f9d965abScott Kennedy            appVersion = packageInfo.versionCode;
133709400efa442422299acf21abe20e3470f9d965abScott Kennedy        } catch (final NameNotFoundException e) {
133809400efa442422299acf21abe20e3470f9d965abScott Kennedy            LogUtils.wtf(LOG_TAG, e, "Couldn't find our own PackageInfo");
133909400efa442422299acf21abe20e3470f9d965abScott Kennedy        }
134009400efa442422299acf21abe20e3470f9d965abScott Kennedy
134109400efa442422299acf21abe20e3470f9d965abScott Kennedy        return uri.buildUpon().appendQueryParameter(APP_VERSION_QUERY_PARAMETER,
134209400efa442422299acf21abe20e3470f9d965abScott Kennedy                Integer.toString(appVersion)).build();
134309400efa442422299acf21abe20e3470f9d965abScott Kennedy    }
1344dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1345dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    /**
1346aeb799480408485a607fcdd356b759964a587c15Paul Westbrook     * Adds the Account extra to mailto intents.
1347aeb799480408485a607fcdd356b759964a587c15Paul Westbrook     */
1348aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    public static void addAccountToMailtoIntent(Intent intent, Account account) {
1349aeb799480408485a607fcdd356b759964a587c15Paul Westbrook        if (TextUtils.equals(MAILTO_SCHEME, intent.getData().getScheme())) {
1350aeb799480408485a607fcdd356b759964a587c15Paul Westbrook            intent.putExtra(Utils.EXTRA_ACCOUNT, account);
1351aeb799480408485a607fcdd356b759964a587c15Paul Westbrook        }
1352aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    }
1353aeb799480408485a607fcdd356b759964a587c15Paul Westbrook
1354aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    /**
1355dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * Gets the specified {@link Folder} object.
1356dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     *
1357dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @param folderUri The {@link Uri} for the folder
1358dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @param allowHidden <code>true</code> to allow a hidden folder to be returned,
1359dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     *        <code>false</code> to return <code>null</code> instead
1360dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @return the specified {@link Folder} object, or <code>null</code>
1361dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     */
1362dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    public static Folder getFolder(final Context context, final Uri folderUri,
1363dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            final boolean allowHidden) {
1364dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        final Uri uri = folderUri
1365dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .buildUpon()
1366dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .appendQueryParameter(UIProvider.ALLOW_HIDDEN_FOLDERS_QUERY_PARAM,
1367dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                        Boolean.toString(allowHidden))
1368dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .build();
1369dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1370dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        final Cursor cursor = context.getContentResolver().query(uri,
1371dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                UIProvider.FOLDERS_PROJECTION, null, null, null);
1372dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1373dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        if (cursor == null) {
1374dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            return null;
1375dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        }
1376dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1377dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        try {
1378dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            if (cursor.moveToFirst()) {
1379dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                return new Folder(cursor);
1380dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            } else {
1381dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                return null;
1382dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            }
1383dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        } finally {
1384dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            cursor.close();
1385dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        }
1386dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    }
138743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
138843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    /**
138943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * Begins systrace tracing for a given tag. No-op on unsupported platform versions.
139043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
139143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @param tag systrace tag to use
139243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
139343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @see android.os.Trace#beginSection(String)
139443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     */
139543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    public static void traceBeginSection(String tag) {
139643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
139743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang            android.os.Trace.beginSection(tag);
139843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        }
139943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    }
140043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
140143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    /**
140243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * Ends systrace tracing for the most recently begun section. No-op on unsupported platform
140343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * versions.
140443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
140543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @see android.os.Trace#endSection()
140643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     */
140743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    public static void traceEndSection() {
140843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
140943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang            android.os.Trace.endSection();
141043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        }
141143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    }
141243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
1413747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    /**
1414747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei     * Get the background color of Gmail's action bar.
1415747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei     */
1416747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    public static int getActionBarBackgroundResource(final Context context) {
1417747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        final TypedValue actionBarStyle = new TypedValue();
1418747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        if (context.getTheme().resolveAttribute(android.R.attr.actionBarStyle, actionBarStyle, true)
1419747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei                && actionBarStyle.type == TypedValue.TYPE_REFERENCE) {
1420747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            final TypedValue backgroundValue = new TypedValue();
1421747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            final TypedArray attr = context.obtainStyledAttributes(actionBarStyle.resourceId,
1422747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei                    STYLE_ATTR);
1423747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            attr.getValue(0, backgroundValue);
1424747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            attr.recycle();
1425747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            return backgroundValue.resourceId;
1426747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        } else {
1427747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            // Default color
1428747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            return context.getResources().getColor(R.color.list_background_color);
1429747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        }
1430747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    }
1431174b5d5afdaea22df9fc52b52082e36cd7fd6923Alice Yang
14325c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    public static Address getAddress(Map<String, Address> cache, String emailStr) {
14330dfae6942a834f32f031c466017539d43d06e466Paul Westbrook        Address addr;
14345c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        synchronized (cache) {
14350dfae6942a834f32f031c466017539d43d06e466Paul Westbrook            addr = cache.get(emailStr);
14365c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein            if (addr == null) {
14375c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                addr = Address.getEmailAddress(emailStr);
14380dfae6942a834f32f031c466017539d43d06e466Paul Westbrook                if (addr != null) {
14395c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                    cache.put(emailStr, addr);
14405c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                }
14415c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein            }
14425c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        }
14435c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        return addr;
14445c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    }
1445570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang
1446570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    /**
1447570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     * Applies the given appearance on the given subString, and inserts that as a parameter in the
1448570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     * given parentString.
1449570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     */
1450570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    public static Spanned insertStringWithStyle(Context context,
1451570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang            int parentStringId, int subStringId, int appearance) {
1452570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final Resources resources = context.getResources();
1453570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final String subString = resources.getString(subStringId);
1454570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final String entireString = resources.getString(parentStringId, subString);
1455570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final int index = entireString.indexOf(subString);
1456570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final SpannableString descriptionText = new SpannableString(entireString);
1457570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        descriptionText.setSpan(
1458570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                new TextAppearanceSpan(context, appearance),
1459570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                index,
1460570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                index + subString.length(),
1461570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                0);
1462570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        return descriptionText;
1463570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    }
1464b573c5c0c05deec139985b883a12869dc6cac5a0Tony Mantler
14654a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    /**
14664a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * Email addresses are supposed to be treated as case-insensitive for the host-part and
14674a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * case-sensitive for the local-part, but nobody really wants email addresses to match
14684a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * case-sensitive on the local-part, so just smash everything to lower case.
14694a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * @param email Hello@Example.COM
14704a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * @return hello@example.com
14714a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     */
14724a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    public static String normalizeEmailAddress(String email) {
14734a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        /*
14744a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        // The RFC5321 version
14754a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (TextUtils.isEmpty(email)) {
14764a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
14774a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
14784a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        String[] parts = email.split("@");
14794a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (parts.length != 2) {
14804a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            LogUtils.d(LOG_TAG, "Tried to normalize a malformed email address: ", email);
14814a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
14824a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
14834a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler
14844a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        return parts[0] + "@" + parts[1].toLowerCase(Locale.US);
14854a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        */
14864a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (TextUtils.isEmpty(email)) {
14874a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
14884a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        } else {
14894a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            // Doing this for other locales might really screw things up, so do US-version only
14904a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email.toLowerCase(Locale.US);
14914a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
14924a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    }
14937b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira}
1494