Utils.java revision 5bb4d053519be10324752f323bcf73f13b9a2604
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
19f59d01c3116dc2adde97a5b52aa6094144c2d315Andrew Sappersteinimport android.annotation.TargetApi;
2010fcd645f703799326da326630b663df69f91508James Lemieuximport android.app.Activity;
213d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yangimport android.app.ActivityManager;
22bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport android.app.Fragment;
2368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereiraimport android.app.SearchManager;
246f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.Context;
258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent;
2609400efa442422299acf21abe20e3470f9d965abScott Kennedyimport android.content.pm.PackageInfo;
2794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.content.pm.PackageManager.NameNotFoundException;
286f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.res.Resources;
29747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Weiimport android.content.res.TypedArray;
30ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.database.Cursor;
3183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport android.graphics.Bitmap;
326f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.graphics.Typeface;
338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.net.Uri;
349f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrookimport android.os.AsyncTask;
35e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrookimport android.os.Build;
36ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.os.Bundle;
3794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.provider.Browser;
383e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spannable;
396f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableString;
403e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spanned;
413e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils;
426f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.CharacterStyle;
436f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.StyleSpan;
44570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yangimport android.text.style.TextAppearanceSpan;
45747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Weiimport android.util.TypedValue;
46863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.Menu;
47863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.MenuItem;
48326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View;
49326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View.MeasureSpec;
50f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.view.ViewGroup;
5118264b956bedb710e94e809a7fa30ad096021c10Andy Huangimport android.view.ViewGroup.MarginLayoutParams;
5283e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport android.view.Window;
538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebSettings;
548b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebView;
558b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
56a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.android.emailcommon.mail.Address;
5730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
58a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport com.android.mail.browse.ConversationCursor;
59f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedyimport com.android.mail.compose.ComposeActivity;
60243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huangimport com.android.mail.perf.SimpleTimer;
618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account;
629ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereiraimport com.android.mail.providers.Conversation;
638a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder;
64ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.providers.UIProvider;
65e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrookimport com.android.mail.providers.UIProvider.EditSettingsExtras;
6610fcd645f703799326da326630b663df69f91508James Lemieuximport com.android.mail.ui.HelpActivity;
671591414ed6dc829852edd219c39ae430314ff538Scott Kennedyimport com.android.mail.ui.ViewMode;
68a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlDocument;
69a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlParser;
70a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlTree;
71a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlTreeBuilder;
723e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
7392551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport org.json.JSONObject;
7492551d057965689f1952faeb06763f0762bc717fMindy Pereira
75bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.FileDescriptor;
76bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.PrintWriter;
77bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.StringWriter;
7894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport java.util.Locale;
793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport java.util.Map;
807b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
816f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereirapublic class Utils {
826f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    /**
836f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     * longest extension we recognize is 4 characters (e.g. "html", "docx")
846f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     */
856f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    private static final int FILE_EXTENSION_MAX_CHARS = 4;
863e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_ELIDED = "e";
873e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_MESSAGES = "n";
883e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_DRAFTS = "d";
893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_LITERAL = "l";
903e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SENDING = "s";
913e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SEND_FAILED = "f";
923e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final Character SENDER_LIST_SEPARATOR = '\n';
938b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    public static final String EXTRA_ACCOUNT = "account";
95e144980ef9c8828401922373ef0dc203281bff6ePaul Westbrook    public static final String EXTRA_ACCOUNT_URI = "accountUri";
9691d0b86db3287f1702913177d347dd42b7d13764Marc Blank    public static final String EXTRA_FOLDER_URI = "folderUri";
9748cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy    public static final String EXTRA_FOLDER = "folder";
987418e4b9942f291b8de8bc7b1b72a7ef7130a8b6Mindy Pereira    public static final String EXTRA_COMPOSE_URI = "composeUri";
99963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_CONVERSATION = "conversationUri";
1004fe0af81874976a1995191321e35c844b2229811Andy Huang    public static final String EXTRA_FROM_NOTIFICATION = "notification";
1015bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    public static final String EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT =
1025bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            "ignore-initial-conversation-limit";
1037517e3b61b898a57f19be0671f70d58a82224643Andy Huang
104aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    private static final String MAILTO_SCHEME = "mailto";
105aeb799480408485a607fcdd356b759964a587c15Paul Westbrook
1067517e3b61b898a57f19be0671f70d58a82224643Andy Huang    /** Extra tag for debugging the blank fragment problem. */
1077517e3b61b898a57f19be0671f70d58a82224643Andy Huang    public static final String VIEW_DEBUGGING_TAG = "MailBlankFragment";
1087517e3b61b898a57f19be0671f70d58a82224643Andy Huang
1098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /*
1108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Notifies that changes happened. Certain UI components, e.g., widgets, can
1118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * register for this {@link Intent} and update accordingly. However, this
1128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * can be very broad and is NOT the preferred way of getting notification.
1138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
1148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    // TODO: UI Provider has this notification URI?
11594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static final String ACTION_NOTIFY_DATASET_CHANGED =
11694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
11794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
11894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /** Parameter keys for context-aware help. */
11994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_HELP_LINK_PARAMETER_NAME = "p";
12094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
12194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_LINK_APP_VERSION = "version";
122fafcd17adae9b7ba5c270611d0f53608ced92e2eScott Kennedy    private static int sVersionCode = -1;
12394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
12483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    private static final int SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH = 600;
12583e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook
12609400efa442422299acf21abe20e3470f9d965abScott Kennedy    private static final String APP_VERSION_QUERY_PARAMETER = "appVersion";
127eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy    private static final String FOLDER_URI_QUERY_PARAMETER = "folderUri";
12809400efa442422299acf21abe20e3470f9d965abScott Kennedy
129b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
1306349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
131243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang    public static final boolean ENABLE_CONV_LOAD_TIMER = false;
132243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang    public static final SimpleTimer sConvLoadTimer =
133243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang            new SimpleTimer(ENABLE_CONV_LOAD_TIMER).withSessionName("ConvLoadTimer");
134243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang
135747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    private static final int[] STYLE_ATTR = new int[] {android.R.attr.background};
136747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei
137e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    public static boolean isRunningJellybeanOrLater() {
138e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
139e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    }
140e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook
141a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein    public static boolean isRunningJBMR1OrLater() {
142a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
143a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein    }
144a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein
1454ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein    public static boolean isRunningKitkatOrLater() {
146a3f66e52c4eede8b340e1ae5f9b461e1d551ad2fAndrew Sapperstein        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
1474ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein    }
1484ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein
1492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1503d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     * @return Whether we are running on a low memory device.  This is used to disable certain
1513d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     * memory intensive features in the app.
1523d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     */
153f59d01c3116dc2adde97a5b52aa6094144c2d315Andrew Sapperstein    @TargetApi(Build.VERSION_CODES.KITKAT)
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    private static int sMaxUnreadCount = -1;
216041676628fb10321ef75680af536a580d607625fPaul Westbrook    private static final CharacterStyle ACTION_BAR_UNREAD_STYLE = new StyleSpan(Typeface.BOLD);
2172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static String sUnreadText;
21855137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    private static int sDefaultFolderBackgroundColor = -1;
2194271bf0350442b594729d3074530bc0d8deac2a2mindyp    private static int sUseFolderListFragmentTransition = -1;
2202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
2222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Returns a boolean indicating whether the table UI should be shown.
2232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
224bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    public static boolean useTabletUI(Resources res) {
22551b6f566984a94f146b4d13b3157bb036bbb82bcAndrew Sapperstein        return res.getBoolean(R.bool.use_tablet_ui);
2262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2284271bf0350442b594729d3074530bc0d8deac2a2mindyp    /**
2291591414ed6dc829852edd219c39ae430314ff538Scott Kennedy     * @return <code>true</code> if the right edge effect should be displayed on list items
2301591414ed6dc829852edd219c39ae430314ff538Scott Kennedy     */
2311591414ed6dc829852edd219c39ae430314ff538Scott Kennedy    public static boolean getDisplayListRightEdgeEffect(final boolean tabletDevice,
2321591414ed6dc829852edd219c39ae430314ff538Scott Kennedy            final boolean listCollapsible, final int viewMode) {
2331591414ed6dc829852edd219c39ae430314ff538Scott Kennedy        return tabletDevice && !listCollapsible
2341591414ed6dc829852edd219c39ae430314ff538Scott Kennedy                && (ViewMode.isConversationMode(viewMode) || ViewMode.isAdMode(viewMode));
2351591414ed6dc829852edd219c39ae430314ff538Scott Kennedy    }
2361591414ed6dc829852edd219c39ae430314ff538Scott Kennedy
2371591414ed6dc829852edd219c39ae430314ff538Scott Kennedy    /**
2384271bf0350442b594729d3074530bc0d8deac2a2mindyp     * Returns a boolean indicating whether or not we should animate in the
2394271bf0350442b594729d3074530bc0d8deac2a2mindyp     * folder list fragment.
2404271bf0350442b594729d3074530bc0d8deac2a2mindyp     */
2414271bf0350442b594729d3074530bc0d8deac2a2mindyp    public static boolean useFolderListFragmentTransition(Context context) {
2424271bf0350442b594729d3074530bc0d8deac2a2mindyp        if (sUseFolderListFragmentTransition == -1) {
2434271bf0350442b594729d3074530bc0d8deac2a2mindyp            sUseFolderListFragmentTransition  = context.getResources().getInteger(
2444271bf0350442b594729d3074530bc0d8deac2a2mindyp                    R.integer.use_folder_list_fragment_transition);
2454271bf0350442b594729d3074530bc0d8deac2a2mindyp        }
2464271bf0350442b594729d3074530bc0d8deac2a2mindyp        return sUseFolderListFragmentTransition != 0;
2474271bf0350442b594729d3074530bc0d8deac2a2mindyp    }
2484271bf0350442b594729d3074530bc0d8deac2a2mindyp
2492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
25095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns displayable text from the provided HTML string.
25195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @param htmlText HTML string
25295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @return Plain text string representation of the specified Html string
25395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
25495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static String convertHtmlToPlainText(String htmlText) {
2559cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        if (TextUtils.isEmpty(htmlText)) {
2569cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook            return "";
2579cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        }
258cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder()).getPlainText();
259cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
260cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
261cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    public static String convertHtmlToPlainText(String htmlText, HtmlParser parser,
262cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
2639cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        if (TextUtils.isEmpty(htmlText)) {
2649cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook            return "";
2659cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        }
266cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, parser, builder).getPlainText();
26795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
26895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
26995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
27095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns a {@link HtmlTree} representation of the specified HTML string.
27195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
27295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static HtmlTree getHtmlTree(String htmlText) {
273cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder());
274cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
275cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
276cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    /**
277cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     * Returns a {@link HtmlTree} representation of the specified HTML string.
278cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     */
2799cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook    private static HtmlTree getHtmlTree(String htmlText, HtmlParser parser,
280cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
28195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlDocument doc = parser.parse(htmlText);
28295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        doc.accept(builder);
28395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
28495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        return builder.getTree();
28595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
28695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
28795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
2882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Perform a simulated measure pass on the given child view, assuming the
2892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * child has a ViewGroup parent and that it should be laid out within that
2902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * parent with a matching width but variable height. Code largely lifted
2912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * from AnimatedAdapter.measureChildHeight().
292f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
2932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param child a child view that has already been placed within its parent
2942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *            ViewGroup
2952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param parent the parent ViewGroup of child
2962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return measured height of the child in px
2972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
2982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static int measureViewHeight(View child, ViewGroup parent) {
29918264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final ViewGroup.LayoutParams lp = child.getLayoutParams();
30018264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int childSideMargin;
30118264b956bedb710e94e809a7fa30ad096021c10Andy Huang        if (lp instanceof MarginLayoutParams) {
30218264b956bedb710e94e809a7fa30ad096021c10Andy Huang            final MarginLayoutParams mlp = (MarginLayoutParams) lp;
30318264b956bedb710e94e809a7fa30ad096021c10Andy Huang            childSideMargin = mlp.leftMargin + mlp.rightMargin;
30418264b956bedb710e94e809a7fa30ad096021c10Andy Huang        } else {
30518264b956bedb710e94e809a7fa30ad096021c10Andy Huang            childSideMargin = 0;
30618264b956bedb710e94e809a7fa30ad096021c10Andy Huang        }
30718264b956bedb710e94e809a7fa30ad096021c10Andy Huang
30818264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int parentWSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY);
30918264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int wSpec = ViewGroup.getChildMeasureSpec(parentWSpec,
31018264b956bedb710e94e809a7fa30ad096021c10Andy Huang                parent.getPaddingLeft() + parent.getPaddingRight() + childSideMargin,
3112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                ViewGroup.LayoutParams.MATCH_PARENT);
31218264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int hSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
3132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        child.measure(wSpec, hSpec);
3142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return child.getMeasuredHeight();
3152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
316326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
31746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
31846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Encode the string in HTML.
31946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
32046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param removeEmptyDoubleQuotes If true, also remove any occurrence of ""
32146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *            found in the string
32246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
3232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static Object cleanUpString(String string, boolean removeEmptyDoubleQuotes) {
3242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return !TextUtils.isEmpty(string) ? TextUtils.htmlEncode(removeEmptyDoubleQuotes ? string
3252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                .replace("\"\"", "") : string) : "";
3262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
3272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
3292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Get the correct display string for the unread count of a folder.
3302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
3312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String getUnreadCountString(Context context, int unreadCount) {
33275d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final String unreadCountString;
33375d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final Resources resources = context.getResources();
3342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (sMaxUnreadCount == -1) {
3352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount);
3362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (unreadCount > sMaxUnreadCount) {
3382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (sUnreadText == null) {
3392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                sUnreadText = resources.getString(R.string.widget_large_unread_count);
3402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
341d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            // Localize "999+" according to the device language
3422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = String.format(sUnreadText, sMaxUnreadCount);
3432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else if (unreadCount <= 0) {
3442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = "";
3452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
346d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            // Localize unread count according to the device language
347d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            unreadCountString = String.format("%d", unreadCount);
3482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
3492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return unreadCountString;
3502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
35128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira
35228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    /**
35375d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal     * Get the correct display string for the unread count in the actionbar.
35475d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal     */
355041676628fb10321ef75680af536a580d607625fPaul Westbrook    public static CharSequence getUnreadMessageString(Context context, int unreadCount) {
356041676628fb10321ef75680af536a580d607625fPaul Westbrook        final SpannableString message;
35775d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final Resources resources = context.getResources();
35875d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        if (sMaxUnreadCount == -1) {
35975d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal            sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount);
36075d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        }
36175d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        if (unreadCount > sMaxUnreadCount) {
362b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler            message = new SpannableString(
363b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler                    resources.getString(R.string.actionbar_large_unread_count, sMaxUnreadCount));
36475d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        } else {
365b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler             message = new SpannableString(resources.getQuantityString(
366b6d3b88847eab5a0b3b414662c3a1acda7b1dfc5Tony Mantler                     R.plurals.actionbar_unread_messages, unreadCount, unreadCount));
36775d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        }
368041676628fb10321ef75680af536a580d607625fPaul Westbrook
369041676628fb10321ef75680af536a580d607625fPaul Westbrook        message.setSpan(CharacterStyle.wrap(ACTION_BAR_UNREAD_STYLE), 0,
370041676628fb10321ef75680af536a580d607625fPaul Westbrook                message.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
371041676628fb10321ef75680af536a580d607625fPaul Westbrook
37275d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        return message;
37375d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal    }
37475d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal
37575d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal    /**
37628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     * Get text matching the last sync status.
37728beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     */
37841b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal    public static CharSequence getSyncStatusText(Context context, int packedStatus) {
37917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final String[] errors = context.getResources().getStringArray(R.array.sync_status);
38041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final int status = packedStatus & 0x0f;
38128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        if (status >= errors.length) {
38228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira            return "";
38328beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        }
38428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        return errors[status];
38528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    }
3868a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3878a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
3889ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to show a conversation.
3899ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @param conversation Conversation to open.
3905c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein     * @param folderUri
391161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param account
3929ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @return
3939ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     */
39409400efa442422299acf21abe20e3470f9d965abScott Kennedy    public static Intent createViewConversationIntent(final Context context,
395b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            Conversation conversation, final Uri folderUri, Account account) {
3969ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
39709400efa442422299acf21abe20e3470f9d965abScott Kennedy        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
39809400efa442422299acf21abe20e3470f9d965abScott Kennedy                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
399eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        final Uri versionedUri = appendVersionQueryParameter(context, conversation.uri);
400eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        // We need the URI to be unique, even if it's for the same message, so append the folder URI
401eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        final Uri uniqueUri = versionedUri.buildUpon().appendQueryParameter(
402eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy                FOLDER_URI_QUERY_PARAMETER, folderUri.toString()).build();
403eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        intent.setDataAndType(uniqueUri, account.mimeType);
4045ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
405b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(Utils.EXTRA_FOLDER_URI, folderUri);
406963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira        intent.putExtra(Utils.EXTRA_CONVERSATION, conversation);
4079ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
4089ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
4099ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
4109ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
4119ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to open a folder.
412c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira     *
4135c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein     * @param folderUri Folder to open.
4140c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
415daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @return
416daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     */
417b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy    public static Intent createViewFolderIntent(final Context context, final Uri folderUri,
41809400efa442422299acf21abe20e3470f9d965abScott Kennedy            Account account) {
419b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        if (folderUri == null || account == null) {
420b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            LogUtils.wtf(LOG_TAG, "Utils.createViewFolderIntent(%s,%s): Bad input", folderUri,
421b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                    account);
4221672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return null;
4231672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
4249ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
4255ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
4265ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
427b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.setDataAndType(appendVersionQueryParameter(context, folderUri), account.mimeType);
4285ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
429b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(Utils.EXTRA_FOLDER_URI, folderUri);
4309ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
4319ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
4329ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
4339ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
4340c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * Creates an intent to open the default inbox for the given account.
4350c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     *
4360c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
4370c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @return
4380c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     */
4390c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    public static Intent createViewInboxIntent(Account account) {
4400c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        if (account == null) {
4410c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            LogUtils.wtf(LOG_TAG, "Utils.createViewInboxIntent(%s): Bad input", account);
4420c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            return null;
4430c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        }
4440c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        final Intent intent = new Intent(Intent.ACTION_VIEW);
4450c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
4460c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
4470c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setDataAndType(account.settings.defaultInbox, account.mimeType);
4480c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
4490c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        return intent;
4500c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    }
4510c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal
4520c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    /**
45310fcd645f703799326da326630b663df69f91508James Lemieux     * Helper method to show context-aware help.
45494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
45594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context to be used to open the help.
45610fcd645f703799326da326630b663df69f91508James Lemieux     * @param account Account from which the help URI is extracted
45710fcd645f703799326da326630b663df69f91508James Lemieux     * @param helpTopic Information about the activity the user was in
45810fcd645f703799326da326630b663df69f91508James Lemieux     *      when they requested help which specifies the help topic to display
45994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
46010fcd645f703799326da326630b663df69f91508James Lemieux    public static void showHelp(Context context, Account account, String helpTopic) {
46110fcd645f703799326da326630b663df69f91508James Lemieux        final String urlString = account.helpIntentUri != null ?
462c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook                account.helpIntentUri.toString() : null;
46310fcd645f703799326da326630b663df69f91508James Lemieux        if (TextUtils.isEmpty(urlString)) {
464498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            LogUtils.e(LOG_TAG, "unable to show help for account: %s", account);
465498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            return;
466498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        }
46710fcd645f703799326da326630b663df69f91508James Lemieux        showHelp(context, account.helpIntentUri, helpTopic);
46810fcd645f703799326da326630b663df69f91508James Lemieux    }
46910fcd645f703799326da326630b663df69f91508James Lemieux
47010fcd645f703799326da326630b663df69f91508James Lemieux    /**
47110fcd645f703799326da326630b663df69f91508James Lemieux     * Helper method to show context-aware help.
47210fcd645f703799326da326630b663df69f91508James Lemieux     *
47310fcd645f703799326da326630b663df69f91508James Lemieux     * @param context Context to be used to open the help.
47410fcd645f703799326da326630b663df69f91508James Lemieux     * @param helpIntentUri URI of the help content to display
47510fcd645f703799326da326630b663df69f91508James Lemieux     * @param helpTopic Information about the activity the user was in
47610fcd645f703799326da326630b663df69f91508James Lemieux     *      when they requested help which specifies the help topic to display
47710fcd645f703799326da326630b663df69f91508James Lemieux     */
47810fcd645f703799326da326630b663df69f91508James Lemieux    public static void showHelp(Context context, Uri helpIntentUri, String helpTopic) {
47910fcd645f703799326da326630b663df69f91508James Lemieux        final String urlString = helpIntentUri == null ? null : helpIntentUri.toString();
48010fcd645f703799326da326630b663df69f91508James Lemieux        if (TextUtils.isEmpty(urlString)) {
48110fcd645f703799326da326630b663df69f91508James Lemieux            LogUtils.e(LOG_TAG, "unable to show help for help URI: %s", helpIntentUri);
48210fcd645f703799326da326630b663df69f91508James Lemieux            return;
48394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
48494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
48510fcd645f703799326da326630b663df69f91508James Lemieux        // generate the full URL to the requested help section
48610fcd645f703799326da326630b663df69f91508James Lemieux        final Uri helpUrl = HelpUrl.getHelpUrl(context, helpIntentUri, helpTopic);
48710fcd645f703799326da326630b663df69f91508James Lemieux
48810fcd645f703799326da326630b663df69f91508James Lemieux        final boolean useBrowser = context.getResources().getBoolean(R.bool.openHelpWithBrowser);
48910fcd645f703799326da326630b663df69f91508James Lemieux        if (useBrowser) {
49010fcd645f703799326da326630b663df69f91508James Lemieux            // open a browser with the full help URL
49110fcd645f703799326da326630b663df69f91508James Lemieux            openUrl(context, helpUrl, null);
49210fcd645f703799326da326630b663df69f91508James Lemieux        } else {
49310fcd645f703799326da326630b663df69f91508James Lemieux            // start the help activity with the full help URL
49410fcd645f703799326da326630b663df69f91508James Lemieux            final Intent intent = new Intent(context, HelpActivity.class);
49510fcd645f703799326da326630b663df69f91508James Lemieux            intent.putExtra(HelpActivity.PARAM_HELP_URL, helpUrl);
49610fcd645f703799326da326630b663df69f91508James Lemieux            context.startActivity(intent);
49710fcd645f703799326da326630b663df69f91508James Lemieux        }
49894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
49994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
50094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
50194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to open a link in a browser.
50294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
50394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context
50494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param uri Uri to open.
50594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
50617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook    private static void openUrl(Context context, Uri uri, Bundle optionalExtras) {
50794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if(uri == null || TextUtils.isEmpty(uri.toString())) {
50894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            LogUtils.wtf(LOG_TAG, "invalid url in Utils.openUrl(): %s", uri);
50994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            return;
51094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
51117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
51217beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        // Fill in any of extras that have been requested.
51317beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        if (optionalExtras != null) {
51417beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            intent.putExtras(optionalExtras);
51517beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        }
51694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
5176f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
51817beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook
51994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        context.startActivity(intent);
52094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
52194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
5221f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    /**
5230e627fd594f3809e2bede76379a3348267185196Alice Yang     * Show the top level settings screen for the supplied account.
5241f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     */
5251f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    public static void showSettings(Context context, Account account) {
52661400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        if (account == null) {
52761400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
52861400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            return;
52961400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        }
5301f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
5316f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
5320e627fd594f3809e2bede76379a3348267185196Alice Yang        context.startActivity(settingsIntent);
5330e627fd594f3809e2bede76379a3348267185196Alice Yang    }
5340e627fd594f3809e2bede76379a3348267185196Alice Yang
5350e627fd594f3809e2bede76379a3348267185196Alice Yang    /**
5360e627fd594f3809e2bede76379a3348267185196Alice Yang     * Show the account level settings screen for the supplied account.
5370e627fd594f3809e2bede76379a3348267185196Alice Yang     */
5380e627fd594f3809e2bede76379a3348267185196Alice Yang    public static void showAccountSettings(Context context, Account account) {
5390e627fd594f3809e2bede76379a3348267185196Alice Yang        if (account == null) {
5400e627fd594f3809e2bede76379a3348267185196Alice Yang            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
5410e627fd594f3809e2bede76379a3348267185196Alice Yang            return;
5420e627fd594f3809e2bede76379a3348267185196Alice Yang        }
5430e627fd594f3809e2bede76379a3348267185196Alice Yang        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT,
5440e627fd594f3809e2bede76379a3348267185196Alice Yang                appendVersionQueryParameter(context, account.settingsIntentUri));
5450e627fd594f3809e2bede76379a3348267185196Alice Yang
5460e627fd594f3809e2bede76379a3348267185196Alice Yang        settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
5470e627fd594f3809e2bede76379a3348267185196Alice Yang        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
5481f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        context.startActivity(settingsIntent);
5491f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    }
55068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira
55168f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    /**
552e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     * Show the settings screen for the supplied account.
553e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     */
554e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook     public static void showFolderSettings(Context context, Account account, Folder folder) {
55509400efa442422299acf21abe20e3470f9d965abScott Kennedy        if (account == null || folder == null) {
55609400efa442422299acf21abe20e3470f9d965abScott Kennedy            LogUtils.e(LOG_TAG, "Invalid attempt to show folder settings. account: %s folder: %s",
55709400efa442422299acf21abe20e3470f9d965abScott Kennedy                    account, folder);
55809400efa442422299acf21abe20e3470f9d965abScott Kennedy            return;
55909400efa442422299acf21abe20e3470f9d965abScott Kennedy        }
56009400efa442422299acf21abe20e3470f9d965abScott Kennedy        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT,
56109400efa442422299acf21abe20e3470f9d965abScott Kennedy                appendVersionQueryParameter(context, account.settingsIntentUri));
562e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
56309400efa442422299acf21abe20e3470f9d965abScott Kennedy        settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
56409400efa442422299acf21abe20e3470f9d965abScott Kennedy        settingsIntent.putExtra(EditSettingsExtras.EXTRA_FOLDER, folder);
56509400efa442422299acf21abe20e3470f9d965abScott Kennedy        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
56609400efa442422299acf21abe20e3470f9d965abScott Kennedy        context.startActivity(settingsIntent);
567e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    }
568e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook
569e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook    /**
57018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     * Show the settings screen for managing all folders.
57118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     */
57218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook     public static void showManageFolder(Context context, Account account) {
57318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         if (account == null) {
57418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             LogUtils.e(LOG_TAG, "Invalid attempt to the manage folders screen with null account");
57518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook             return;
57618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         }
57718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
57818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
57918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
58018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         settingsIntent.putExtra(EditSettingsExtras.EXTRA_MANAGE_FOLDERS, true);
5816f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook         settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
58218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook         context.startActivity(settingsIntent);
58318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    }
58418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook
58518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook    /**
586fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Show the feedback screen for the supplied account.
587fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
58810fcd645f703799326da326630b663df69f91508James Lemieux    public static void sendFeedback(Activity activity, Account account, boolean reportingProblem) {
5890a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        if (activity != null && account != null) {
5900a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook            sendFeedback(activity, account.sendFeedbackIntentUri, reportingProblem);
5910a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        }
5920a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook    }
59310fcd645f703799326da326630b663df69f91508James Lemieux    public static void sendFeedback(Activity activity, Uri feedbackIntentUri,
59483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            boolean reportingProblem) {
5950a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        if (activity != null &&  !isEmpty(feedbackIntentUri)) {
59683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            final Bundle optionalExtras = new Bundle(2);
59717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            optionalExtras.putBoolean(
59817beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook                    UIProvider.SendFeedbackExtras.EXTRA_REPORTING_PROBLEM, reportingProblem);
59910fcd645f703799326da326630b663df69f91508James Lemieux            final Bitmap screenBitmap = getReducedSizeBitmap(activity);
60083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            if (screenBitmap != null) {
60183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                optionalExtras.putParcelable(
60283e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                        UIProvider.SendFeedbackExtras.EXTRA_SCREEN_SHOT, screenBitmap);
60383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            }
60410fcd645f703799326da326630b663df69f91508James Lemieux            openUrl(activity, feedbackIntentUri, optionalExtras);
6053ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
606fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
607fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
60810fcd645f703799326da326630b663df69f91508James Lemieux    private static Bitmap getReducedSizeBitmap(Activity activity) {
60983e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final Window activityWindow = activity.getWindow();
61083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final View currentView = activityWindow != null ? activityWindow.getDecorView() : null;
61183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final View rootView = currentView != null ? currentView.getRootView() : null;
61283e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        if (rootView != null) {
61383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            rootView.setDrawingCacheEnabled(true);
614c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            final Bitmap drawingCache = rootView.getDrawingCache();
615c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            // Null check to avoid NPE discovered from monkey crash:
616c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            if (drawingCache != null) {
617d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                try {
618d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    final Bitmap originalBitmap = drawingCache.copy(Bitmap.Config.RGB_565, false);
619d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double originalHeight = originalBitmap.getHeight();
620d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double originalWidth = originalBitmap.getWidth();
621d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    int newHeight = SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH;
622d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    int newWidth = SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH;
623d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double scaleX, scaleY;
624d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    scaleX = newWidth  / originalWidth;
625d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    scaleY = newHeight / originalHeight;
626d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    final double scale = Math.min(scaleX, scaleY);
627d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    newWidth = (int)Math.round(originalWidth * scale);
628d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    newHeight = (int)Math.round(originalHeight * scale);
629d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    return Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true);
630d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                } catch (OutOfMemoryError e) {
631d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    LogUtils.e(LOG_TAG, e, "OOME when attempting to scale screenshot");
632d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                }
633c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            }
63483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        }
63583e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        return null;
63683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    }
63783e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook
638fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
63968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * Retrieves the mailbox search query associated with an intent (or null if not available),
64068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     * doing proper sanitizing (e.g. trims whitespace).
64168f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira     */
64268f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    public static String mailSearchQueryForIntent(Intent intent) {
64368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        String query = intent.getStringExtra(SearchManager.QUERY);
64468f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        return TextUtils.isEmpty(query) ? null : query.trim();
64568f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira   }
64688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
64788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    /**
64888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * Split out a filename's extension and return it.
64988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @param filename a file name
65088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @return the file extension (max of 5 chars including period, like ".docx"), or null
65188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     */
65296f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira    public static String getFileExtension(String filename) {
65388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        String extension = null;
65496f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira        int index = !TextUtils.isEmpty(filename) ? filename.lastIndexOf('.') : -1;
65588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        // Limit the suffix to dot + four characters
65688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        if (index >= 0 && filename.length() - index <= FILE_EXTENSION_MAX_CHARS + 1) {
65788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang            extension = filename.substring(index);
65888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        }
65988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        return extension;
66088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    }
66188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
66288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
66388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Intent#normalizeMimeType(String)} for pre-J)
66488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
6658750066b10f80e2a8080016973b3296d76d18266Mark Wei    * Normalize a MIME data type.
66688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
66788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized MIME type has white-space trimmed,
66888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * content-type parameters removed, and is lower-case.
66988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the type with Android best practices for
67088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
67188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
67288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "text/plain; charset=utf-8" becomes "text/plain".
67388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "text/x-vCard" becomes "text/x-vcard".
67488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
67588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All MIME types received from outside Android (such as user input,
67688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
67788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
67888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
67988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @param type MIME data type to normalize
68088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized MIME data type, or null if the input was null
6815c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setType}
6825c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setTypeAndNormalize}
68388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
6848750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static String normalizeMimeType(String type) {
68588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (type == null) {
68688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           return null;
68788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
68888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
68988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       type = type.trim().toLowerCase(Locale.US);
69088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
69188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       final int semicolonIndex = type.indexOf(';');
69288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (semicolonIndex != -1) {
69388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           type = type.substring(0, semicolonIndex);
69488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
6958750066b10f80e2a8080016973b3296d76d18266Mark Wei       return type;
69688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
69788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
69888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
6995c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * (copied from {@link android.net.Uri#normalizeScheme()} for pre-J)
70088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
70188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Return a normalized representation of this Uri.
70288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
70388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized Uri has a lowercase scheme component.
70488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the Uri with Android best practices for
70588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
70688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
70788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "HTTP://www.android.com" becomes
70888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "http://www.android.com"
70988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
71088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All URIs received from outside Android (such as user input,
71188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
71288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
71388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
71488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p class="note">This method does <em>not</em> validate bad URI's,
71588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or 'fix' poorly formatted URI's - so do not use it for input validation.
71688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * A Uri will always be returned, even if the Uri is badly formatted to
71788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * begin with and a scheme component cannot be found.
71888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
71988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized Uri (never null)
72088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link android.content.Intent#setData}
72188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
72288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Uri normalizeUri(Uri uri) {
72388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String scheme = uri.getScheme();
72488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme == null) return uri;  // give up
72588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String lowerScheme = scheme.toLowerCase(Locale.US);
72688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme.equals(lowerScheme)) return uri;  // no change
72788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
72888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return uri.buildUpon().scheme(lowerScheme).build();
72988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
73088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
7318750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static Intent setIntentTypeAndNormalize(Intent intent, String type) {
7328750066b10f80e2a8080016973b3296d76d18266Mark Wei       return intent.setType(normalizeMimeType(type));
73388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
73488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
7358750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static Intent setIntentDataAndTypeAndNormalize(Intent intent, Uri data, String type) {
7368750066b10f80e2a8080016973b3296d76d18266Mark Wei       return intent.setDataAndType(normalizeUri(data), normalizeMimeType(type));
73788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
73888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
739b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   public static int getTransparentColor(int color) {
740b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira       return 0x00ffffff & color;
741b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   }
742863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira
743863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    public static void setMenuItemVisibility(Menu menu, int itemId, boolean shouldShow) {
744863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        final MenuItem item = menu.findItem(itemId);
745863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        if (item == null) {
746863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira            return;
747863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        }
748863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        item.setVisible(shouldShow);
749863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    }
75092551d057965689f1952faeb06763f0762bc717fMindy Pereira
75192551d057965689f1952faeb06763f0762bc717fMindy Pereira    /**
75292551d057965689f1952faeb06763f0762bc717fMindy Pereira     * Parse a string (possibly null or empty) into a URI. If the string is null
75392551d057965689f1952faeb06763f0762bc717fMindy Pereira     * or empty, null is returned back. Otherwise an empty URI is returned.
75492551d057965689f1952faeb06763f0762bc717fMindy Pereira     *
75592551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @param uri
75692551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
75792551d057965689f1952faeb06763f0762bc717fMindy Pereira     */
75892551d057965689f1952faeb06763f0762bc717fMindy Pereira    public static Uri getValidUri(String uri) {
759b378d64bab3c7517794ad7e2aee1d06c074e99eeMindy Pereira        if (TextUtils.isEmpty(uri) || uri == JSONObject.NULL)
76092551d057965689f1952faeb06763f0762bc717fMindy Pereira            return Uri.EMPTY;
76192551d057965689f1952faeb06763f0762bc717fMindy Pereira        return Uri.parse(uri);
76292551d057965689f1952faeb06763f0762bc717fMindy Pereira    }
76310ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank
76410ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    public static boolean isEmpty(Uri uri) {
76510ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank        return uri == null || uri.equals(Uri.EMPTY);
76610ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    }
767ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
768bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang    public static String dumpFragment(Fragment f) {
769bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        final StringWriter sw = new StringWriter();
770bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        f.dump("", new FileDescriptor(), new PrintWriter(sw), new String[0]);
771bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        return sw.toString();
772bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang    }
773bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang
774fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    public static void dumpViewTree(ViewGroup root) {
775fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        dumpViewTree(root, "");
776fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    }
777fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang
778fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    private static void dumpViewTree(ViewGroup g, String prefix) {
779fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        LogUtils.i(LOG_TAG, "%sVIEWGROUP: %s childCount=%s", prefix, g, g.getChildCount());
780fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        final String childPrefix = prefix + "  ";
781fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        for (int i = 0; i < g.getChildCount(); i++) {
782fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            final View child = g.getChildAt(i);
783fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            if (child instanceof ViewGroup) {
784fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang                dumpViewTree((ViewGroup) child, childPrefix);
785fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            } else {
786fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang                LogUtils.i(LOG_TAG, "%sCHILD #%s: %s", childPrefix, i, child);
787fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang            }
788fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang        }
789fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang    }
790fbc519e976de0c0debd810ed8c7f77de44d136a1Andy Huang
79169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
79269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Executes an out-of-band command on the cursor.
79369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param cursor
79469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param request Bundle with all keys and values set for the command.
79569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param key The string value against which we will check for success or failure
79669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @return true if the operation was a success.
79769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
79869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    private static boolean executeConversationCursorCommand(
79969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, Bundle request, String key) {
80069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle response = cursor.respond(request);
80169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final String result = response.getString(key,
802ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_FAILED);
803ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
804ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK.equals(result);
805ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
806ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
807ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
808983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * Commands a cursor representing a set of conversations to indicate that an item is being shown
809983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * in the UI.
810ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
811ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
812983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * @param position position of the item being shown.
813ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
814983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook    public static boolean notifyCursorUIPositionChange(Cursor cursor, int position) {
81569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle request = new Bundle();
816983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook        final String key =
817983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook                UIProvider.ConversationCursorCommand.COMMAND_NOTIFY_CURSOR_UI_POSITION_CHANGE;
818983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook        request.putInt(key, position);
81969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        return executeConversationCursorCommand(cursor, request, key);
820ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
8219f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
8229f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
8239f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * Commands a cursor representing a set of conversations to set its visibility state.
8249f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     *
8259f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * @param cursor a conversation cursor
82669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param visible true if the conversation list is visible, false otherwise.
82769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param isFirstSeen true if you want to notify the cursor that this conversation list was seen
82869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        for the first time: the user launched the app into it, or the user switched from some
82969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        other folder into it.
8309f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
83169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    public static void setConversationCursorVisibility(
83269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, boolean visible, boolean isFirstSeen) {
83369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        new MarkConversationCursorVisibleTask(cursor, visible, isFirstSeen).execute();
8349f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
8359f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
8369f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
83769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Async task for  marking conversations "seen" and informing the cursor that the folder was
83869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * seen for the first time by the UI.
8399f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
8409f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    private static class MarkConversationCursorVisibleTask extends AsyncTask<Void, Void, Void> {
8419f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final Cursor mCursor;
8429f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final boolean mVisible;
84369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        private final boolean mIsFirstSeen;
84469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal
84569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        /**
84669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * Create a new task with the given cursor, with the given visibility and
84769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *
84869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param cursor
84969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isVisible true if the conversation list is visible, false otherwise.
85069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isFirstSeen true if the folder was shown for the first time: either the user has
85169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *        just switched to it, or the user started the app in this folder.
85269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         */
85369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        public MarkConversationCursorVisibleTask(
85469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                Cursor cursor, boolean isVisible, boolean isFirstSeen) {
8559f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            mCursor = cursor;
85669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mVisible = isVisible;
85769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mIsFirstSeen = isFirstSeen;
8589f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
8599f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
8609f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        @Override
8619f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        protected Void doInBackground(Void... params) {
86269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mCursor == null) {
86369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                return null;
86469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            }
86569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final Bundle request = new Bundle();
86669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mIsFirstSeen) {
86769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                request.putBoolean(
86869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                        UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER, true);
8699f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
87069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY;
87169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            request.putBoolean(key, mVisible);
87269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            executeConversationCursorCommand(mCursor, request, key);
8739f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            return null;
8749f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
8759f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
8769fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira
877a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
878a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    /**
879a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * This utility method returns the conversation ID at the current cursor position.
880a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * @return the conversation id at the cursor.
881a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     */
882a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    public static long getConversationId(ConversationCursor cursor) {
883d781c02b91162afa91ba7d7477f40b7eec2aacedPaul Westbrook        return cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
884a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    }
885a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
8869fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    /**
8870a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook     * This utility method returns the conversation Uri at the current cursor position.
8880a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook     * @return the conversation id at the cursor.
8890a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook     */
8900a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook    public static String getConversationUri(ConversationCursor cursor) {
8910a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook        return cursor.getString(UIProvider.CONVERSATION_URI_COLUMN);
8920a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook    }
8930a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook
8940a22d4482396f3717b36796e594d5f8e9760d509Paul Westbrook    /**
8959fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     * @return whether to show two pane or single pane search results.
8969fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira     */
8979fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    public static boolean showTwoPaneSearchResults(Context context) {
8989fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira        return context.getResources().getBoolean(R.bool.show_two_pane_search_results);
8999fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    }
900a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira
901a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    /**
902bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     * Sets the layer type of a view to hardware if the view is attached and hardware acceleration
903bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     * is enabled. Does nothing otherwise.
904bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     */
905bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    public static void enableHardwareLayer(View v) {
906bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang        if (v != null && v.isHardwareAccelerated()) {
907bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
908bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang            v.buildLayer();
909bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang        }
910bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    }
911bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang
912bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    /**
913a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     * Return whether menus should show the disabled archive menu item or just
914a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     * remove it when archive is not available.
915a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira     */
916a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    public static boolean shouldShowDisabledArchiveIcon(Context context) {
917a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira        return context.getResources().getBoolean(R.bool.show_disabled_archive_menu_item);
918a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira    }
91955137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira
92055137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    public static int getDefaultFolderBackgroundColor(Context context) {
92155137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        if (sDefaultFolderBackgroundColor == -1) {
92255137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira            sDefaultFolderBackgroundColor = context.getResources().getColor(
92355137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira                    R.color.default_folder_background_color);
92455137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        }
92555137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira        return sDefaultFolderBackgroundColor;
92655137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    }
927443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal
928a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    /**
929601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * Returns the count that should be shown for the specified folder.  This method should be used
930601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * when the UI wants to display an "unread" count.  For most labels, the returned value will be
931601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * the unread count, but for some folder types (outbox, drafts, trash) this will return the
932601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * total count.
933601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     */
9348c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy    public static int getFolderUnreadDisplayCount(final Folder folder) {
935601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        if (folder != null) {
936f18b1173c9624d1e0a8fa37ed15a63e9fe4ccdc8Vikram Aggarwal            if (folder.isUnreadCountHidden()) {
9378c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                return folder.totalCount;
9388c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy            } else {
9398c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                return folder.unreadCount;
940601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook            }
941601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        }
9428c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy        return 0;
943601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook    }
944601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook
945f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    /**
946f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     * @return an intent which, if launched, will reply to the conversation
947f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     */
948f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    public static Intent createReplyIntent(final Context context, final Account account,
949f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy            final Uri messageUri, final boolean isReplyAll) {
950f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        final Intent intent =
951f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy                ComposeActivity.createReplyIntent(context, account, messageUri, isReplyAll);
952f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        return intent;
953f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    }
954f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy
955f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    /**
956f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     * @return an intent which, if launched, will forward the conversation
957f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy     */
958f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    public static Intent createForwardIntent(
959f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy            final Context context, final Account account, final Uri messageUri) {
960f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        final Intent intent = ComposeActivity.createForwardIntent(context, account, messageUri);
961f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy        return intent;
962f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy    }
96309400efa442422299acf21abe20e3470f9d965abScott Kennedy
96409400efa442422299acf21abe20e3470f9d965abScott Kennedy    public static Uri appendVersionQueryParameter(final Context context, final Uri uri) {
96509400efa442422299acf21abe20e3470f9d965abScott Kennedy        int appVersion = 0;
96609400efa442422299acf21abe20e3470f9d965abScott Kennedy
96709400efa442422299acf21abe20e3470f9d965abScott Kennedy        try {
96809400efa442422299acf21abe20e3470f9d965abScott Kennedy            final PackageInfo packageInfo =
96909400efa442422299acf21abe20e3470f9d965abScott Kennedy                    context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
97009400efa442422299acf21abe20e3470f9d965abScott Kennedy            appVersion = packageInfo.versionCode;
97109400efa442422299acf21abe20e3470f9d965abScott Kennedy        } catch (final NameNotFoundException e) {
97209400efa442422299acf21abe20e3470f9d965abScott Kennedy            LogUtils.wtf(LOG_TAG, e, "Couldn't find our own PackageInfo");
97309400efa442422299acf21abe20e3470f9d965abScott Kennedy        }
97409400efa442422299acf21abe20e3470f9d965abScott Kennedy
97509400efa442422299acf21abe20e3470f9d965abScott Kennedy        return uri.buildUpon().appendQueryParameter(APP_VERSION_QUERY_PARAMETER,
97609400efa442422299acf21abe20e3470f9d965abScott Kennedy                Integer.toString(appVersion)).build();
97709400efa442422299acf21abe20e3470f9d965abScott Kennedy    }
978dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
979dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    /**
980184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * Convenience method for diverting mailto: uris directly to our compose activity. Using this
981184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * method ensures that the Account object is not accidentally sent to a different process.
982184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     *
983184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @param context for sending the intent
984184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @param uri mailto: or other uri
985184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @param account desired account for potential compose activity
986184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @return true if a compose activity was started, false if uri should be sent to a view intent
987aeb799480408485a607fcdd356b759964a587c15Paul Westbrook     */
988184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler    public static boolean divertMailtoUri(final Context context, final Uri uri,
989184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler            final Account account) {
990184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler        final String scheme = normalizeUri(uri).getScheme();
991184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler        if (TextUtils.equals(MAILTO_SCHEME, scheme)) {
9920a2a346c8f40ad256eef840338a75a3bdfbe8251Andy Huang            ComposeActivity.composeMailto(context, account, uri);
993184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler            return true;
994aeb799480408485a607fcdd356b759964a587c15Paul Westbrook        }
995184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler        return false;
996aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    }
997aeb799480408485a607fcdd356b759964a587c15Paul Westbrook
998aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    /**
999dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * Gets the specified {@link Folder} object.
1000dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     *
1001dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @param folderUri The {@link Uri} for the folder
1002dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @param allowHidden <code>true</code> to allow a hidden folder to be returned,
1003dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     *        <code>false</code> to return <code>null</code> instead
1004dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @return the specified {@link Folder} object, or <code>null</code>
1005dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     */
1006dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    public static Folder getFolder(final Context context, final Uri folderUri,
1007dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            final boolean allowHidden) {
1008dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        final Uri uri = folderUri
1009dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .buildUpon()
1010dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .appendQueryParameter(UIProvider.ALLOW_HIDDEN_FOLDERS_QUERY_PARAM,
1011dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                        Boolean.toString(allowHidden))
1012dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .build();
1013dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1014dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        final Cursor cursor = context.getContentResolver().query(uri,
1015dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                UIProvider.FOLDERS_PROJECTION, null, null, null);
1016dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1017dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        if (cursor == null) {
1018dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            return null;
1019dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        }
1020dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1021dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        try {
1022dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            if (cursor.moveToFirst()) {
1023dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                return new Folder(cursor);
1024dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            } else {
1025dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                return null;
1026dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            }
1027dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        } finally {
1028dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            cursor.close();
1029dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        }
1030dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    }
103143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
103243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    /**
103343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * Begins systrace tracing for a given tag. No-op on unsupported platform versions.
103443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
103543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @param tag systrace tag to use
103643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
103743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @see android.os.Trace#beginSection(String)
103843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     */
103943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    public static void traceBeginSection(String tag) {
104043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
104143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang            android.os.Trace.beginSection(tag);
104243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        }
104343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    }
104443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
104543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    /**
104643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * Ends systrace tracing for the most recently begun section. No-op on unsupported platform
104743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * versions.
104843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
104943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @see android.os.Trace#endSection()
105043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     */
105143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    public static void traceEndSection() {
105243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
105343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang            android.os.Trace.endSection();
105443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        }
105543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    }
105643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
1057747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    /**
10585506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * Given a value and a set of upper-bounds to use as buckets, return the smallest upper-bound
10595506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * that is greater than the value.<br>
10605506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * <br>
10615506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * Useful for turning a continuous value into one of a set of discrete ones.
10625506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     *
10635506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * @param value a value to bucketize
10645506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * @param upperBounds list of upper-bound buckets to clamp to, sorted from smallest-greatest
10655506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * @return the smallest upper-bound larger than the value, or -1 if the value is larger than
10665506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * all upper-bounds
10675506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     */
10685506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang    public static long getUpperBound(long value, long[] upperBounds) {
10695506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang        for (long ub : upperBounds) {
10705506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang            if (value < ub) {
10715506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang                return ub;
10725506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang            }
10735506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang        }
10745506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang        return -1;
10755506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang    }
10765506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang
10775506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang    /**
1078747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei     * Get the background color of Gmail's action bar.
1079747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei     */
1080747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    public static int getActionBarBackgroundResource(final Context context) {
1081747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        final TypedValue actionBarStyle = new TypedValue();
1082747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        if (context.getTheme().resolveAttribute(android.R.attr.actionBarStyle, actionBarStyle, true)
1083747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei                && actionBarStyle.type == TypedValue.TYPE_REFERENCE) {
1084747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            final TypedValue backgroundValue = new TypedValue();
1085747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            final TypedArray attr = context.obtainStyledAttributes(actionBarStyle.resourceId,
1086747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei                    STYLE_ATTR);
1087747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            attr.getValue(0, backgroundValue);
1088747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            attr.recycle();
1089747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            return backgroundValue.resourceId;
1090747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        } else {
1091747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            // Default color
1092747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            return context.getResources().getColor(R.color.list_background_color);
1093747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        }
1094747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    }
1095174b5d5afdaea22df9fc52b52082e36cd7fd6923Alice Yang
10965c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    public static Address getAddress(Map<String, Address> cache, String emailStr) {
10970dfae6942a834f32f031c466017539d43d06e466Paul Westbrook        Address addr;
10985c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        synchronized (cache) {
10990dfae6942a834f32f031c466017539d43d06e466Paul Westbrook            addr = cache.get(emailStr);
11005c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein            if (addr == null) {
11015c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                addr = Address.getEmailAddress(emailStr);
11020dfae6942a834f32f031c466017539d43d06e466Paul Westbrook                if (addr != null) {
11035c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                    cache.put(emailStr, addr);
11045c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                }
11055c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein            }
11065c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        }
11075c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        return addr;
11085c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    }
1109570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang
1110570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    /**
1111570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     * Applies the given appearance on the given subString, and inserts that as a parameter in the
1112570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     * given parentString.
1113570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     */
1114570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    public static Spanned insertStringWithStyle(Context context,
11158b7493b30c6e1fe3a39f8cbd5a7b5b2f75c8e718Andy Huang            String entireString, String subString, int appearance) {
1116570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final Resources resources = context.getResources();
1117570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final int index = entireString.indexOf(subString);
1118570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final SpannableString descriptionText = new SpannableString(entireString);
1119570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        descriptionText.setSpan(
1120570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                new TextAppearanceSpan(context, appearance),
1121570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                index,
1122570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                index + subString.length(),
1123570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang                0);
1124570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        return descriptionText;
1125570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    }
1126b573c5c0c05deec139985b883a12869dc6cac5a0Tony Mantler
11274a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    /**
11284a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * Email addresses are supposed to be treated as case-insensitive for the host-part and
11294a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * case-sensitive for the local-part, but nobody really wants email addresses to match
11304a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * case-sensitive on the local-part, so just smash everything to lower case.
11314a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * @param email Hello@Example.COM
11324a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * @return hello@example.com
11334a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     */
11344a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    public static String normalizeEmailAddress(String email) {
11354a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        /*
11364a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        // The RFC5321 version
11374a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (TextUtils.isEmpty(email)) {
11384a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
11394a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
11404a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        String[] parts = email.split("@");
11414a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (parts.length != 2) {
11424a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            LogUtils.d(LOG_TAG, "Tried to normalize a malformed email address: ", email);
11434a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
11444a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
11454a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler
11464a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        return parts[0] + "@" + parts[1].toLowerCase(Locale.US);
11474a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        */
11484a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (TextUtils.isEmpty(email)) {
11494a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
11504a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        } else {
11514a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            // Doing this for other locales might really screw things up, so do US-version only
11524a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email.toLowerCase(Locale.US);
11534a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
11544a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    }
11557b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira}
1156