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