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