Utils.java revision cc399081e9f39d6c3238791b7116e36de66e4ac7
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 1968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereiraimport android.app.SearchManager; 206f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.Context; 218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent; 2294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.content.pm.PackageManager.NameNotFoundException; 236f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.content.res.Resources; 24ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.database.Cursor; 256f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.graphics.Typeface; 268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.net.Uri; 279f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrookimport android.os.AsyncTask; 28e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrookimport android.os.Build; 29ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport android.os.Bundle; 3094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport android.provider.Browser; 313e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Html; 323e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spannable; 336f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableString; 346f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.SpannableStringBuilder; 353e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.Spanned; 363e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils; 373e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport android.text.TextUtils.SimpleStringSplitter; 386f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.CharacterStyle; 396f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.ForegroundColorSpan; 406f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereiraimport android.text.style.StyleSpan; 41863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.Menu; 42863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereiraimport android.view.MenuItem; 43326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View; 44443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwalimport android.view.ViewParent; 45326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.View.MeasureSpec; 46f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.view.ViewGroup; 478b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebSettings; 488b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereiraimport android.webkit.WebView; 498b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira 5030e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R; 51a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrookimport com.android.mail.browse.ConversationCursor; 528a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account; 539ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereiraimport com.android.mail.providers.Conversation; 548a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder; 55ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huangimport com.android.mail.providers.UIProvider; 56e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrookimport com.android.mail.providers.UIProvider.EditSettingsExtras; 5795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlDocument; 5895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlParser; 5995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlTree; 6095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrookimport com.google.android.common.html.parser.HtmlTreeBuilder; 6110ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blankimport com.google.common.collect.Maps; 623e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira 6392551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport org.json.JSONObject; 6492551d057965689f1952faeb06763f0762bc717fMindy Pereira 6594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrookimport java.util.Locale; 663e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereiraimport java.util.Map; 677b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira 686f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereirapublic class Utils { 696f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira /** 706f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira * longest extension we recognize is 4 characters (e.g. "html", "docx") 716f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira */ 726f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira private static final int FILE_EXTENSION_MAX_CHARS = 4; 733e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira private static final Map<Integer, Integer> sPriorityToLength = Maps.newHashMap(); 743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final String SENDER_LIST_TOKEN_ELIDED = "e"; 753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final String SENDER_LIST_TOKEN_NUM_MESSAGES = "n"; 763e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final String SENDER_LIST_TOKEN_NUM_DRAFTS = "d"; 773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final String SENDER_LIST_TOKEN_LITERAL = "l"; 783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final String SENDER_LIST_TOKEN_SENDING = "s"; 793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final String SENDER_LIST_TOKEN_SEND_FAILED = "f"; 803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final Character SENDER_LIST_SEPARATOR = '\n'; 813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static final SimpleStringSplitter sSenderListSplitter = new SimpleStringSplitter( 823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira SENDER_LIST_SEPARATOR); 833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira public static String[] sSenderFragments = new String[8]; 848b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira 856349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira public static final String EXTRA_ACCOUNT = "account"; 86e144980ef9c8828401922373ef0dc203281bff6ePaul Westbrook public static final String EXTRA_ACCOUNT_URI = "accountUri"; 8791d0b86db3287f1702913177d347dd42b7d13764Marc Blank public static final String EXTRA_FOLDER_URI = "folderUri"; 887418e4b9942f291b8de8bc7b1b72a7ef7130a8b6Mindy Pereira public static final String EXTRA_COMPOSE_URI = "composeUri"; 89963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira public static final String EXTRA_CONVERSATION = "conversationUri"; 90963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira public static final String EXTRA_FOLDER = "folder"; 917517e3b61b898a57f19be0671f70d58a82224643Andy Huang 927517e3b61b898a57f19be0671f70d58a82224643Andy Huang /** Extra tag for debugging the blank fragment problem. */ 937517e3b61b898a57f19be0671f70d58a82224643Andy Huang public static final String VIEW_DEBUGGING_TAG = "MailBlankFragment"; 947517e3b61b898a57f19be0671f70d58a82224643Andy Huang 958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira /* 968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * Notifies that changes happened. Certain UI components, e.g., widgets, can 978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * register for this {@link Intent} and update accordingly. However, this 988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * can be very broad and is NOT the preferred way of getting notification. 998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira */ 1008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira // TODO: UI Provider has this notification URI? 10194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook public static final String ACTION_NOTIFY_DATASET_CHANGED = 10294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook "com.android.mail.ACTION_NOTIFY_DATASET_CHANGED"; 10394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 10494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook /** Parameter keys for context-aware help. */ 10594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook private static final String SMART_HELP_LINK_PARAMETER_NAME = "p"; 10694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 10794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook private static final String SMART_LINK_APP_VERSION = "version"; 10894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook private static String sVersionCode = null; 10994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 110b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private static final String LOG_TAG = LogTag.getLogTag(); 1116349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira 112e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook public static boolean isRunningJellybeanOrLater() { 113e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; 114e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook } 115e4fc81ef363a072480edaced9c89457b8ef019d1Paul Westbrook 1162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 1172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * Sets WebView in a restricted mode suitable for email use. 1182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * 1192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @param webView The WebView to restrict 1202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira */ 1212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static void restrictWebView(WebView webView) { 1228b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira WebSettings webSettings = webView.getSettings(); 1238b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira webSettings.setSavePassword(false); 1248b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira webSettings.setSaveFormData(false); 1258b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira webSettings.setJavaScriptEnabled(true); 1268b99ba451db6973978e60f91da2199686a9c85e7Mindy Pereira webSettings.setSupportZoom(false); 1272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 1292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 1302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * Format a plural string. 1312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * 1322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @param resource The identity of the resource, which must be a R.plurals 1332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @param count The number of items. 1342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira */ 1352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static String formatPlural(Context context, int resource, int count) { 1362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence formatString = context.getResources().getQuantityText(resource, count); 1372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return String.format(formatString.toString(), count); 1382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 1402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 1412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @return an ellipsized String that's at most maxCharacters long. If the 1422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * text passed is longer, it will be abbreviated. If it contains a 1432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * suffix, the ellipses will be inserted in the middle and the 1442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * suffix will be preserved. 1452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira */ 1462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static String ellipsize(String text, int maxCharacters) { 1472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int length = text.length(); 1482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (length < maxCharacters) 1492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return text; 1502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 1512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int realMax = Math.min(maxCharacters, length); 1522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // Preserve the suffix if any 1532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int index = text.lastIndexOf("."); 1542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String extension = "\u2026"; // "..."; 1552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (index >= 0) { 1562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // Limit the suffix to dot + four characters 1572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (length - index <= FILE_EXTENSION_MAX_CHARS + 1) { 1582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira extension = extension + text.substring(index + 1); 1592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira realMax -= extension.length(); 1622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (realMax < 0) 1632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira realMax = 0; 1642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return text.substring(0, realMax) + extension; 1652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 1672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 1684ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira * Ensures that the given string starts and ends with the double quote 1694ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira * character. The string is not modified in any way except to add the double 1704ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira * quote character to start and end if it's not already there. sample -> 1714ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira * "sample" "sample" -> "sample" ""sample"" -> "sample" 1724ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira * "sample"" -> "sample" sa"mp"le -> "sa"mp"le" "sa"mp"le" -> "sa"mp"le" 1734ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira * (empty string) -> "" " -> "" 1744ebb916ddca5f59d4f854f104fca0de6e0dda706Mindy Pereira */ 1752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static String ensureQuotedString(String s) { 1762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (s == null) { 1772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return null; 1782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (!s.matches("^\".*\"$")) { 1802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return "\"" + s + "\""; 1812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else { 1822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return s; 1832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 1852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 1862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // TODO: Move this to the UI Provider. 1872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharacterStyle sUnreadStyleSpan = null; 1882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharacterStyle sReadStyleSpan; 1892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharacterStyle sDraftsStyleSpan; 1902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharSequence sMeString; 1912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharSequence sDraftSingularString; 1922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharSequence sDraftPluralString; 1932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharSequence sSendingString; 1942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static CharSequence sSendFailedString; 1952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 1962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static int sMaxUnreadCount = -1; 1972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira private static String sUnreadText; 19855137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira private static int sDefaultFolderBackgroundColor = -1; 1992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static void getStyledSenderSnippet(Context context, String senderInstructions, 2012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder, 2022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int maxChars, boolean forceAllUnread, boolean forceAllRead, boolean allowDraft) { 2032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira Resources res = context.getResources(); 2042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (sUnreadStyleSpan == null) { 2052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sUnreadStyleSpan = new StyleSpan(Typeface.BOLD); 2062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sReadStyleSpan = new StyleSpan(Typeface.NORMAL); 2072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sDraftsStyleSpan = new ForegroundColorSpan(res.getColor(R.color.drafts)); 2082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sMeString = context.getText(R.string.me); 2102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sDraftSingularString = res.getQuantityText(R.plurals.draft, 1); 2112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sDraftPluralString = res.getQuantityText(R.plurals.draft, 2); 2122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira SpannableString sendingString = new SpannableString(context.getText(R.string.sending)); 2132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sendingString.setSpan(CharacterStyle.wrap(sDraftsStyleSpan), 0, sendingString.length(), 2142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 0); 2152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sSendingString = sendingString; 2162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sSendFailedString = context.getText(R.string.send_failed); 2172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 2182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira getSenderSnippet(senderInstructions, senderBuilder, statusBuilder, maxChars, 2202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sUnreadStyleSpan, sReadStyleSpan, sDraftsStyleSpan, sMeString, 2212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sDraftSingularString, sDraftPluralString, sSendingString, sSendFailedString, 2222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira forceAllUnread, forceAllRead, allowDraft); 2232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 2242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 2263e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * Uses sender instructions to build a formatted string. 2273e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <p> 2283e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * Sender list instructions contain compact information about the sender 2293e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * list. Most work that can be done without knowing how much room will be 2303e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * availble for the sender list is done when creating the instructions. 2313e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <p> 2323e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * The instructions string consists of tokens separated by 2333e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * SENDER_LIST_SEPARATOR. Here are the tokens, one per line: 2343e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <ul> 2353e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><tt>n</tt></li> 2363e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>int</em>, the number of non-draft messages in the conversation</li> 2373e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><tt>d</tt</li> 2383e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>int</em>, the number of drafts in the conversation</li> 2393e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><tt>l</tt></li> 2403e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>literal html to be included in the output</em></li> 2413e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><tt>s</tt> indicates that the message is sending (in the outbox 2423e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * without errors)</li> 2433e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><tt>f</tt> indicates that the message failed to send (in the outbox 2443e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * with errors)</li> 2453e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>for each message</em> 2463e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <ul> 2473e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>int</em>, 0 for read, 1 for unread</li> 2483e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>int</em>, the priority of the message. Zero is the most important 2493e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * </li> 2503e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><em>text</em>, the sender text or blank for messages from 'me'</li> 2513e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * </ul> 2523e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * </li> 2533e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <li><tt>e</tt> to indicate that one or more messages have been elided</li> 2543e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * <p> 2553e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * The instructions indicate how many messages and drafts are in the 2563e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * conversation and then describe the most important messages in order, 2573e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * indicating the priority of each message and whether the message is 2583e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * unread. 259f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 2603e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param instructions instructions as described above 2613e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param senderBuilder the SpannableStringBuilder to append to for sender 2623e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * information 2633e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param statusBuilder the SpannableStringBuilder to append to for status 2643e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param maxChars the number of characters available to display the text 2653e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param unreadStyle the CharacterStyle for unread messages, or null 2663e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param draftsStyle the CharacterStyle for draft messages, or null 2673e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param sendingString the string to use when there are messages scheduled 2683e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * to be sent 2693e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param sendFailedString the string to use when there are messages that 2703e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * mailed to send 2713e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param meString the string to use for messages sent by this user 2723e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param draftString the string to use for "Draft" 2733e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param draftPluralString the string to use for "Drafts" 2743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira */ 2752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static synchronized void getSenderSnippet(String instructions, 2762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira SpannableStringBuilder senderBuilder, SpannableStringBuilder statusBuilder, 2772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int maxChars, CharacterStyle unreadStyle, CharacterStyle readStyle, 2782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharacterStyle draftsStyle, CharSequence meString, CharSequence draftString, 2792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence draftPluralString, CharSequence sendingString, 2802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence sendFailedString, boolean forceAllUnread, boolean forceAllRead, 2812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira boolean allowDraft) { 2822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira assert !(forceAllUnread && forceAllRead); 2832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira boolean unreadStatusIsForced = forceAllUnread || forceAllRead; 2842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira boolean forcedUnreadStatus = forceAllUnread; 2852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // Measure each fragment. It's ok to iterate over the entire set of 2872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // fragments because it is 2882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // never a long list, even if there are many senders. 2892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira final Map<Integer, Integer> priorityToLength = sPriorityToLength; 2902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira priorityToLength.clear(); 2912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int maxFoundPriority = Integer.MIN_VALUE; 2932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int numMessages = 0; 2942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int numDrafts = 0; 2952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence draftsFragment = ""; 2962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence sendingFragment = ""; 2972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence sendFailedFragment = ""; 2982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 2992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sSenderListSplitter.setString(instructions); 3002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int numFragments = 0; 3012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String[] fragments = sSenderFragments; 3022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int currentSize = fragments.length; 3032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira while (sSenderListSplitter.hasNext()) { 3042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fragments[numFragments++] = sSenderListSplitter.next(); 3052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (numFragments == currentSize) { 3062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sSenderFragments = new String[2 * currentSize]; 3072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira System.arraycopy(fragments, 0, sSenderFragments, 0, currentSize); 3082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira currentSize *= 2; 3092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fragments = sSenderFragments; 3102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 3132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira for (int i = 0; i < numFragments;) { 3142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String fragment0 = fragments[i++]; 3152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if ("".equals(fragment0)) { 3162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // This should be the final fragment. 3172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) { 3182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // ignore 3192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) { 3202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira numMessages = Integer.valueOf(fragments[i++]); 3212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) { 3222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String numDraftsString = fragments[i++]; 3232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira numDrafts = Integer.parseInt(numDraftsString); 3242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira draftsFragment = numDrafts == 1 ? draftString : draftPluralString + " (" 3252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira + numDraftsString + ")"; 3262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_LITERAL.equals(fragment0)) { 3272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(Html.fromHtml(fragments[i++])); 3282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return; 3292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) { 3302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sendingFragment = sendingString; 3312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) { 3322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sendFailedFragment = sendFailedString; 3332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else { 3342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String priorityString = fragments[i++]; 3352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharSequence nameString = fragments[i++]; 3362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (nameString.length() == 0) 3372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira nameString = meString; 3382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int priority = Integer.parseInt(priorityString); 3392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira priorityToLength.put(priority, nameString.length()); 3402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira maxFoundPriority = Math.max(maxFoundPriority, priority); 3412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String numMessagesFragment = (numMessages != 0) ? " \u00A0" 3442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira + Integer.toString(numMessages + numDrafts) : ""; 3452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 3462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // Don't allocate fixedFragment unless we need it 3472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira SpannableStringBuilder fixedFragment = null; 3482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int fixedFragmentLength = 0; 3492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (draftsFragment.length() != 0 && allowDraft) { 3502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragment == null) { 3512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment = new SpannableStringBuilder(); 3522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment.append(draftsFragment); 3542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (draftsStyle != null) { 3552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment.setSpan(CharacterStyle.wrap(draftsStyle), 0, fixedFragment.length(), 3562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 3572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (sendingFragment.length() != 0) { 3602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragment == null) { 3612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment = new SpannableStringBuilder(); 3622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragment.length() != 0) 3642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment.append(", "); 3652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment.append(sendingFragment); 3662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (sendFailedFragment.length() != 0) { 3682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragment == null) { 3692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment = new SpannableStringBuilder(); 3702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragment.length() != 0) 3722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment.append(", "); 3732c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragment.append(sendFailedFragment); 3742c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3752c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 3762c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragment != null) { 3772c47a117415bc4461150f190837e0e94389b7597Mindy Pereira fixedFragmentLength = fixedFragment.length(); 3782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira maxChars -= fixedFragmentLength; 3802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 3812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int maxPriorityToInclude = -1; // inclusive 3822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int numCharsUsed = numMessagesFragment.length(); 3832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int numSendersUsed = 0; 3842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira while (maxPriorityToInclude < maxFoundPriority) { 3852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (priorityToLength.containsKey(maxPriorityToInclude + 1)) { 3862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int length = numCharsUsed + priorityToLength.get(maxPriorityToInclude + 1); 3872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (numCharsUsed > 0) 3882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira length += 2; 3892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // We must show at least two senders if they exist. If we don't 3902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // have space for both 3912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // then we will truncate names. 3922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (length > maxChars && numSendersUsed >= 2) { 3932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira break; 3942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira numCharsUsed = length; 3962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira numSendersUsed++; 3972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 3982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira maxPriorityToInclude++; 3992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 4012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int numCharsToRemovePerWord = 0; 4022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (numCharsUsed > maxChars) { 4032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira numCharsToRemovePerWord = (numCharsUsed - maxChars) / numSendersUsed; 4042c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4052c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 4062c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String lastFragment = null; 4072c47a117415bc4461150f190837e0e94389b7597Mindy Pereira CharacterStyle lastStyle = null; 4082c47a117415bc4461150f190837e0e94389b7597Mindy Pereira for (int i = 0; i < numFragments;) { 4092c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String fragment0 = fragments[i++]; 4102c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if ("".equals(fragment0)) { 4112c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // This should be the final fragment. 4122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_ELIDED.equals(fragment0)) { 4132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (lastFragment != null) { 4142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira addStyledFragment(senderBuilder, lastFragment, lastStyle, false); 4152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(" "); 4162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira addStyledFragment(senderBuilder, "..", lastStyle, true); 4172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(" "); 4182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira lastFragment = null; 4202c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_NUM_MESSAGES.equals(fragment0)) { 4212c47a117415bc4461150f190837e0e94389b7597Mindy Pereira i++; 4222c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_NUM_DRAFTS.equals(fragment0)) { 4232c47a117415bc4461150f190837e0e94389b7597Mindy Pereira i++; 4242c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_SENDING.equals(fragment0)) { 4252c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (SENDER_LIST_TOKEN_SEND_FAILED.equals(fragment0)) { 4262c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else { 4272c47a117415bc4461150f190837e0e94389b7597Mindy Pereira final String unreadString = fragment0; 4282c47a117415bc4461150f190837e0e94389b7597Mindy Pereira final String priorityString = fragments[i++]; 4292c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String nameString = fragments[i++]; 4302c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (nameString.length() == 0) { 4312c47a117415bc4461150f190837e0e94389b7597Mindy Pereira nameString = meString.toString(); 4322c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else { 4332c47a117415bc4461150f190837e0e94389b7597Mindy Pereira nameString = Html.fromHtml(nameString).toString(); 4342c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4352c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (numCharsToRemovePerWord != 0) { 4362c47a117415bc4461150f190837e0e94389b7597Mindy Pereira nameString = nameString.substring(0, 4372c47a117415bc4461150f190837e0e94389b7597Mindy Pereira Math.max(nameString.length() - numCharsToRemovePerWord, 0)); 4382c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4392c47a117415bc4461150f190837e0e94389b7597Mindy Pereira final boolean unread = unreadStatusIsForced ? forcedUnreadStatus : Integer 4402c47a117415bc4461150f190837e0e94389b7597Mindy Pereira .parseInt(unreadString) != 0; 4412c47a117415bc4461150f190837e0e94389b7597Mindy Pereira final int priority = Integer.parseInt(priorityString); 4422c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (priority <= maxPriorityToInclude) { 4432c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (lastFragment != null && !lastFragment.equals(nameString)) { 4442c47a117415bc4461150f190837e0e94389b7597Mindy Pereira addStyledFragment(senderBuilder, lastFragment.concat(","), lastStyle, 4452c47a117415bc4461150f190837e0e94389b7597Mindy Pereira false); 4462c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(" "); 4472c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4482c47a117415bc4461150f190837e0e94389b7597Mindy Pereira lastFragment = nameString; 4492c47a117415bc4461150f190837e0e94389b7597Mindy Pereira lastStyle = unread ? unreadStyle : readStyle; 4502c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else { 4512c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (lastFragment != null) { 4522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira addStyledFragment(senderBuilder, lastFragment, lastStyle, false); 4532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // Adjacent spans can cause the TextView in Gmail widget 4542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // confused and leads to weird behavior on scrolling. 4552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // Our workaround here is to separate the spans by 4562c47a117415bc4461150f190837e0e94389b7597Mindy Pereira // spaces. 4572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(" "); 4582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira addStyledFragment(senderBuilder, "..", lastStyle, true); 4592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(" "); 4602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira lastFragment = null; 4622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (lastFragment != null) { 4662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira addStyledFragment(senderBuilder, lastFragment, lastStyle, false); 4672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira senderBuilder.append(numMessagesFragment); 4692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (fixedFragmentLength != 0) { 4702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira statusBuilder.append(fixedFragment); 4712c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4722c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 4736f92de64bbdf1ea6c4cd9774fc96921a10c266d7Mindy Pereira 4743e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira /** 4753e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * Adds a fragment with given style to a string builder. 476f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 4773e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param builder the current string builder 4783e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param fragment the fragment to be added 4793e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param style the style of the fragment 4803e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * @param withSpaces whether to add the whole fragment or to divide it into 4813e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira * smaller ones 4823e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira */ 4833e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira private static void addStyledFragment(SpannableStringBuilder builder, String fragment, 4843e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira CharacterStyle style, boolean withSpaces) { 4853e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira if (withSpaces) { 4863e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira int pos = builder.length(); 4873e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira builder.append(fragment); 4883e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira builder.setSpan(CharacterStyle.wrap(style), pos, builder.length(), 4893e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 4903e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } else { 4913e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira int start = 0; 4923e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira while (true) { 4933e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira int pos = fragment.substring(start).indexOf(' '); 4943e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira if (pos == -1) { 4953e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira addStyledFragment(builder, fragment.substring(start), style, true); 4963e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira break; 4973e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } else { 4983e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira pos += start; 4993e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira if (start < pos) { 5003e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira addStyledFragment(builder, fragment.substring(start, pos), style, true); 5013e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira builder.append(' '); 5023e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } 5033e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira start = pos + 1; 5043e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira if (start >= fragment.length()) { 5053e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira break; 5063e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } 5073e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } 5083e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } 5093e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } 5103e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira } 5113e0426cceb884d13b5d39b56b2171686a1afdf03Mindy Pereira 5122c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 5132c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * Returns a boolean indicating whether the table UI should be shown. 5142c47a117415bc4461150f190837e0e94389b7597Mindy Pereira */ 5152c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static boolean useTabletUI(Context context) { 5162c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return context.getResources().getInteger(R.integer.use_tablet_ui) != 0; 5172c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 5182c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 5192c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 52095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook * Returns displayable text from the provided HTML string. 52195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook * @param htmlText HTML string 52295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook * @return Plain text string representation of the specified Html string 52395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook */ 52495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook public static String convertHtmlToPlainText(String htmlText) { 525cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder()).getPlainText(); 526cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp } 527cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp 528cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp public static String convertHtmlToPlainText(String htmlText, HtmlParser parser, 529cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp HtmlTreeBuilder builder) { 530cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp return getHtmlTree(htmlText, parser, builder).getPlainText(); 53195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook } 53295141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook 53395141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook /** 53495141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook * Returns a {@link HtmlTree} representation of the specified HTML string. 53595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook */ 53695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook public static HtmlTree getHtmlTree(String htmlText) { 537cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp return getHtmlTree(htmlText, new HtmlParser(), new HtmlTreeBuilder()); 538cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp } 539cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp 540cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp /** 541cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp * Returns a {@link HtmlTree} representation of the specified HTML string. 542cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp */ 543cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp public static HtmlTree getHtmlTree(String htmlText, HtmlParser parser, 544cc399081e9f39d6c3238791b7116e36de66e4ac7mindyp HtmlTreeBuilder builder) { 54595141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook final HtmlDocument doc = parser.parse(htmlText); 54695141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook doc.accept(builder); 54795141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook 54895141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook return builder.getTree(); 54995141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook } 55095141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook 55195141f8887011fcdd647006ddd4f75866a7e1419Paul Westbrook /** 5522c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * Perform a simulated measure pass on the given child view, assuming the 5532c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * child has a ViewGroup parent and that it should be laid out within that 5542c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * parent with a matching width but variable height. Code largely lifted 5552c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * from AnimatedAdapter.measureChildHeight(). 556f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 5572c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @param child a child view that has already been placed within its parent 5582c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * ViewGroup 5592c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @param parent the parent ViewGroup of child 5602c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * @return measured height of the child in px 5612c47a117415bc4461150f190837e0e94389b7597Mindy Pereira */ 5622c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static int measureViewHeight(View child, ViewGroup parent) { 5632c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int parentWSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.EXACTLY); 5642c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int wSpec = ViewGroup.getChildMeasureSpec(parentWSpec, 5652c47a117415bc4461150f190837e0e94389b7597Mindy Pereira parent.getPaddingLeft() + parent.getPaddingRight(), 5662c47a117415bc4461150f190837e0e94389b7597Mindy Pereira ViewGroup.LayoutParams.MATCH_PARENT); 5672c47a117415bc4461150f190837e0e94389b7597Mindy Pereira int hSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 5682c47a117415bc4461150f190837e0e94389b7597Mindy Pereira child.measure(wSpec, hSpec); 5692c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return child.getMeasuredHeight(); 5702c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 571326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira 57246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira /** 57346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira * Encode the string in HTML. 57446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira * 57546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira * @param removeEmptyDoubleQuotes If true, also remove any occurrence of "" 57646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira * found in the string 57746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira */ 5782c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static Object cleanUpString(String string, boolean removeEmptyDoubleQuotes) { 5792c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return !TextUtils.isEmpty(string) ? TextUtils.htmlEncode(removeEmptyDoubleQuotes ? string 5802c47a117415bc4461150f190837e0e94389b7597Mindy Pereira .replace("\"\"", "") : string) : ""; 5812c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 5822c47a117415bc4461150f190837e0e94389b7597Mindy Pereira 5832c47a117415bc4461150f190837e0e94389b7597Mindy Pereira /** 5842c47a117415bc4461150f190837e0e94389b7597Mindy Pereira * Get the correct display string for the unread count of a folder. 5852c47a117415bc4461150f190837e0e94389b7597Mindy Pereira */ 5862c47a117415bc4461150f190837e0e94389b7597Mindy Pereira public static String getUnreadCountString(Context context, int unreadCount) { 5872c47a117415bc4461150f190837e0e94389b7597Mindy Pereira String unreadCountString; 5882c47a117415bc4461150f190837e0e94389b7597Mindy Pereira Resources resources = context.getResources(); 5892c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (sMaxUnreadCount == -1) { 5902c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sMaxUnreadCount = resources.getInteger(R.integer.maxUnreadCount); 5912c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 5922c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (unreadCount > sMaxUnreadCount) { 5932c47a117415bc4461150f190837e0e94389b7597Mindy Pereira if (sUnreadText == null) { 5942c47a117415bc4461150f190837e0e94389b7597Mindy Pereira sUnreadText = resources.getString(R.string.widget_large_unread_count); 5952c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 5962c47a117415bc4461150f190837e0e94389b7597Mindy Pereira unreadCountString = String.format(sUnreadText, sMaxUnreadCount); 5972c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else if (unreadCount <= 0) { 5982c47a117415bc4461150f190837e0e94389b7597Mindy Pereira unreadCountString = ""; 5992c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } else { 6002c47a117415bc4461150f190837e0e94389b7597Mindy Pereira unreadCountString = String.valueOf(unreadCount); 6012c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 6022c47a117415bc4461150f190837e0e94389b7597Mindy Pereira return unreadCountString; 6032c47a117415bc4461150f190837e0e94389b7597Mindy Pereira } 60428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira 60528beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira /** 60628beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira * Get text matching the last sync status. 60728beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira */ 60828beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira public static CharSequence getSyncStatusText(Context context, int status) { 60917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook final String[] errors = context.getResources().getStringArray(R.array.sync_status); 61028beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira if (status >= errors.length) { 61128beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira return ""; 61228beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira } 61328beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira return errors[status]; 61428beb84263683aa2a47d3e42bd322aa11e4dd838Mindy Pereira } 6158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira 6168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira /** 6179ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira * Create an intent to show a conversation. 6189ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira * @param conversation Conversation to open. 619161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * @param folder 620161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * @param account 6219ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira * @return 6229ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira */ 623161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira public static Intent createViewConversationIntent(Conversation conversation, Folder folder, 624161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira Account account) { 6259ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira final Intent intent = new Intent(Intent.ACTION_VIEW); 6267425d482040c5c2badb466ce840b67396c73f118Paul Westbrook intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | 6277425d482040c5c2badb466ce840b67396c73f118Paul Westbrook Intent.FLAG_ACTIVITY_TASK_ON_HOME); 628898cd38018ff4331e79262451f464efa9dc5d43eMindy Pereira intent.setDataAndType(conversation.uri, account.mimeType); 6295ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize()); 6307b6d03db55338cbf9717896f99eb20d02bf371e4Mindy Pereira intent.putExtra(Utils.EXTRA_FOLDER, Folder.toString(folder)); 631963cdedf714e6e37c7447413cff767b3f2826b28Mindy Pereira intent.putExtra(Utils.EXTRA_CONVERSATION, conversation); 6329ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira return intent; 6339ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira } 6349ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira 6359ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira /** 6369ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira * Create an intent to open a folder. 637c7968873cb1b64b669733aff1e4e6ef766ebd815Mindy Pereira * 638daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook * @param folder Folder to open. 6390c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal * @param account 640daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook * @return 641daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook */ 64296a7f7aa2de33b1722009a9ee77d2b7b18a8e3e9Mindy Pereira public static Intent createViewFolderIntent(Folder folder, Account account) { 6431672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal if (folder == null || account == null) { 6441672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal LogUtils.wtf( 6451672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal LOG_TAG, "Utils.createViewFolderIntent(%s,%s): Bad input", folder, account); 6461672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal return null; 6471672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal } 6489ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira final Intent intent = new Intent(Intent.ACTION_VIEW); 6495ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK 6505ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira | Intent.FLAG_ACTIVITY_TASK_ON_HOME); 6515ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira intent.setDataAndType(folder.uri, account.mimeType); 6525ad02918f663bc52522a2505de985df9ef5ea347Mindy Pereira intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize()); 6531672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal intent.putExtra(Utils.EXTRA_FOLDER, Folder.toString(folder)); 6549ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira return intent; 6559ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira } 6569ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira 6579ae8ce0578b5b097e59fbd1b09fbfb8f824500fbMindy Pereira /** 6580c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal * Creates an intent to open the default inbox for the given account. 6590c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal * 6600c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal * @param account 6610c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal * @return 6620c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal */ 6630c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal public static Intent createViewInboxIntent(Account account) { 6640c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal if (account == null) { 6650c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal LogUtils.wtf(LOG_TAG, "Utils.createViewInboxIntent(%s): Bad input", account); 6660c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal return null; 6670c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal } 6680c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal final Intent intent = new Intent(Intent.ACTION_VIEW); 6690c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK 6700c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal | Intent.FLAG_ACTIVITY_TASK_ON_HOME); 6710c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal intent.setDataAndType(account.settings.defaultInbox, account.mimeType); 6720c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize()); 6730c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal return intent; 6740c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal } 6750c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal 6760c3c2058f75487713c64634c7f08025fd78ba012Vikram Aggarwal /** 67794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * Helper method to show context-aware Gmail help. 67894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * 67994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * @param context Context to be used to open the help. 68094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * @param fromWhere Information about the activity the user was in 68194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * when they requested help. 68294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook */ 683498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook public static void showHelp(Context context, Account account, String fromWhere) { 684c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook final String urlString = (account != null && account.helpIntentUri != null) ? 685c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook account.helpIntentUri.toString() : null; 686c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook if (TextUtils.isEmpty(urlString) ) { 687498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook LogUtils.e(LOG_TAG, "unable to show help for account: %s", account); 688498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook return; 689498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook } 690c2b791d22684fc4fe4a19bf31bead54434eab354Paul Westbrook final Uri uri = addParamsToUrl(context, urlString); 69194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook Uri.Builder builder = uri.buildUpon(); 69294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook // Add the activity specific information parameter. 69330745b6e9c4d280ac578d1186fbc0cec9bd2a838Paul Westbrook if (!TextUtils.isEmpty(fromWhere)) { 69494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook builder = builder.appendQueryParameter(SMART_HELP_LINK_PARAMETER_NAME, fromWhere); 69594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 69694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 69717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook openUrl(context, builder.build(), null); 69894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 69994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 70094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook /** 70194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * Helper method to open a link in a browser. 70294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * 70394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * @param context Context 70494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * @param uri Uri to open. 70594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook */ 70617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook private static void openUrl(Context context, Uri uri, Bundle optionalExtras) { 70794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook if(uri == null || TextUtils.isEmpty(uri.toString())) { 70894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook LogUtils.wtf(LOG_TAG, "invalid url in Utils.openUrl(): %s", uri); 70994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook return; 71094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 71117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook final Intent intent = new Intent(Intent.ACTION_VIEW, uri); 71217beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook // Fill in any of extras that have been requested. 71317beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook if (optionalExtras != null) { 71417beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook intent.putExtras(optionalExtras); 71517beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook } 71694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); 71717beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook 71894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook context.startActivity(intent); 71994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 72094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 72194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 72294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook private static Uri addParamsToUrl(Context context, String url) { 72394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook url = replaceLocale(url); 72494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook Uri.Builder builder = Uri.parse(url).buildUpon(); 72594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook final String versionCode = getVersionCode(context); 72694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook if (versionCode != null) { 72794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook builder = builder.appendQueryParameter(SMART_LINK_APP_VERSION, versionCode); 72894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 72994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 73094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook return builder.build(); 73194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 73294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 73394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook /** 73494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * Replaces the language/country of the device into the given string. The pattern "%locale%" 73594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * will be replaced with the <language_code>_<country_code> value. 73694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * 73794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * @param str the string to replace the language/country within 73894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * 73994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * @return the string with replacement 74094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook */ 74194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook private static String replaceLocale(String str) { 74294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook // Substitute locale if present in string 74394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook if (str.contains("%locale%")) { 74494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook Locale locale = Locale.getDefault(); 74594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook String tmp = locale.getLanguage() + "_" + locale.getCountry().toLowerCase(); 74694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook str = str.replace("%locale%", tmp); 74794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 74894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook return str; 74994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 75094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook 75194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook /** 75294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook * Returns the version code for the package, or null if it cannot be retrieved. 75394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook */ 75494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook public static String getVersionCode(Context context) { 75594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook if (sVersionCode == null) { 75694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook try { 75794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook sVersionCode = String.valueOf(context.getPackageManager() 75894e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook .getPackageInfo(context.getPackageName(), 0 /* flags */) 75994e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook .versionCode); 76094e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } catch (NameNotFoundException e) { 76194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook LogUtils.e(Utils.LOG_TAG, "Error finding package %s", 76294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook context.getApplicationInfo().packageName); 76394e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 76494e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 76594e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook return sVersionCode; 76694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook } 7671f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira 7681f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira /** 7691f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira * Show the settings screen for the supplied account. 7701f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira */ 7711f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira public static void showSettings(Context context, Account account) { 77261400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook if (account == null) { 77361400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook LogUtils.e(LOG_TAG, "Invalid attempt to show setting screen with null account"); 77461400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook return; 77561400846077d5b4ea2da0374732cfd75e207c0c4Paul Westbrook } 7761f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri); 7771f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira context.startActivity(settingsIntent); 7781f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira } 77968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira 78068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira /** 781e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook * Show the settings screen for the supplied account. 782e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook */ 783e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook public static void showFolderSettings(Context context, Account account, Folder folder) { 7843580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook if (account == null || folder == null) { 7853580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook LogUtils.e(LOG_TAG, "Invalid attempt to show folder settings. account: %s folder: %s", 7863580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook account, folder); 7873580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook return; 7883580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook } 7893580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri); 790e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook 7913580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account); 7923580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook settingsIntent.putExtra(EditSettingsExtras.EXTRA_FOLDER, folder); 793e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook 7943580f1c5d39bd2131511bdd7ab6d1e2c6246e53ePaul Westbrook context.startActivity(settingsIntent); 795e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook } 796e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook 797e550355f79eed5b133c80b88b332de50dcad8697Paul Westbrook /** 79818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook * Show the settings screen for managing all folders. 79918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook */ 80018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook public static void showManageFolder(Context context, Account account) { 80118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook if (account == null) { 80218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook LogUtils.e(LOG_TAG, "Invalid attempt to the manage folders screen with null account"); 80318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook return; 80418babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook } 80518babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook final Intent settingsIntent = new Intent(Intent.ACTION_EDIT, account.settingsIntentUri); 80618babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook 80718babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook settingsIntent.putExtra(EditSettingsExtras.EXTRA_ACCOUNT, account); 80818babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook settingsIntent.putExtra(EditSettingsExtras.EXTRA_MANAGE_FOLDERS, true); 80918babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook 81018babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook context.startActivity(settingsIntent); 81118babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook } 81218babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook 81318babd2334a0c3dfdcb3519299e4dcbd781c8c2dPaul Westbrook /** 814fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira * Show the feedback screen for the supplied account. 815fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira */ 81617beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook public static void sendFeedback(Context context, Account account, boolean reportingProblem) { 817ff2b4f2d920b42d43bd0ccf969fea0e751c8b29aAndy Huang if (account != null && account.sendFeedbackIntentUri != null) { 81817beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook final Bundle optionalExtras = new Bundle(1); 81917beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook optionalExtras.putBoolean( 82017beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook UIProvider.SendFeedbackExtras.EXTRA_REPORTING_PROBLEM, reportingProblem); 82117beb0b8aee78a39a2094a48ba7f55a602f701cbPaul Westbrook openUrl(context, account.sendFeedbackIntentUri, optionalExtras); 8223ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira } 823fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira } 824fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira 825fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira /** 82668f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira * Retrieves the mailbox search query associated with an intent (or null if not available), 82768f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira * doing proper sanitizing (e.g. trims whitespace). 82868f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira */ 82968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira public static String mailSearchQueryForIntent(Intent intent) { 83068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira String query = intent.getStringExtra(SearchManager.QUERY); 83168f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira return TextUtils.isEmpty(query) ? null : query.trim(); 83268f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira } 83388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 83488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang /** 83588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * Split out a filename's extension and return it. 83688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @param filename a file name 83788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @return the file extension (max of 5 chars including period, like ".docx"), or null 83888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang */ 83996f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira public static String getFileExtension(String filename) { 84088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang String extension = null; 84196f6bed5c71d3cec9725ee3efc6465176c3023a8Mindy Pereira int index = !TextUtils.isEmpty(filename) ? filename.lastIndexOf('.') : -1; 84288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang // Limit the suffix to dot + four characters 84388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang if (index >= 0 && filename.length() - index <= FILE_EXTENSION_MAX_CHARS + 1) { 84488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang extension = filename.substring(index); 84588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 84688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang return extension; 84788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 84888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 84988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang /** 85088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * (copied from {@link Intent#normalizeMimeType(String)} for pre-J) 85188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 85288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * Normalize a MIME data type. 85388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 85488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p>A normalized MIME type has white-space trimmed, 85588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * content-type parameters removed, and is lower-case. 85688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * This aligns the type with Android best practices for 85788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * intent filtering. 85888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 85988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p>For example, "text/plain; charset=utf-8" becomes "text/plain". 86088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * "text/x-vCard" becomes "text/x-vcard". 86188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 86288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p>All MIME types received from outside Android (such as user input, 86388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * or external sources like Bluetooth, NFC, or the Internet) should 86488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * be normalized before they are used to create an Intent. 86588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 86688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @param type MIME data type to normalize 86788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @return normalized MIME data type, or null if the input was null 86888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @see {@link #setType} 86988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @see {@link #setTypeAndNormalize} 87088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang */ 87188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang public static String normalizeMimeType(String type) { 87288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang if (type == null) { 87388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang return null; 87488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 87588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 87688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang type = type.trim().toLowerCase(Locale.US); 87788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 87888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang final int semicolonIndex = type.indexOf(';'); 87988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang if (semicolonIndex != -1) { 88088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang type = type.substring(0, semicolonIndex); 88188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 88288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang return type; 88388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 88488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 88588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang /** 88688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * (copied from {@link Uri#normalize()} for pre-J) 88788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 88888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * Return a normalized representation of this Uri. 88988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 89088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p>A normalized Uri has a lowercase scheme component. 89188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * This aligns the Uri with Android best practices for 89288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * intent filtering. 89388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 89488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p>For example, "HTTP://www.android.com" becomes 89588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * "http://www.android.com" 89688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 89788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p>All URIs received from outside Android (such as user input, 89888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * or external sources like Bluetooth, NFC, or the Internet) should 89988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * be normalized before they are used to create an Intent. 90088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 90188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * <p class="note">This method does <em>not</em> validate bad URI's, 90288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * or 'fix' poorly formatted URI's - so do not use it for input validation. 90388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * A Uri will always be returned, even if the Uri is badly formatted to 90488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * begin with and a scheme component cannot be found. 90588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * 90688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @return normalized Uri (never null) 90788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @see {@link android.content.Intent#setData} 90888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang * @see {@link #setNormalizedData} 90988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang */ 91088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang public static Uri normalizeUri(Uri uri) { 91188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang String scheme = uri.getScheme(); 91288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang if (scheme == null) return uri; // give up 91388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang String lowerScheme = scheme.toLowerCase(Locale.US); 91488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang if (scheme.equals(lowerScheme)) return uri; // no change 91588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 91688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang return uri.buildUpon().scheme(lowerScheme).build(); 91788fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 91888fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 91988fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang public static Intent setIntentTypeAndNormalize(Intent intent, String type) { 92088fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang return intent.setType(normalizeMimeType(type)); 92188fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 92288fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 92388fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang public static Intent setIntentDataAndTypeAndNormalize(Intent intent, Uri data, String type) { 92488fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang return intent.setDataAndType(normalizeUri(data), normalizeMimeType(type)); 92588fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang } 92688fc42e48ee4e927eb77e5cab23f2f5151cac649Andy Huang 927b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira public static int getTransparentColor(int color) { 928b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira return 0x00ffffff & color; 929b5080d5335d2aa445a660ad426ab008750be24cbMindy Pereira } 930863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira 931863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira public static void setMenuItemVisibility(Menu menu, int itemId, boolean shouldShow) { 932863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira final MenuItem item = menu.findItem(itemId); 933863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira if (item == null) { 934863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira return; 935863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira } 936863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira item.setVisible(shouldShow); 937863e44160d9175023d30e7e225ecb69ad3892eecMindy Pereira } 93892551d057965689f1952faeb06763f0762bc717fMindy Pereira 93992551d057965689f1952faeb06763f0762bc717fMindy Pereira /** 94092551d057965689f1952faeb06763f0762bc717fMindy Pereira * Parse a string (possibly null or empty) into a URI. If the string is null 94192551d057965689f1952faeb06763f0762bc717fMindy Pereira * or empty, null is returned back. Otherwise an empty URI is returned. 94292551d057965689f1952faeb06763f0762bc717fMindy Pereira * 94392551d057965689f1952faeb06763f0762bc717fMindy Pereira * @param uri 94492551d057965689f1952faeb06763f0762bc717fMindy Pereira * @return a valid URI, possibly {@link android.net.Uri#EMPTY} 94592551d057965689f1952faeb06763f0762bc717fMindy Pereira */ 94692551d057965689f1952faeb06763f0762bc717fMindy Pereira public static Uri getValidUri(String uri) { 947b378d64bab3c7517794ad7e2aee1d06c074e99eeMindy Pereira if (TextUtils.isEmpty(uri) || uri == JSONObject.NULL) 94892551d057965689f1952faeb06763f0762bc717fMindy Pereira return Uri.EMPTY; 94992551d057965689f1952faeb06763f0762bc717fMindy Pereira return Uri.parse(uri); 95092551d057965689f1952faeb06763f0762bc717fMindy Pereira } 95110ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank 95210ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank public static boolean isEmpty(Uri uri) { 95310ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank return uri == null || uri.equals(Uri.EMPTY); 95410ddc197b8c3df994ee3575b7abac4c36ea81c1fMarc Blank } 955ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 95669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal /** 95769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * Executes an out-of-band command on the cursor. 95869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param cursor 95969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param request Bundle with all keys and values set for the command. 96069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param key The string value against which we will check for success or failure 96169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @return true if the operation was a success. 96269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal */ 96369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal private static boolean executeConversationCursorCommand( 96469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal Cursor cursor, Bundle request, String key) { 96569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final Bundle response = cursor.respond(request); 96669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final String result = response.getString(key, 967ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_FAILED); 968ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 969ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang return UIProvider.ConversationCursorCommand.COMMAND_RESPONSE_OK.equals(result); 970ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang } 971ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 972ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang /** 973ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * Commands a cursor representing a set of conversations to disable any network requests it may 974ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * do as clients move through the cursor. 975ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * 976ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * @param cursor a conversation cursor 977ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * @return true iff the provider supports network requests and they were previously enabled 978ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang */ 979ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang public static boolean disableConversationCursorNetworkAccess(Cursor cursor) { 98069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final Bundle request = new Bundle(); 98169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_ALLOW_NETWORK_ACCESS; 98269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal request.putBoolean(key, false); 98369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal return executeConversationCursorCommand(cursor, request, key); 984ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang } 985ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang 986ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang /** 987ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * Commands a cursor representing a set of conversations to [re-]enable any network requests it 988ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * may do as clients move through the cursor. 989ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * 990ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * @param cursor a conversation cursor 991ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang * @return true iff the provider supports network requests and they are successfully enabled 992ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang */ 993ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang public static boolean enableConversationCursorNetworkAccess(Cursor cursor) { 99469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final Bundle request = new Bundle(); 99569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_ALLOW_NETWORK_ACCESS; 99669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal request.putBoolean(key, true); 99769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal return executeConversationCursorCommand(cursor, request, key); 998ca85441f810fdcdd50d285ac5f0319078050c90dAndy Huang } 9999f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook 10009f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook /** 10019f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook * Commands a cursor representing a set of conversations to set its visibility state. 10029f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook * 10039f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook * @param cursor a conversation cursor 100469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param visible true if the conversation list is visible, false otherwise. 100569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param isFirstSeen true if you want to notify the cursor that this conversation list was seen 100669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * for the first time: the user launched the app into it, or the user switched from some 100769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * other folder into it. 10089f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook */ 100969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal public static void setConversationCursorVisibility( 101069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal Cursor cursor, boolean visible, boolean isFirstSeen) { 101169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal new MarkConversationCursorVisibleTask(cursor, visible, isFirstSeen).execute(); 10129f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook } 10139f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook 10149f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook /** 101569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * Async task for marking conversations "seen" and informing the cursor that the folder was 101669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * seen for the first time by the UI. 10179f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook */ 10189f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook private static class MarkConversationCursorVisibleTask extends AsyncTask<Void, Void, Void> { 10199f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook private final Cursor mCursor; 10209f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook private final boolean mVisible; 102169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal private final boolean mIsFirstSeen; 102269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal 102369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal /** 102469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * Create a new task with the given cursor, with the given visibility and 102569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * 102669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param cursor 102769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param isVisible true if the conversation list is visible, false otherwise. 102869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * @param isFirstSeen true if the folder was shown for the first time: either the user has 102969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal * just switched to it, or the user started the app in this folder. 103069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal */ 103169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal public MarkConversationCursorVisibleTask( 103269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal Cursor cursor, boolean isVisible, boolean isFirstSeen) { 10339f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook mCursor = cursor; 103469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal mVisible = isVisible; 103569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal mIsFirstSeen = isFirstSeen; 10369f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook } 10379f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook 10389f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook @Override 10399f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook protected Void doInBackground(Void... params) { 104069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal if (mCursor == null) { 104169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal return null; 104269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal } 104369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final Bundle request = new Bundle(); 104469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal if (mIsFirstSeen) { 104569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal request.putBoolean( 104669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal UIProvider.ConversationCursorCommand.COMMAND_KEY_ENTERED_FOLDER, true); 10479f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook } 104869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal final String key = UIProvider.ConversationCursorCommand.COMMAND_KEY_SET_VISIBILITY; 104969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal request.putBoolean(key, mVisible); 105069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal executeConversationCursorCommand(mCursor, request, key); 10519f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook return null; 10529f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook } 10539f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook } 10549fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira 1055a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 1056a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook /** 1057a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * This utility method returns the conversation ID at the current cursor position. 1058a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook * @return the conversation id at the cursor. 1059a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook */ 1060a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook public static long getConversationId(ConversationCursor cursor) { 1061d781c02b91162afa91ba7d7477f40b7eec2aacedPaul Westbrook return cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN); 1062a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook } 1063a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook 10649fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira /** 10659fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira * @return whether to show two pane or single pane search results. 10669fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira */ 10679fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira public static boolean showTwoPaneSearchResults(Context context) { 10689fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira return context.getResources().getBoolean(R.bool.show_two_pane_search_results); 10699fa43cab6a83c70a7739c3d5300fc354856f22ffMindy Pereira } 1070a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira 1071a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira /** 1072a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira * Return whether menus should show the disabled archive menu item or just 1073a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira * remove it when archive is not available. 1074a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira */ 1075a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira public static boolean shouldShowDisabledArchiveIcon(Context context) { 1076a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira return context.getResources().getBoolean(R.bool.show_disabled_archive_menu_item); 1077a41a57a186b84f98b497e109cff94aa3c1362697Mindy Pereira } 107855137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira 107955137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira public static int getDefaultFolderBackgroundColor(Context context) { 108055137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira if (sDefaultFolderBackgroundColor == -1) { 108155137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira sDefaultFolderBackgroundColor = context.getResources().getColor( 108255137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira R.color.default_folder_background_color); 108355137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira } 108455137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira return sDefaultFolderBackgroundColor; 108555137b88f01521d15a0677b7105416bbc6528a49Mindy Pereira } 1086443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal 1087a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang public static void fixSubTreeLayoutIfOrphaned(View v, String tag) { 1088a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang if (isLayoutSubTreeOrphaned(v)) { 1089a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang markDirtyTillRoot(tag, v); 1090a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 1091a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 1092a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang 1093a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang /** 1094601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook * Returns the count that should be shown for the specified folder. This method should be used 1095601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook * when the UI wants to display an "unread" count. For most labels, the returned value will be 1096601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook * the unread count, but for some folder types (outbox, drafts, trash) this will return the 1097601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook * total count. 1098601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook */ 1099601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook public static int getFolderUnreadDisplayCount(Folder folder) { 1100601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook final int count; 1101601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook if (folder != null) { 1102601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook switch (folder.type) { 1103601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook case UIProvider.FolderType.DRAFT: 1104601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook case UIProvider.FolderType.TRASH: 1105601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook case UIProvider.FolderType.OUTBOX: 1106601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook count = folder.totalCount; 1107601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook break; 1108601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook default: 1109601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook count = folder.unreadCount; 1110601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook break; 1111601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook } 1112601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook } else { 1113601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook count = 0; 1114601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook } 1115601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook return count; 1116601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook } 1117601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook 1118601ab03706b6674c4154ca313080b6549bdb562fPaul Westbrook /** 1119a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * An orphaned subtree is one where child views have requested layout, but at least one 1120a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * ancestor is not marked for layout. In this scenario, any future layout requests on the root 1121a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * will ignore the orphaned subtree, and we have to force the issue. 1122a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * <p> 1123a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * Note: this might be okay if it's true while a layout pass is in progress. Callers should 1124a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * only trust this return value if not currently in layout. 1125a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang * 1126a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang */ 1127a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang public static boolean isLayoutSubTreeOrphaned(View v) { 1128a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang if (v == null) { 1129a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang return false; 1130a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 1131a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang 1132a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang final boolean isLayoutRequested = v.isLayoutRequested(); 1133a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang ViewParent parent = v.getParent(); 1134a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang while (isLayoutRequested && parent != null) { 1135a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang if (!parent.isLayoutRequested()) { 1136a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang return true; 1137a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 1138a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang parent = parent.getParent(); 1139a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 1140a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang return false; 1141a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 1142a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang 1143443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal /** 1144443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal * Hacky method to allow invalidating views all the way up the hierarchy. 1145443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal */ 1146443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal public static void markDirtyTillRoot(String message, View v) { 1147d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// LogUtils.d(VIEW_DEBUGGING_TAG, "%s: markingDirtyTillRoot", message); 1148d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// v.invalidate(); 1149d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// ViewParent parent = v.getParent(); 1150d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// while (parent != null) { 1151d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// parent.requestLayout(); 1152d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// parent = parent.getParent(); 1153d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang// } 1154443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal } 1155443275a9e4f9f66d982cbcadca215be239663a7aVikram Aggarwal 11567517e3b61b898a57f19be0671f70d58a82224643Andy Huang public static void checkRequestLayout(View v) { 11577517e3b61b898a57f19be0671f70d58a82224643Andy Huang boolean inLayout = false; 11587517e3b61b898a57f19be0671f70d58a82224643Andy Huang final View root = v.getRootView(); 11597517e3b61b898a57f19be0671f70d58a82224643Andy Huang 1160d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang if (root == null || v.isLayoutRequested()) { 11617517e3b61b898a57f19be0671f70d58a82224643Andy Huang return; 11627517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 11637517e3b61b898a57f19be0671f70d58a82224643Andy Huang 11647517e3b61b898a57f19be0671f70d58a82224643Andy Huang final Error e = new Error(); 11657517e3b61b898a57f19be0671f70d58a82224643Andy Huang for (StackTraceElement ste : e.getStackTrace()) { 11667517e3b61b898a57f19be0671f70d58a82224643Andy Huang if ("android.view.ViewGroup".equals(ste.getClassName()) 11677517e3b61b898a57f19be0671f70d58a82224643Andy Huang && "layout".equals(ste.getMethodName())) { 11687517e3b61b898a57f19be0671f70d58a82224643Andy Huang inLayout = true; 11697517e3b61b898a57f19be0671f70d58a82224643Andy Huang break; 11707517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 11717517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 11727517e3b61b898a57f19be0671f70d58a82224643Andy Huang if (inLayout && !v.isLayoutRequested()) { 1173bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal LogUtils.i(VIEW_DEBUGGING_TAG, 11747517e3b61b898a57f19be0671f70d58a82224643Andy Huang e, "WARNING: in requestLayout during layout pass, view=%s", v); 11757517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 11767517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 11777517e3b61b898a57f19be0671f70d58a82224643Andy Huang 11787517e3b61b898a57f19be0671f70d58a82224643Andy Huang /** 11797517e3b61b898a57f19be0671f70d58a82224643Andy Huang * Logs extra information about the views to help find the problem with blank fragments. 11807517e3b61b898a57f19be0671f70d58a82224643Andy Huang * To turn on this debugging, enable the "MailBlankFragment" tag with 11817517e3b61b898a57f19be0671f70d58a82224643Andy Huang * adb shell setprop log.tag.MailBlankFragment VERBOSE 11827517e3b61b898a57f19be0671f70d58a82224643Andy Huang * @param message 11837517e3b61b898a57f19be0671f70d58a82224643Andy Huang * @param v 11847517e3b61b898a57f19be0671f70d58a82224643Andy Huang */ 11857517e3b61b898a57f19be0671f70d58a82224643Andy Huang public static void dumpLayoutRequests(String message, View v) { 1186bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal LogUtils.d(VIEW_DEBUGGING_TAG, "dumpLayoutRequests: %s", message); 11877517e3b61b898a57f19be0671f70d58a82224643Andy Huang 11887517e3b61b898a57f19be0671f70d58a82224643Andy Huang while (v != null) { 1189bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal LogUtils.d(VIEW_DEBUGGING_TAG, 11907517e3b61b898a57f19be0671f70d58a82224643Andy Huang "view item: %s mw/mh=%d/%d w/h=%d/%d layoutRequested=%s vis=%s id=0x%x", 1191d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang v.getClass().getSimpleName(), v.getMeasuredWidth(), v.getMeasuredHeight(), 1192d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang v.getWidth(), v.getHeight(), v.isLayoutRequested(), v.getVisibility(), 1193d736a385f1ce5e1a59a7953f9245526b37126809Andy Huang v.getId()); 11947517e3b61b898a57f19be0671f70d58a82224643Andy Huang 11957517e3b61b898a57f19be0671f70d58a82224643Andy Huang ViewParent vp = v.getParent(); 11967517e3b61b898a57f19be0671f70d58a82224643Andy Huang if (vp instanceof ViewGroup) { 11977517e3b61b898a57f19be0671f70d58a82224643Andy Huang v = (ViewGroup) vp; 11987517e3b61b898a57f19be0671f70d58a82224643Andy Huang } else { 11997517e3b61b898a57f19be0671f70d58a82224643Andy Huang if (vp != null) { 12007517e3b61b898a57f19be0671f70d58a82224643Andy Huang // this is the root. can't really get access to this guy 1201bc88f16af48d9538b79cbeab88cd275cb769a67dVikram Aggarwal LogUtils.d(VIEW_DEBUGGING_TAG, 12027517e3b61b898a57f19be0671f70d58a82224643Andy Huang "view item: (ViewRootImpl) isLayoutRequested=%s\n", 12037517e3b61b898a57f19be0671f70d58a82224643Andy Huang vp.isLayoutRequested()); 12047517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 12057517e3b61b898a57f19be0671f70d58a82224643Andy Huang v = null; 12067517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 12077517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 12087517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 12097b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira} 1210