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;
23ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantlerimport android.content.ComponentCallbacks;
246f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.Context;
258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent;
2694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.content.pm.PackageManager.NameNotFoundException;
27ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantlerimport android.content.res.Configuration;
286f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.res.Resources;
29ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.database.Cursor;
3083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport android.graphics.Bitmap;
31248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décampsimport android.net.ConnectivityManager;
32248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décampsimport android.net.NetworkInfo;
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;
38fa674294e4ccfae7bd1317d87065b842bdc46181Tony Mantlerimport android.support.annotation.Nullable;
39b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewskiimport android.support.annotation.VisibleForTesting;
406f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableString;
413e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spanned;
423e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils;
43570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yangimport android.text.style.TextAppearanceSpan;
44863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.Menu;
45863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.MenuItem;
46326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View;
47326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View.MeasureSpec;
48f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.view.ViewGroup;
4918264b956bedb710e94e809a7fa30ad096021c10Andy Huangimport android.view.ViewGroup.MarginLayoutParams;
5083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrookimport android.view.Window;
518b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebSettings;
528b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebView;
538b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
54a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.android.emailcommon.mail.Address;
5530e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
56a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport com.android.mail.browse.ConversationCursor;
57f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedyimport com.android.mail.compose.ComposeActivity;
58243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huangimport com.android.mail.perf.SimpleTimer;
598a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account;
609ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereiraimport com.android.mail.providers.Conversation;
618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder;
62ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.providers.UIProvider;
63e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrookimport com.android.mail.providers.UIProvider.EditSettingsExtras;
6410fcd645f703799326da326630b663df69f91508James Lemieuximport com.android.mail.ui.HelpActivity;
65a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlDocument;
66a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlParser;
67a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlTree;
68a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sappersteinimport com.google.android.mail.common.html.parser.HtmlTreeBuilder;
693e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira
7092551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport org.json.JSONObject;
7192551d057965689f1952faeb06763f0762bc717fMindy Pereira
72bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.FileDescriptor;
73bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.PrintWriter;
74bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huangimport java.io.StringWriter;
7594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport java.util.Locale;
763e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport java.util.Map;
777b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
786f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereirapublic class Utils {
796f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    /**
806f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     * longest extension we recognize is 4 characters (e.g. "html", "docx")
816f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira     */
826f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira    private static final int FILE_EXTENSION_MAX_CHARS = 4;
833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_ELIDED = "e";
843e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_MESSAGES = "n";
853e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_NUM_DRAFTS = "d";
863e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_LITERAL = "l";
873e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SENDING = "s";
883e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final String SENDER_LIST_TOKEN_SEND_FAILED = "f";
893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira    public static final Character SENDER_LIST_SEPARATOR = '\n';
908b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira
916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    public static final String EXTRA_ACCOUNT = "account";
92e144980ef9c8828401922373ef0dc203281bff6ePaul Westbrook    public static final String EXTRA_ACCOUNT_URI = "accountUri";
9391d0b86db3287f1702913177d347dd42b7d13764Marc Blank    public static final String EXTRA_FOLDER_URI = "folderUri";
9448cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy    public static final String EXTRA_FOLDER = "folder";
957418e4b9942f291b8de8bc7b1b72a7ef7130a8b6Mindy Pereira    public static final String EXTRA_COMPOSE_URI = "composeUri";
96963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira    public static final String EXTRA_CONVERSATION = "conversationUri";
974fe0af81874976a1995191321e35c844b2229811Andy Huang    public static final String EXTRA_FROM_NOTIFICATION = "notification";
985bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    public static final String EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT =
995bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            "ignore-initial-conversation-limit";
1007517e3b61b898a57f19be0671f70d58a82224643Andy Huang
1018dc449cf36bb9c93b0b10f91f58be67dc950fe6bGreg Bullock    public static final String MAILTO_SCHEME = "mailto";
102aeb799480408485a607fcdd356b759964a587c15Paul Westbrook
1037517e3b61b898a57f19be0671f70d58a82224643Andy Huang    /** Extra tag for debugging the blank fragment problem. */
1047517e3b61b898a57f19be0671f70d58a82224643Andy Huang    public static final String VIEW_DEBUGGING_TAG = "MailBlankFragment";
1057517e3b61b898a57f19be0671f70d58a82224643Andy Huang
1068a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /*
1078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Notifies that changes happened. Certain UI components, e.g., widgets, can
1088a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * register for this {@link Intent} and update accordingly. However, this
1098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * can be very broad and is NOT the preferred way of getting notification.
1108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
1118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    // TODO: UI Provider has this notification URI?
11294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    public static final String ACTION_NOTIFY_DATASET_CHANGED =
11394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED";
11494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
11594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /** Parameter keys for context-aware help. */
11694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_HELP_LINK_PARAMETER_NAME = "p";
11794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
11894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    private static final String SMART_LINK_APP_VERSION = "version";
119ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    private static String sVersionCode = null;
12094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
12183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    private static final int SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH = 600;
12283e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook
12309400efa442422299acf21abe20e3470f9d965abScott Kennedy    private static final String APP_VERSION_QUERY_PARAMETER = "appVersion";
124eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy    private static final String FOLDER_URI_QUERY_PARAMETER = "folderUri";
12509400efa442422299acf21abe20e3470f9d965abScott Kennedy
126b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
1276349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
128243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang    public static final boolean ENABLE_CONV_LOAD_TIMER = false;
129243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang    public static final SimpleTimer sConvLoadTimer =
130243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang            new SimpleTimer(ENABLE_CONV_LOAD_TIMER).withSessionName("ConvLoadTimer");
131243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang
132e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    public static boolean isRunningJellybeanOrLater() {
133e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
134e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook    }
135e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook
136a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein    public static boolean isRunningJBMR1OrLater() {
137a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
138a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein    }
139a4296c179cd4eb12e7c4912d72c22dd2ec724a7eAndrew Sapperstein
1404ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein    public static boolean isRunningKitkatOrLater() {
141a3f66e52c4eede8b340e1ae5f9b461e1d551ad2fAndrew Sapperstein        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
1424ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein    }
1434ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein
1445043cc2ae07bbf82691101585084bb7f1334bb23Paul Westbrook    public static boolean isRunningLOrLater() {
1458291a8a61e2f75b454c3f97f060ef0c48fe7ede0Andrew Sapperstein        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
1465043cc2ae07bbf82691101585084bb7f1334bb23Paul Westbrook    }
1475043cc2ae07bbf82691101585084bb7f1334bb23Paul Westbrook
1486b802e8d33c6230907dc0fd0353a808f8e8f5f16Tomasz Mikolajewski    public static boolean isRunningNOrLater() {
1496b802e8d33c6230907dc0fd0353a808f8e8f5f16Tomasz Mikolajewski        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
1506b802e8d33c6230907dc0fd0353a808f8e8f5f16Tomasz Mikolajewski    }
1516b802e8d33c6230907dc0fd0353a808f8e8f5f16Tomasz Mikolajewski
1522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
1533d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     * @return Whether we are running on a low memory device.  This is used to disable certain
1543d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     * memory intensive features in the app.
1553d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang     */
156f59d01c3116dc2adde97a5b52aa6094144c2d315Andrew Sapperstein    @TargetApi(Build.VERSION_CODES.KITKAT)
1573d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang    public static boolean isLowRamDevice(Context context) {
158a3f66e52c4eede8b340e1ae5f9b461e1d551ad2fAndrew Sapperstein        if (isRunningKitkatOrLater()) {
1593d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang            final ActivityManager am = (ActivityManager) context.getSystemService(
1603d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang                    Context.ACTIVITY_SERVICE);
161e96c6d821417a9b345ec2d56895d9eab6f42e38eTony Mantler            // This will be null when running unit tests
162e96c6d821417a9b345ec2d56895d9eab6f42e38eTony Mantler            return am != null && am.isLowRamDevice();
1633d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang        } else {
1643d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang            return false;
1653d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang        }
1663d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang    }
1673d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang
1683d5ea425bce684d8fdd0a51f055025a663f3dc2eAlice Yang    /**
1692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Sets WebView in a restricted mode suitable for email use.
1702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
1712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param webView The WebView to restrict
1722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
1732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static void restrictWebView(WebView webView) {
1748b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        WebSettings webSettings = webView.getSettings();
1758b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSavePassword(false);
1768b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSaveFormData(false);
177fbe7839cee778f1493dab3219b2078518b35f22eAndrew Sapperstein        webSettings.setJavaScriptEnabled(false);
1788b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira        webSettings.setSupportZoom(false);
1792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
1802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
1812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
182ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic     * Sets custom user agent to WebView so we don't get GAIA interstitials b/13990689.
183ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic     *
184ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic     * @param webView The WebView to customize.
185ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic     */
186ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    public static void setCustomUserAgent(WebView webView, Context context) {
187ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        final WebSettings settings = webView.getSettings();
188ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        final String version = getVersionCode(context);
189ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        final String originalUserAgent = settings.getUserAgentString();
190ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        final String userAgent = context.getResources().getString(
191ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                R.string.user_agent_format, originalUserAgent, version);
192ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        settings.setUserAgentString(userAgent);
193ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    }
194ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic
195ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    /**
196ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic     * Returns the version code for the package, or null if it cannot be retrieved.
197ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic     */
198ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    public static String getVersionCode(Context context) {
199ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        if (sVersionCode == null) {
200ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic            try {
201ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                sVersionCode = String.valueOf(context.getPackageManager()
202ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                        .getPackageInfo(context.getPackageName(), 0 /* flags */)
203ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                        .versionCode);
204ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic            } catch (NameNotFoundException e) {
205ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                LogUtils.e(Utils.LOG_TAG, "Error finding package %s",
206ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                        context.getApplicationInfo().packageName);
207ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic            }
208ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        }
209ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic        return sVersionCode;
210ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    }
211ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic
212ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic    /**
2132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Format a plural string.
2142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *
2152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param resource The identity of the resource, which must be a R.plurals
2162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param count The number of items.
2172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
2182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String formatPlural(Context context, int resource, int count) {
219041676628fb10321ef75680af536a580d607625fPaul Westbrook        final CharSequence formatString = context.getResources().getQuantityText(resource, count);
2202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return String.format(formatString.toString(), count);
2212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
2242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return an ellipsized String that's at most maxCharacters long. If the
2252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         text passed is longer, it will be abbreviated. If it contains a
2262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix, the ellipses will be inserted in the middle and the
2272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *         suffix will be preserved.
2282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
2292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String ellipsize(String text, int maxCharacters) {
2302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int length = text.length();
2312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (length < maxCharacters)
2322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            return text;
2332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
2342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int realMax = Math.min(maxCharacters, length);
2352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        // Preserve the suffix if any
2362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        int index = text.lastIndexOf(".");
2372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        String extension = "\u2026"; // "...";
2382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (index >= 0) {
2392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            // Limit the suffix to dot + four characters
2402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            if (length - index <= FILE_EXTENSION_MAX_CHARS + 1) {
2412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                extension = extension + text.substring(index + 1);
2422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            }
2432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
2442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        realMax -= extension.length();
2452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        if (realMax < 0)
2462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            realMax = 0;
2472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return text.substring(0, realMax) + extension;
2482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
2492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
250ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    /**
251ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler     * This lock must be held before accessing any of the following fields
252ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler     */
253ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static final Object sStaticResourcesLock = new Object();
254ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static ComponentCallbacksListener sComponentCallbacksListener;
2552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static int sMaxUnreadCount = -1;
2562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    private static String sUnreadText;
257bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler    private static String sUnseenText;
258bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler    private static String sLargeUnseenText;
25955137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira    private static int sDefaultFolderBackgroundColor = -1;
260ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
261ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static class ComponentCallbacksListener implements ComponentCallbacks {
262ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
263ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        @Override
264ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        public void onConfigurationChanged(Configuration configuration) {
265ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            synchronized (sStaticResourcesLock) {
266ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sMaxUnreadCount = -1;
267ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sUnreadText = null;
268ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sUnseenText = null;
269ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sLargeUnseenText = null;
270ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sDefaultFolderBackgroundColor = -1;
271ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            }
272ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
273ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
274ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        @Override
275ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        public void onLowMemory() {}
276ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
277ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
278ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    public static void getStaticResources(Context context) {
279ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        synchronized (sStaticResourcesLock) {
280ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            if (sUnreadText == null) {
281ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                final Resources r = context.getResources();
282ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sMaxUnreadCount = r.getInteger(R.integer.maxUnreadCount);
283ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sUnreadText = r.getString(R.string.widget_large_unread_count);
284ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sUnseenText = r.getString(R.string.unseen_count);
285ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sLargeUnseenText = r.getString(R.string.large_unseen_count);
286ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                sDefaultFolderBackgroundColor = r.getColor(R.color.default_folder_background_color);
287ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
288ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                if (sComponentCallbacksListener == null) {
289ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                    sComponentCallbacksListener = new ComponentCallbacksListener();
290ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                    context.getApplicationContext()
291ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                            .registerComponentCallbacks(sComponentCallbacksListener);
292ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler                }
293ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            }
294ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
295ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
296ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
297ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static int getMaxUnreadCount(Context context) {
298ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        synchronized (sStaticResourcesLock) {
299ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            getStaticResources(context);
300ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            return sMaxUnreadCount;
301ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
302ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
303ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
304ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static String getUnreadText(Context context) {
305ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        synchronized (sStaticResourcesLock) {
306ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            getStaticResources(context);
307ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            return sUnreadText;
308ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
309ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
310ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
311ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static String getUnseenText(Context context) {
312ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        synchronized (sStaticResourcesLock) {
313ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            getStaticResources(context);
314ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            return sUnseenText;
315ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
316ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
317ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
318ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    private static String getLargeUnseenText(Context context) {
319ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        synchronized (sStaticResourcesLock) {
320ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            getStaticResources(context);
321ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            return sLargeUnseenText;
322ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
323ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
324ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler
325ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    public static int getDefaultFolderBackgroundColor(Context context) {
326ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        synchronized (sStaticResourcesLock) {
327ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            getStaticResources(context);
328ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            return sDefaultFolderBackgroundColor;
329ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        }
330ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler    }
3312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
3332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Returns a boolean indicating whether the table UI should be shown.
3342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
335bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    public static boolean useTabletUI(Resources res) {
33651b6f566984a94f146b4d13b3157bb036bbb82bcAndrew Sapperstein        return res.getBoolean(R.bool.use_tablet_ui);
3372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
3382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
3394271bf0350442b594729d3074530bc0d8deac2a2mindyp    /**
34095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns displayable text from the provided HTML string.
34195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @param htmlText HTML string
34295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * @return Plain text string representation of the specified Html string
34395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
34495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static String convertHtmlToPlainText(String htmlText) {
3459cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        if (TextUtils.isEmpty(htmlText)) {
3469cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook            return "";
3479cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        }
348cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder()).getPlainText();
349cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
350cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
351cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    public static String convertHtmlToPlainText(String htmlText, HtmlParser parser,
352cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
3539cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        if (TextUtils.isEmpty(htmlText)) {
3549cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook            return "";
3559cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook        }
356cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, parser, builder).getPlainText();
35795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
35895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
35995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
36095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     * Returns a {@link HtmlTree} representation of the specified HTML string.
36195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook     */
36295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    public static HtmlTree getHtmlTree(String htmlText) {
363cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp        return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder());
364cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    }
365cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp
366cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp    /**
367cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     * Returns a {@link HtmlTree} representation of the specified HTML string.
368cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp     */
3699cd585662be55469d4aeae3e0d288b575f9d21faPaul Westbrook    private static HtmlTree getHtmlTree(String htmlText, HtmlParser parser,
370cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp            HtmlTreeBuilder builder) {
37195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        final HtmlDocument doc = parser.parse(htmlText);
37295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        doc.accept(builder);
37395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
37495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook        return builder.getTree();
37595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    }
37695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook
37795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook    /**
3782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Perform a simulated measure pass on the given child view, assuming the
3792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * child has a ViewGroup parent and that it should be laid out within that
3802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * parent with a matching width but variable height. Code largely lifted
3812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * from AnimatedAdapter.measureChildHeight().
382f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang     *
3832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param child a child view that has already been placed within its parent
3842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     *            ViewGroup
3852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @param parent the parent ViewGroup of child
3862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * @return measured height of the child in px
3872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
3882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static int measureViewHeight(View child, ViewGroup parent) {
38918264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final ViewGroup.LayoutParams lp = child.getLayoutParams();
39018264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int childSideMargin;
39118264b956bedb710e94e809a7fa30ad096021c10Andy Huang        if (lp instanceof MarginLayoutParams) {
39218264b956bedb710e94e809a7fa30ad096021c10Andy Huang            final MarginLayoutParams mlp = (MarginLayoutParams) lp;
39318264b956bedb710e94e809a7fa30ad096021c10Andy Huang            childSideMargin = mlp.leftMargin + mlp.rightMargin;
39418264b956bedb710e94e809a7fa30ad096021c10Andy Huang        } else {
39518264b956bedb710e94e809a7fa30ad096021c10Andy Huang            childSideMargin = 0;
39618264b956bedb710e94e809a7fa30ad096021c10Andy Huang        }
39718264b956bedb710e94e809a7fa30ad096021c10Andy Huang
39818264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int parentWSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY);
39918264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int wSpec = ViewGroup.getChildMeasureSpec(parentWSpec,
40018264b956bedb710e94e809a7fa30ad096021c10Andy Huang                parent.getPaddingLeft() + parent.getPaddingRight() + childSideMargin,
4012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                ViewGroup.LayoutParams.MATCH_PARENT);
40218264b956bedb710e94e809a7fa30ad096021c10Andy Huang        final int hSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
4032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        child.measure(wSpec, hSpec);
4042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return child.getMeasuredHeight();
4052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
406326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
40746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
40846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Encode the string in HTML.
40946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
41046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param removeEmptyDoubleQuotes If true, also remove any occurrence of ""
41146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *            found in the string
41246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
4132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static Object cleanUpString(String string, boolean removeEmptyDoubleQuotes) {
4142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return !TextUtils.isEmpty(string) ? TextUtils.htmlEncode(removeEmptyDoubleQuotes ? string
4152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira                .replace("\"\"", "") : string) : "";
4162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
4172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira
4182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    /**
4192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     * Get the correct display string for the unread count of a folder.
4202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira     */
4212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    public static String getUnreadCountString(Context context, int unreadCount) {
42275d1bb1c9d4bcb6a3d6154ee481c64855ef39f94Vikram Aggarwal        final String unreadCountString;
423ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        final int maxUnreadCount = getMaxUnreadCount(context);
424ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        if (unreadCount > maxUnreadCount) {
425ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            final String unreadText = getUnreadText(context);
426bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler            // Localize "99+" according to the device language
427ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            unreadCountString = String.format(unreadText, maxUnreadCount);
4282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else if (unreadCount <= 0) {
4292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira            unreadCountString = "";
4302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        } else {
431d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            // Localize unread count according to the device language
432d878a84ca3e1046fe39c61cc5da501982f85885eVikram Aggarwal            unreadCountString = String.format("%d", unreadCount);
4332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        }
4342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira        return unreadCountString;
4352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira    }
43628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira
43728beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    /**
438bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler     * Get the correct display string for the unseen count of a folder.
439bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler     */
440bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler    public static String getUnseenCountString(Context context, int unseenCount) {
441bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler        final String unseenCountString;
442ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        final int maxUnreadCount = getMaxUnreadCount(context);
443ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler        if (unseenCount > maxUnreadCount) {
444ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            final String largeUnseenText = getLargeUnseenText(context);
445bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler            // Localize "99+" according to the device language
446ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            unseenCountString = String.format(largeUnseenText, maxUnreadCount);
447bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler        } else if (unseenCount <= 0) {
448bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler            unseenCountString = "";
449bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler        } else {
450bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler            // Localize unseen count according to the device language
451ce2f7364d6fb0ed7241652e39a6bf45892a9a3f5Tony Mantler            unseenCountString = String.format(getUnseenText(context), unseenCount);
452bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler        }
453bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler        return unseenCountString;
454bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler    }
455bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler
456bfeeaa17294f699c59adf86393160fdcccbd60c9Tony Mantler    /**
45728beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     * Get text matching the last sync status.
45828beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira     */
45941b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal    public static CharSequence getSyncStatusText(Context context, int packedStatus) {
46017beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final String[] errors = context.getResources().getStringArray(R.array.sync_status);
46141b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final int status = packedStatus & 0x0f;
46228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        if (status >= errors.length) {
46328beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira            return "";
46428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        }
46528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira        return errors[status];
46628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira    }
4678a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
4688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
4699ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to show a conversation.
4709ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @param conversation Conversation to open.
4715c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein     * @param folderUri
472161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * @param account
4739ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * @return
4749ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     */
47509400efa442422299acf21abe20e3470f9d965abScott Kennedy    public static Intent createViewConversationIntent(final Context context,
476b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            Conversation conversation, final Uri folderUri, Account account) {
4779ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
47809400efa442422299acf21abe20e3470f9d965abScott Kennedy        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
47909400efa442422299acf21abe20e3470f9d965abScott Kennedy                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
480eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        final Uri versionedUri = appendVersionQueryParameter(context, conversation.uri);
481eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        // We need the URI to be unique, even if it's for the same message, so append the folder URI
482eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        final Uri uniqueUri = versionedUri.buildUpon().appendQueryParameter(
483eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy                FOLDER_URI_QUERY_PARAMETER, folderUri.toString()).build();
484eed722dc6c840b70143c0af0c6d08d54fd29716cScott Kennedy        intent.setDataAndType(uniqueUri, account.mimeType);
4855ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
486b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(Utils.EXTRA_FOLDER_URI, folderUri);
487963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira        intent.putExtra(Utils.EXTRA_CONVERSATION, conversation);
4889ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
4899ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
4909ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
4919ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
4929ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira     * Create an intent to open a folder.
493c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira     *
4945c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein     * @param folderUri Folder to open.
4950c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
496daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * @return
497daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     */
498b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy    public static Intent createViewFolderIntent(final Context context, final Uri folderUri,
49909400efa442422299acf21abe20e3470f9d965abScott Kennedy            Account account) {
500b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        if (folderUri == null || account == null) {
501b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            LogUtils.wtf(LOG_TAG, "Utils.createViewFolderIntent(%s,%s): Bad input", folderUri,
502b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                    account);
5031672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return null;
5041672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
5059ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        final Intent intent = new Intent(Intent.ACTION_VIEW);
5065ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
5075ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
508b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.setDataAndType(appendVersionQueryParameter(context, folderUri), account.mimeType);
5095ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
510b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(Utils.EXTRA_FOLDER_URI, folderUri);
5119ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira        return intent;
5129ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    }
5139ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira
5149ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira    /**
5150c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * Creates an intent to open the default inbox for the given account.
5160c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     *
5170c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @param account
5180c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     * @return
5190c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal     */
5200c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    public static Intent createViewInboxIntent(Account account) {
5210c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        if (account == null) {
5220c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            LogUtils.wtf(LOG_TAG, "Utils.createViewInboxIntent(%s): Bad input", account);
5230c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal            return null;
5240c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        }
5250c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        final Intent intent = new Intent(Intent.ACTION_VIEW);
5260c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
5270c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
5280c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.setDataAndType(account.settings.defaultInbox, account.mimeType);
5290c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
5300c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal        return intent;
5310c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    }
5320c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal
5330c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal    /**
53410fcd645f703799326da326630b663df69f91508James Lemieux     * Helper method to show context-aware help.
53594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
53694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context to be used to open the help.
53710fcd645f703799326da326630b663df69f91508James Lemieux     * @param account Account from which the help URI is extracted
53810fcd645f703799326da326630b663df69f91508James Lemieux     * @param helpTopic Information about the activity the user was in
53910fcd645f703799326da326630b663df69f91508James Lemieux     *      when they requested help which specifies the help topic to display
54094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
54110fcd645f703799326da326630b663df69f91508James Lemieux    public static void showHelp(Context context, Account account, String helpTopic) {
54210fcd645f703799326da326630b663df69f91508James Lemieux        final String urlString = account.helpIntentUri != null ?
543c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook                account.helpIntentUri.toString() : null;
54410fcd645f703799326da326630b663df69f91508James Lemieux        if (TextUtils.isEmpty(urlString)) {
545498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            LogUtils.e(LOG_TAG, "unable to show help for account: %s", account);
546498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook            return;
547498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook        }
54810fcd645f703799326da326630b663df69f91508James Lemieux        showHelp(context, account.helpIntentUri, helpTopic);
54910fcd645f703799326da326630b663df69f91508James Lemieux    }
55010fcd645f703799326da326630b663df69f91508James Lemieux
55110fcd645f703799326da326630b663df69f91508James Lemieux    /**
55210fcd645f703799326da326630b663df69f91508James Lemieux     * Helper method to show context-aware help.
55310fcd645f703799326da326630b663df69f91508James Lemieux     *
55410fcd645f703799326da326630b663df69f91508James Lemieux     * @param context Context to be used to open the help.
55510fcd645f703799326da326630b663df69f91508James Lemieux     * @param helpIntentUri URI of the help content to display
55610fcd645f703799326da326630b663df69f91508James Lemieux     * @param helpTopic Information about the activity the user was in
55710fcd645f703799326da326630b663df69f91508James Lemieux     *      when they requested help which specifies the help topic to display
55810fcd645f703799326da326630b663df69f91508James Lemieux     */
55910fcd645f703799326da326630b663df69f91508James Lemieux    public static void showHelp(Context context, Uri helpIntentUri, String helpTopic) {
56010fcd645f703799326da326630b663df69f91508James Lemieux        final String urlString = helpIntentUri == null ? null : helpIntentUri.toString();
56110fcd645f703799326da326630b663df69f91508James Lemieux        if (TextUtils.isEmpty(urlString)) {
56210fcd645f703799326da326630b663df69f91508James Lemieux            LogUtils.e(LOG_TAG, "unable to show help for help URI: %s", helpIntentUri);
56310fcd645f703799326da326630b663df69f91508James Lemieux            return;
56494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
56594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
56610fcd645f703799326da326630b663df69f91508James Lemieux        // generate the full URL to the requested help section
56710fcd645f703799326da326630b663df69f91508James Lemieux        final Uri helpUrl = HelpUrl.getHelpUrl(context, helpIntentUri, helpTopic);
56810fcd645f703799326da326630b663df69f91508James Lemieux
56910fcd645f703799326da326630b663df69f91508James Lemieux        final boolean useBrowser = context.getResources().getBoolean(R.bool.openHelpWithBrowser);
57010fcd645f703799326da326630b663df69f91508James Lemieux        if (useBrowser) {
57110fcd645f703799326da326630b663df69f91508James Lemieux            // open a browser with the full help URL
57210fcd645f703799326da326630b663df69f91508James Lemieux            openUrl(context, helpUrl, null);
57310fcd645f703799326da326630b663df69f91508James Lemieux        } else {
57410fcd645f703799326da326630b663df69f91508James Lemieux            // start the help activity with the full help URL
57510fcd645f703799326da326630b663df69f91508James Lemieux            final Intent intent = new Intent(context, HelpActivity.class);
57610fcd645f703799326da326630b663df69f91508James Lemieux            intent.putExtra(HelpActivity.PARAM_HELP_URL, helpUrl);
57710fcd645f703799326da326630b663df69f91508James Lemieux            context.startActivity(intent);
57810fcd645f703799326da326630b663df69f91508James Lemieux        }
57994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
58094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
58194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    /**
58294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * Helper method to open a link in a browser.
58394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     *
58494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param context Context
58594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     * @param uri Uri to open.
58694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook     */
58717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook    private static void openUrl(Context context, Uri uri, Bundle optionalExtras) {
58894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        if(uri == null || TextUtils.isEmpty(uri.toString())) {
58994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            LogUtils.wtf(LOG_TAG, "invalid url in Utils.openUrl(): %s", uri);
59094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook            return;
59194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        }
59217beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
59317beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        // Fill in any of extras that have been requested.
59417beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        if (optionalExtras != null) {
59517beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            intent.putExtras(optionalExtras);
59617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook        }
59794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
5986f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
59917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook
60094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook        context.startActivity(intent);
60194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
60294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
6031f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    /**
6040e627fd594f3809e2bede76379a3348267185196Alice Yang     * Show the top level settings screen for the supplied account.
6051f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira     */
6061f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    public static void showSettings(Context context, Account account) {
60761400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        if (account == null) {
60861400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
60961400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook            return;
61061400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook        }
6111f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri);
612ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
613ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler        settingsIntent.setPackage(context.getPackageName());
6146f7a886390498b346daa03358ffd310d9e1f4ba0Paul Westbrook        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
615ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
6160e627fd594f3809e2bede76379a3348267185196Alice Yang        context.startActivity(settingsIntent);
6170e627fd594f3809e2bede76379a3348267185196Alice Yang    }
6180e627fd594f3809e2bede76379a3348267185196Alice Yang
6190e627fd594f3809e2bede76379a3348267185196Alice Yang    /**
6200e627fd594f3809e2bede76379a3348267185196Alice Yang     * Show the account level settings screen for the supplied account.
6210e627fd594f3809e2bede76379a3348267185196Alice Yang     */
6220e627fd594f3809e2bede76379a3348267185196Alice Yang    public static void showAccountSettings(Context context, Account account) {
6230e627fd594f3809e2bede76379a3348267185196Alice Yang        if (account == null) {
6240e627fd594f3809e2bede76379a3348267185196Alice Yang            LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account");
6250e627fd594f3809e2bede76379a3348267185196Alice Yang            return;
6260e627fd594f3809e2bede76379a3348267185196Alice Yang        }
6270e627fd594f3809e2bede76379a3348267185196Alice Yang        final Intent settingsIntent = new Intent(Intent.ACTION_EDIT,
6280e627fd594f3809e2bede76379a3348267185196Alice Yang                appendVersionQueryParameter(context, account.settingsIntentUri));
6290e627fd594f3809e2bede76379a3348267185196Alice Yang
630ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler        settingsIntent.setPackage(context.getPackageName());
6310e627fd594f3809e2bede76379a3348267185196Alice Yang        settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account);
6320e627fd594f3809e2bede76379a3348267185196Alice Yang        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
633ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
6341f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        context.startActivity(settingsIntent);
6351f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    }
63668f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira
63768f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    /**
638fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Show the feedback screen for the supplied account.
639fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
64010fcd645f703799326da326630b663df69f91508James Lemieux    public static void sendFeedback(Activity activity, Account account, boolean reportingProblem) {
6410a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        if (activity != null && account != null) {
6420a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook            sendFeedback(activity, account.sendFeedbackIntentUri, reportingProblem);
6430a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        }
6440a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook    }
645ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
64610fcd645f703799326da326630b663df69f91508James Lemieux    public static void sendFeedback(Activity activity, Uri feedbackIntentUri,
64783e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            boolean reportingProblem) {
6480a9915394a090e7f2aa95e169a8418f5884e0518Paul Westbrook        if (activity != null &&  !isEmpty(feedbackIntentUri)) {
64983e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            final Bundle optionalExtras = new Bundle(2);
65017beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook            optionalExtras.putBoolean(
65117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook                    UIProvider.SendFeedbackExtras.EXTRA_REPORTING_PROBLEM, reportingProblem);
65210fcd645f703799326da326630b663df69f91508James Lemieux            final Bitmap screenBitmap = getReducedSizeBitmap(activity);
65383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            if (screenBitmap != null) {
65483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                optionalExtras.putParcelable(
65583e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                        UIProvider.SendFeedbackExtras.EXTRA_SCREEN_SHOT, screenBitmap);
65683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            }
65710fcd645f703799326da326630b663df69f91508James Lemieux            openUrl(activity, feedbackIntentUri, optionalExtras);
6583ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
659fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
660fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
66110fcd645f703799326da326630b663df69f91508James Lemieux    private static Bitmap getReducedSizeBitmap(Activity activity) {
66283e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final Window activityWindow = activity.getWindow();
66383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final View currentView = activityWindow != null ? activityWindow.getDecorView() : null;
66483e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        final View rootView = currentView != null ? currentView.getRootView() : null;
66583e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        if (rootView != null) {
66683e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook            rootView.setDrawingCacheEnabled(true);
667c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            final Bitmap drawingCache = rootView.getDrawingCache();
668c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            // Null check to avoid NPE discovered from monkey crash:
669c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            if (drawingCache != null) {
670d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                try {
671d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    final Bitmap originalBitmap = drawingCache.copy(Bitmap.Config.RGB_565, false);
672d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double originalHeight = originalBitmap.getHeight();
673d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double originalWidth = originalBitmap.getWidth();
674d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    int newHeight = SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH;
675d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    int newWidth = SCALED_SCREENSHOT_MAX_HEIGHT_WIDTH;
676d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    double scaleX, scaleY;
677d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    scaleX = newWidth  / originalWidth;
678d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    scaleY = newHeight / originalHeight;
679d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    final double scale = Math.min(scaleX, scaleY);
680d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    newWidth = (int)Math.round(originalWidth * scale);
681d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    newHeight = (int)Math.round(originalHeight * scale);
682d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    return Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true);
683d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                } catch (OutOfMemoryError e) {
684d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                    LogUtils.e(LOG_TAG, e, "OOME when attempting to scale screenshot");
685d1c7c36c79918dc58829a60c5b802aadf32833a4Paul Westbrook                }
686c1ed8e1d12d8afa02cd23a6971f86deb20cf7f7aAlice Yang            }
68783e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        }
68883e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook        return null;
68983e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook    }
69083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook
691fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
69288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * Split out a filename's extension and return it.
69388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @param filename a file name
69488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     * @return the file extension (max of 5 chars including period, like ".docx"), or null
69588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang     */
69696f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira    public static String getFileExtension(String filename) {
69788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        String extension = null;
69896f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira        int index = !TextUtils.isEmpty(filename) ? filename.lastIndexOf('.') : -1;
69988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        // Limit the suffix to dot + four characters
70088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        if (index >= 0 && filename.length() - index <= FILE_EXTENSION_MAX_CHARS + 1) {
70188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang            extension = filename.substring(index);
70288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        }
70388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang        return extension;
70488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    }
70588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
70688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
70788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * (copied from {@link Intent#normalizeMimeType(String)} for pre-J)
70888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
7098750066b10f80e2a8080016973b3296d76d18266Mark Wei    * Normalize a MIME data type.
71088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
71188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized MIME type has white-space trimmed,
71288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * content-type parameters removed, and is lower-case.
71388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the type with Android best practices for
71488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
71588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
71688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "text/plain; charset=utf-8" becomes "text/plain".
71788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "text/x-vCard" becomes "text/x-vcard".
71888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
71988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All MIME types received from outside Android (such as user input,
72088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
72188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
72288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
72388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @param type MIME data type to normalize
72488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized MIME data type, or null if the input was null
7255c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setType}
7265c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * @see {@link android.content.Intent#setTypeAndNormalize}
72788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
7288750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static String normalizeMimeType(String type) {
72988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (type == null) {
73088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           return null;
73188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
73288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
73388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       type = type.trim().toLowerCase(Locale.US);
73488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
73588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       final int semicolonIndex = type.indexOf(';');
73688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (semicolonIndex != -1) {
73788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang           type = type.substring(0, semicolonIndex);
73888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       }
7398750066b10f80e2a8080016973b3296d76d18266Mark Wei       return type;
74088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
74188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
74288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   /**
7435c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    * (copied from {@link android.net.Uri#normalizeScheme()} for pre-J)
74488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
74588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * Return a normalized representation of this Uri.
74688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
74788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>A normalized Uri has a lowercase scheme component.
74888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * This aligns the Uri with Android best practices for
74988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * intent filtering.
75088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
75188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>For example, "HTTP://www.android.com" becomes
75288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * "http://www.android.com"
75388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
75488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p>All URIs received from outside Android (such as user input,
75588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or external sources like Bluetooth, NFC, or the Internet) should
75688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * be normalized before they are used to create an Intent.
75788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
75888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * <p class="note">This method does <em>not</em> validate bad URI's,
75988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * or 'fix' poorly formatted URI's - so do not use it for input validation.
76088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * A Uri will always be returned, even if the Uri is badly formatted to
76188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * begin with and a scheme component cannot be found.
76288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    *
76388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @return normalized Uri (never null)
76488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    * @see {@link android.content.Intent#setData}
76588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang    */
76688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   public static Uri normalizeUri(Uri uri) {
76788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String scheme = uri.getScheme();
76888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme == null) return uri;  // give up
76988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       String lowerScheme = scheme.toLowerCase(Locale.US);
77088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       if (scheme.equals(lowerScheme)) return uri;  // no change
77188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
77288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang       return uri.buildUpon().scheme(lowerScheme).build();
77388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
77488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
7758750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static Intent setIntentTypeAndNormalize(Intent intent, String type) {
7768750066b10f80e2a8080016973b3296d76d18266Mark Wei       return intent.setType(normalizeMimeType(type));
77788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
77888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
7798750066b10f80e2a8080016973b3296d76d18266Mark Wei   public static Intent setIntentDataAndTypeAndNormalize(Intent intent, Uri data, String type) {
7808750066b10f80e2a8080016973b3296d76d18266Mark Wei       return intent.setDataAndType(normalizeUri(data), normalizeMimeType(type));
78188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang   }
78288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang
783b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   public static int getTransparentColor(int color) {
784b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira       return 0x00ffffff & color;
785b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira   }
786863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira
7871e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao    /**
7881e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao     * Note that this function sets both the visibility and enabled flags for the menu item so that
7891e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao     * if shouldShow is false then the menu item is also no longer valid for keyboard shortcuts.
7901e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao     */
7911e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao    public static void setMenuItemPresent(Menu menu, int itemId, boolean shouldShow) {
7921e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao        setMenuItemPresent(menu.findItem(itemId), shouldShow);
7931e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao    }
7941e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao
7951e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao    /**
7961e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao     * Note that this function sets both the visibility and enabled flags for the menu item so that
7971e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao     * if shouldShow is false then the menu item is also no longer valid for keyboard shortcuts.
7981e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao     */
7991e5bccefa627306120fbfd783838e1ef8b7fe0d8Jin Cao    public static void setMenuItemPresent(MenuItem item, boolean shouldShow) {
800863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        if (item == null) {
801863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira            return;
802863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        }
803863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira        item.setVisible(shouldShow);
804a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        item.setEnabled(shouldShow);
805863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira    }
80692551d057965689f1952faeb06763f0762bc717fMindy Pereira
80792551d057965689f1952faeb06763f0762bc717fMindy Pereira    /**
80892551d057965689f1952faeb06763f0762bc717fMindy Pereira     * Parse a string (possibly null or empty) into a URI. If the string is null
80992551d057965689f1952faeb06763f0762bc717fMindy Pereira     * or empty, null is returned back. Otherwise an empty URI is returned.
81092551d057965689f1952faeb06763f0762bc717fMindy Pereira     *
81192551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @param uri
81292551d057965689f1952faeb06763f0762bc717fMindy Pereira     * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
81392551d057965689f1952faeb06763f0762bc717fMindy Pereira     */
81492551d057965689f1952faeb06763f0762bc717fMindy Pereira    public static Uri getValidUri(String uri) {
815b378d64bab3c7517794ad7e2aee1d06c074e99eeMindy Pereira        if (TextUtils.isEmpty(uri) || uri == JSONObject.NULL)
81692551d057965689f1952faeb06763f0762bc717fMindy Pereira            return Uri.EMPTY;
81792551d057965689f1952faeb06763f0762bc717fMindy Pereira        return Uri.parse(uri);
81892551d057965689f1952faeb06763f0762bc717fMindy Pereira    }
81910ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank
82010ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    public static boolean isEmpty(Uri uri) {
82139667579c1fd3d0275b404ae9a94b28f0c775657Jin Cao        return uri == null || Uri.EMPTY.equals(uri);
82210ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank    }
823ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
824bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang    public static String dumpFragment(Fragment f) {
825bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        final StringWriter sw = new StringWriter();
826bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        f.dump("", new FileDescriptor(), new PrintWriter(sw), new String[0]);
827bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang        return sw.toString();
828bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang    }
829bb9dd6b315c2b3a5816c8f52418a991c5c1b70fdAndy Huang
83069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
83169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Executes an out-of-band command on the cursor.
83269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param cursor
83369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param request Bundle with all keys and values set for the command.
83469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param key The string value against which we will check for success or failure
83569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @return true if the operation was a success.
83669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
83769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    private static boolean executeConversationCursorCommand(
83869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, Bundle request, String key) {
83969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle response = cursor.respond(request);
84069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final String result = response.getString(key,
841ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang                UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_FAILED);
842ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
843ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang        return UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK.equals(result);
844ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
845ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang
846ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    /**
847983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * Commands a cursor representing a set of conversations to indicate that an item is being shown
848983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * in the UI.
849ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     *
850ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     * @param cursor a conversation cursor
851983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook     * @param position position of the item being shown.
852ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang     */
853983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook    public static boolean notifyCursorUIPositionChange(Cursor cursor, int position) {
85469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final Bundle request = new Bundle();
855983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook        final String key =
856983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook                UIProvider.ConversationCursorCommand.COMMAND_NOTIFY_CURSOR_UI_POSITION_CHANGE;
857983a723931447b6261a2e42b25e6f931dba6de33Paul Westbrook        request.putInt(key, position);
85869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        return executeConversationCursorCommand(cursor, request, key);
859ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang    }
8609f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
8619f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
8629f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * Commands a cursor representing a set of conversations to set its visibility state.
8639f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     *
8649f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     * @param cursor a conversation cursor
86569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param visible true if the conversation list is visible, false otherwise.
86669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param isFirstSeen true if you want to notify the cursor that this conversation list was seen
86769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        for the first time: the user launched the app into it, or the user switched from some
86869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     *        other folder into it.
8699f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
87069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    public static void setConversationCursorVisibility(
87169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Cursor cursor, boolean visible, boolean isFirstSeen) {
87269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        new MarkConversationCursorVisibleTask(cursor, visible, isFirstSeen).execute();
8739f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
8749f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
8759f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    /**
87669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Async task for  marking conversations "seen" and informing the cursor that the folder was
87769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * seen for the first time by the UI.
8789f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook     */
8799f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    private static class MarkConversationCursorVisibleTask extends AsyncTask<Void, Void, Void> {
8809f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final Cursor mCursor;
8819f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        private final boolean mVisible;
88269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        private final boolean mIsFirstSeen;
88369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal
88469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        /**
88569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * Create a new task with the given cursor, with the given visibility and
88669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *
88769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param cursor
88869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isVisible true if the conversation list is visible, false otherwise.
88969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         * @param isFirstSeen true if the folder was shown for the first time: either the user has
89069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         *        just switched to it, or the user started the app in this folder.
89169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal         */
89269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        public MarkConversationCursorVisibleTask(
89369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                Cursor cursor, boolean isVisible, boolean isFirstSeen) {
8949f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            mCursor = cursor;
89569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mVisible = isVisible;
89669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mIsFirstSeen = isFirstSeen;
8979f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
8989f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
8999f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        @Override
9009f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        protected Void doInBackground(Void... params) {
90169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mCursor == null) {
90269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                return null;
90369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            }
90469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final Bundle request = new Bundle();
90569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            if (mIsFirstSeen) {
90669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                request.putBoolean(
90769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                        UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER, true);
9089f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
90969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY;
91069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            request.putBoolean(key, mVisible);
91169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            executeConversationCursorCommand(mCursor, request, key);
9129f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            return null;
9139f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
9149f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook    }
9159fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira
916a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
917a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    /**
918a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * This utility method returns the conversation ID at the current cursor position.
919a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     * @return the conversation id at the cursor.
920a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook     */
921a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    public static long getConversationId(ConversationCursor cursor) {
922d781c02b91162afa91ba7d7477f40b7eec2aacedPaul Westbrook        return cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
923a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook    }
924a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook
9259fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira    /**
926bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     * Sets the layer type of a view to hardware if the view is attached and hardware acceleration
927bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     * is enabled. Does nothing otherwise.
928bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang     */
929bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    public static void enableHardwareLayer(View v) {
9306ac079b4d236120232f59c080aeeb17ac16c7c8bJin Cao        if (v != null && v.isHardwareAccelerated() &&
9316ac079b4d236120232f59c080aeeb17ac16c7c8bJin Cao                v.getLayerType() != View.LAYER_TYPE_HARDWARE) {
932bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
933bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang            v.buildLayer();
934bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang        }
935bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang    }
936bb6039ed45a5eeccf08d97cb91d1b91069fed5afAndy Huang
937a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang    /**
938601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * Returns the count that should be shown for the specified folder.  This method should be used
939601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * when the UI wants to display an "unread" count.  For most labels, the returned value will be
940601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * the unread count, but for some folder types (outbox, drafts, trash) this will return the
941601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     * total count.
942601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook     */
9438c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy    public static int getFolderUnreadDisplayCount(final Folder folder) {
944601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        if (folder != null) {
945bc2370402e8ed6862da46c2bc9b39be3d934952fTony Mantler            if (folder.supportsCapability(UIProvider.FolderCapabilities.UNSEEN_COUNT_ONLY)) {
946bc2370402e8ed6862da46c2bc9b39be3d934952fTony Mantler                return 0;
947bc2370402e8ed6862da46c2bc9b39be3d934952fTony Mantler            } else if (folder.isUnreadCountHidden()) {
9488c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                return folder.totalCount;
9498c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy            } else {
9508c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy                return folder.unreadCount;
951601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook            }
952601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook        }
9538c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy        return 0;
954601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook    }
955601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook
95609400efa442422299acf21abe20e3470f9d965abScott Kennedy    public static Uri appendVersionQueryParameter(final Context context, final Uri uri) {
95709400efa442422299acf21abe20e3470f9d965abScott Kennedy        return uri.buildUpon().appendQueryParameter(APP_VERSION_QUERY_PARAMETER,
958ebdc1665d2395ff481c5e1d6091d6409b9083552Milos Stankovic                getVersionCode(context)).build();
95909400efa442422299acf21abe20e3470f9d965abScott Kennedy    }
960dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
961dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    /**
962184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * Convenience method for diverting mailto: uris directly to our compose activity. Using this
963184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * method ensures that the Account object is not accidentally sent to a different process.
964184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     *
965184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @param context for sending the intent
966184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @param uri mailto: or other uri
967184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @param account desired account for potential compose activity
968184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler     * @return true if a compose activity was started, false if uri should be sent to a view intent
969aeb799480408485a607fcdd356b759964a587c15Paul Westbrook     */
970184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler    public static boolean divertMailtoUri(final Context context, final Uri uri,
971184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler            final Account account) {
972184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler        final String scheme = normalizeUri(uri).getScheme();
973184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler        if (TextUtils.equals(MAILTO_SCHEME, scheme)) {
9740a2a346c8f40ad256eef840338a75a3bdfbe8251Andy Huang            ComposeActivity.composeMailto(context, account, uri);
975184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler            return true;
976aeb799480408485a607fcdd356b759964a587c15Paul Westbrook        }
977184ec73e169fa7c54e7f3c9459191dbe1702f3b4Tony Mantler        return false;
978aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    }
979aeb799480408485a607fcdd356b759964a587c15Paul Westbrook
980aeb799480408485a607fcdd356b759964a587c15Paul Westbrook    /**
981dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * Gets the specified {@link Folder} object.
982dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     *
983dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @param folderUri The {@link Uri} for the folder
984dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @param allowHidden <code>true</code> to allow a hidden folder to be returned,
985dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     *        <code>false</code> to return <code>null</code> instead
986dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     * @return the specified {@link Folder} object, or <code>null</code>
987dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy     */
988dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    public static Folder getFolder(final Context context, final Uri folderUri,
989dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            final boolean allowHidden) {
990dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        final Uri uri = folderUri
991dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .buildUpon()
992dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .appendQueryParameter(UIProvider.ALLOW_HIDDEN_FOLDERS_QUERY_PARAM,
993dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                        Boolean.toString(allowHidden))
994dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                .build();
995dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
996dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        final Cursor cursor = context.getContentResolver().query(uri,
997dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                UIProvider.FOLDERS_PROJECTION, null, null, null);
998dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
999dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        if (cursor == null) {
1000dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            return null;
1001dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        }
1002dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1003dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        try {
1004dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            if (cursor.moveToFirst()) {
1005dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                return new Folder(cursor);
1006dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            } else {
1007dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy                return null;
1008dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            }
1009dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        } finally {
1010dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy            cursor.close();
1011dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy        }
1012dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy    }
101343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
101443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    /**
101543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * Begins systrace tracing for a given tag. No-op on unsupported platform versions.
101643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
101743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @param tag systrace tag to use
101843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
101943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @see android.os.Trace#beginSection(String)
102043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     */
102143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    public static void traceBeginSection(String tag) {
102243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
102343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang            android.os.Trace.beginSection(tag);
102443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        }
102543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    }
102643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
102743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    /**
102843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * Ends systrace tracing for the most recently begun section. No-op on unsupported platform
102943d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * versions.
103043d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     *
103143d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     * @see android.os.Trace#endSection()
103243d2cb188e586d84db0db1da3241a615185c7d12Andy Huang     */
103343d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    public static void traceEndSection() {
103443d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
103543d2cb188e586d84db0db1da3241a615185c7d12Andy Huang            android.os.Trace.endSection();
103643d2cb188e586d84db0db1da3241a615185c7d12Andy Huang        }
103743d2cb188e586d84db0db1da3241a615185c7d12Andy Huang    }
103843d2cb188e586d84db0db1da3241a615185c7d12Andy Huang
1039747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei    /**
10405506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * Given a value and a set of upper-bounds to use as buckets, return the smallest upper-bound
10415506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * that is greater than the value.<br>
10425506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * <br>
10435506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * Useful for turning a continuous value into one of a set of discrete ones.
10445506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     *
10455506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * @param value a value to bucketize
10465506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * @param upperBounds list of upper-bound buckets to clamp to, sorted from smallest-greatest
10475506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * @return the smallest upper-bound larger than the value, or -1 if the value is larger than
10485506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     * all upper-bounds
10495506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang     */
10505506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang    public static long getUpperBound(long value, long[] upperBounds) {
10515506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang        for (long ub : upperBounds) {
10525506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang            if (value < ub) {
10535506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang                return ub;
10545506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang            }
10555506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang        }
10565506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang        return -1;
10575506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang    }
10585506369d57ffff90cbda613c561f5c03df6a4fd1Andy Huang
1059fa674294e4ccfae7bd1317d87065b842bdc46181Tony Mantler    public static @Nullable Address getAddress(Map<String, Address> cache, String emailStr) {
10600dfae6942a834f32f031c466017539d43d06e466Paul Westbrook        Address addr;
10615c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        synchronized (cache) {
10620dfae6942a834f32f031c466017539d43d06e466Paul Westbrook            addr = cache.get(emailStr);
10635c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein            if (addr == null) {
10645c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                addr = Address.getEmailAddress(emailStr);
10650dfae6942a834f32f031c466017539d43d06e466Paul Westbrook                if (addr != null) {
10665c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                    cache.put(emailStr, addr);
10675c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein                }
10685c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein            }
10695c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        }
10705c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein        return addr;
10715c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein    }
1072570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang
1073570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    /**
1074570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     * Applies the given appearance on the given subString, and inserts that as a parameter in the
1075570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     * given parentString.
1076570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang     */
1077b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski    @VisibleForTesting
1078570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    public static Spanned insertStringWithStyle(Context context,
10798b7493b30c6e1fe3a39f8cbd5a7b5b2f75c8e718Andy Huang            String entireString, String subString, int appearance) {
1080570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final int index = entireString.indexOf(subString);
1081570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        final SpannableString descriptionText = new SpannableString(entireString);
10821b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps        if (index >= 0) {
10831b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps            descriptionText.setSpan(
10841b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps                    new TextAppearanceSpan(context, appearance),
10851b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps                    index,
10861b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps                    index + subString.length(),
10871b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps                    0);
10881b7d532f160dca7a3850f50d431cc5bb5b92d85dRégis Décamps        }
1089570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang        return descriptionText;
1090570bc3e3b4a86f638f5e223cbd7c95f808ace0b2Alice Yang    }
1091b573c5c0c05deec139985b883a12869dc6cac5a0Tony Mantler
10924a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    /**
10934a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * Email addresses are supposed to be treated as case-insensitive for the host-part and
10944a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * case-sensitive for the local-part, but nobody really wants email addresses to match
10954a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * case-sensitive on the local-part, so just smash everything to lower case.
10964a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * @param email Hello@Example.COM
10974a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     * @return hello@example.com
10984a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler     */
10994a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    public static String normalizeEmailAddress(String email) {
11004a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        /*
11014a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        // The RFC5321 version
11024a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (TextUtils.isEmpty(email)) {
11034a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
11044a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
11054a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        String[] parts = email.split("@");
11064a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (parts.length != 2) {
11074a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            LogUtils.d(LOG_TAG, "Tried to normalize a malformed email address: ", email);
11084a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
11094a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
11104a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler
11114a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        return parts[0] + "@" + parts[1].toLowerCase(Locale.US);
11124a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        */
11134a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        if (TextUtils.isEmpty(email)) {
11144a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email;
11154a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        } else {
11164a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            // Doing this for other locales might really screw things up, so do US-version only
11174a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler            return email.toLowerCase(Locale.US);
11184a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler        }
11194a78f7a89adf1d0bac6049d5c892e72a8b7250c4Tony Mantler    }
1120248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps
1121248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps    /**
1122248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps     * Returns whether the device currently has network connection. This does not guarantee that
1123248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps     * the connection is reliable.
1124248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps     */
1125248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps    public static boolean isConnected(final Context context) {
1126248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps        final ConnectivityManager connectivityManager =
1127248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps                ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
1128248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps        final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
1129248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps        return (networkInfo != null) && networkInfo.isConnected();
1130248c606924e59c7986627bdff3efc2d56b0a05b3Régis Décamps    }
11317b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira}
1132