196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* 296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License. 696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at 796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and 1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under the License. 1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1731d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankpackage com.android.emailcommon.utility; 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 197e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onukiimport android.app.Activity; 20d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onukiimport android.app.Fragment; 21e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.content.ContentResolver; 2209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport android.content.ContentUris; 2309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport android.content.ContentValues; 24f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.Context; 25e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.database.Cursor; 26adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onukiimport android.database.CursorWrapper; 27a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onukiimport android.graphics.Typeface; 28bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onukiimport android.net.Uri; 2959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onukiimport android.os.AsyncTask; 3009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport android.os.Environment; 31082443978595bee3b7907563dc7665f908872e20Makoto Onukiimport android.os.Handler; 32f11295f3352f7c89fc92c73731e41b057e236969Makoto Onukiimport android.os.Looper; 3319b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onukiimport android.os.StrictMode; 34d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onukiimport android.provider.OpenableColumns; 35a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onukiimport android.text.Spannable; 36a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onukiimport android.text.SpannableString; 37a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onukiimport android.text.SpannableStringBuilder; 38128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport android.text.TextUtils; 39a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onukiimport android.text.style.StyleSpan; 406cec1104fe8863fce2ee86ff5145076e6c436a00Doug Zongkerimport android.util.Base64; 41082443978595bee3b7907563dc7665f908872e20Makoto Onukiimport android.widget.ListView; 42f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.widget.TextView; 437e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onukiimport android.widget.Toast; 44e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler 45190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.Logging; 46190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.Account; 47190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.EmailContent; 48190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns; 49190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment; 50190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.EmailContent.AttachmentColumns; 51190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.EmailContent.HostAuthColumns; 52190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.EmailContent.Message; 53190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.HostAuth; 54190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blankimport com.android.emailcommon.provider.ProviderUnavailableException; 555a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport com.android.mail.utils.LogUtils; 56190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank 577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.io.ByteArrayInputStream; 5844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onukiimport java.io.File; 59d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onukiimport java.io.FileDescriptor; 60d755cdce13217d25ac33169b5e410709636255c4Makoto Onukiimport java.io.FileNotFoundException; 6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException; 6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream; 6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStreamReader; 64d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onukiimport java.io.PrintWriter; 65d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onukiimport java.io.StringWriter; 6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.UnsupportedEncodingException; 67ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onukiimport java.net.URI; 68ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onukiimport java.net.URISyntaxException; 6920225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.ByteBuffer; 7020225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.CharBuffer; 7120225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.charset.Charset; 72697f98aea5a8344a17ca5e9b0410d484fdce6555Makoto Onukiimport java.security.MessageDigest; 73d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport java.security.NoSuchAlgorithmException; 74f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onukiimport java.util.ArrayList; 75767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onukiimport java.util.Collection; 76989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.GregorianCalendar; 774e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onukiimport java.util.HashSet; 784e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onukiimport java.util.Set; 79989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.TimeZone; 80128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport java.util.regex.Pattern; 8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class Utility { 8320225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki public static final Charset UTF_8 = Charset.forName("UTF-8"); 847e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static final Charset ASCII = Charset.forName("US-ASCII"); 857e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 867e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static final String[] EMPTY_STRINGS = new String[0]; 87f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki public static final Long[] EMPTY_LONGS = new Long[0]; 8820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki 89128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki // "GMT" + "+" or "-" + 4 digits 90128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE = 91128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki Pattern.compile("GMT([-+]\\d{4})$"); 92128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki 93f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki private static Handler sMainThreadHandler; 94f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki 95f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki /** 96f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki * @return a {@link Handler} tied to the main thread. 97f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki */ 98f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki public static Handler getMainThreadHandler() { 99f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki if (sMainThreadHandler == null) { 100f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki // No need to synchronize -- it's okay to create an extra Handler, which will be used 101f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki // only once and then thrown away. 102f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki sMainThreadHandler = new Handler(Looper.getMainLooper()); 103f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki } 104f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki return sMainThreadHandler; 105f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki } 106f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki 10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public final static String readInputStream(InputStream in, String encoding) throws IOException { 10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project InputStreamReader reader = new InputStreamReader(in, encoding); 10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project StringBuffer sb = new StringBuffer(); 11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int count; 11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project char[] buf = new char[512]; 11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project while ((count = reader.read(buf)) != -1) { 11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append(buf, 0, count); 11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return sb.toString(); 11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public final static boolean arrayContains(Object[] a, Object o) { 119190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank int index = arrayIndex(a, o); 120190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank return (index >= 0); 121190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank } 122190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank 123190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank public final static int arrayIndex(Object[] a, Object o) { 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0, count = a.length; i < count; i++) { 12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (a[i].equals(o)) { 126190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank return i; 12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 129190b2fb8a1be1d72475e2a60d0f00422712ee094Marc Blank return -1; 13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 133fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy * Returns a concatenated string containing the output of every Object's 134fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy * toString() method, each separated by the given separator character. 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 136fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy public static String combine(Object[] parts, char separator) { 13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (parts == null) { 13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project StringBuffer sb = new StringBuffer(); 14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0; i < parts.length; i++) { 14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append(parts[i].toString()); 14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (i < parts.length - 1) { 144fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy sb.append(separator); 14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return sb.toString(); 14896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 150e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank public static boolean isPortFieldValid(TextView view) { 151e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank CharSequence chars = view.getText(); 152e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank if (TextUtils.isEmpty(chars)) return false; 153e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank Integer port; 154e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank // In theory, we can't get an illegal value here, since the field is monitored for valid 155e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank // numeric input. But this might be used elsewhere without such a check. 156e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank try { 157e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank port = Integer.parseInt(chars.toString()); 158e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank } catch (NumberFormatException e) { 159e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank return false; 160e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank } 161e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank return port > 0 && port < 65536; 162ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki } 163ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki 164ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki /** 165ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki * Validate a hostname name field. 166ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki * 167ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki * Because we just use the {@link URI} class for validation, it'll accept some invalid 168ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki * host names, but it works well enough... 169ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki */ 170ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki public static boolean isServerNameValid(TextView view) { 171ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki return isServerNameValid(view.getText().toString()); 172ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki } 173ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki 174ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki public static boolean isServerNameValid(String serverName) { 175ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki serverName = serverName.trim(); 176ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki if (TextUtils.isEmpty(serverName)) { 177ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki return false; 178ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki } 179ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki try { 180ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki URI uri = new URI( 181ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki "http", 182ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki null, 183ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki serverName, 184ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki -1, 185ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki null, // path 186ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki null, // query 187ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki null); 188ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki return true; 189ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki } catch (URISyntaxException e) { 190ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki return false; 191ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki } 192ce4cce05b2ee5ea2d9629c189a79f7f30778f534Makoto Onuki } 19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * A fast version of URLDecoder.decode() that works only with UTF-8 and does only two 19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * allocations. This version is around 3x as fast as the standard one and I'm using it 19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * hundreds of times in places that slow down the UI, so it helps. 19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String fastUrlDecode(String s) { 20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project byte[] bytes = s.getBytes("UTF-8"); 20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project byte ch; 20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int length = 0; 20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0, count = bytes.length; i < count; i++) { 20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project ch = bytes[i]; 20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (ch == '%') { 20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int h = (bytes[i + 1] - '0'); 20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int l = (bytes[i + 2] - '0'); 20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (h > 9) { 21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project h -= 7; 21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (l > 9) { 21396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project l -= 7; 21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bytes[length] = (byte) ((h << 4) | l); 21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project i += 2; 21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else if (ch == '+') { 21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bytes[length] = ' '; 22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else { 22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bytes[length] = bytes[i]; 22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project length++; 22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return new String(bytes, 0, length, "UTF-8"); 22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project catch (UnsupportedEncodingException uee) { 22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 232da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler private final static String HOSTAUTH_WHERE_CREDENTIALS = HostAuthColumns.ADDRESS + " like ?" 2330ff0e155c51250bedc35b5b585003c8cb87f2244Marc Blank + " and " + HostAuthColumns.LOGIN + " like ? ESCAPE '\\'" 234da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler + " and " + HostAuthColumns.PROTOCOL + " not like \"smtp\""; 235da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler private final static String ACCOUNT_WHERE_HOSTAUTH = AccountColumns.HOST_AUTH_KEY_RECV + "=?"; 236da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler 237da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler /** 238da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * Look for an existing account with the same username & server 239da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * 240da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * @param context a system context 241da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * @param allowAccountId this account Id will not trigger (when editing an existing account) 2429d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank * @param hostName the server's address 2439d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank * @param userLogin the user's login string 2449d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank * @result null = no matching account found. Account = matching account 245da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler */ 2469d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank public static Account findExistingAccount(Context context, long allowAccountId, 2479d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank String hostName, String userLogin) { 248da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler ContentResolver resolver = context.getContentResolver(); 2490ff0e155c51250bedc35b5b585003c8cb87f2244Marc Blank String userName = userLogin.replace("_", "\\_"); 250da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler Cursor c = resolver.query(HostAuth.CONTENT_URI, HostAuth.ID_PROJECTION, 2510ff0e155c51250bedc35b5b585003c8cb87f2244Marc Blank HOSTAUTH_WHERE_CREDENTIALS, new String[] { hostName, userName }, null); 25274143e89d538ce74e8c95e04cee56dca9412a7bdMarc Blank if (c == null) throw new ProviderUnavailableException(); 253da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler try { 254da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler while (c.moveToNext()) { 255da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler long hostAuthId = c.getLong(HostAuth.ID_PROJECTION_COLUMN); 2569d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank // Find account with matching hostauthrecv key, and return it 257da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler Cursor c2 = resolver.query(Account.CONTENT_URI, Account.ID_PROJECTION, 258da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler ACCOUNT_WHERE_HOSTAUTH, new String[] { Long.toString(hostAuthId) }, null); 259da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler try { 260da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler while (c2.moveToNext()) { 261da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler long accountId = c2.getLong(Account.ID_PROJECTION_COLUMN); 262da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler if (accountId != allowAccountId) { 263da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler Account account = Account.restoreAccountWithId(context, accountId); 264da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler if (account != null) { 2659d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank return account; 266da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 267da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 268da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 269da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } finally { 270da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler c2.close(); 271da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 272da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 273da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } finally { 274da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler c.close(); 275da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 276da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler 277da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler return null; 278da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 279eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler 280eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler /** 281463584d23f747cfbbd9856b39390269342363b41Tony Mantler * This only actually matches against the email address. It's technically kosher to allow the 282463584d23f747cfbbd9856b39390269342363b41Tony Mantler * same address across different account types, but that's a pretty rare use case and isn't well 283463584d23f747cfbbd9856b39390269342363b41Tony Mantler * handled in the UI. 284463584d23f747cfbbd9856b39390269342363b41Tony Mantler * 285463584d23f747cfbbd9856b39390269342363b41Tony Mantler * @param context context 286463584d23f747cfbbd9856b39390269342363b41Tony Mantler * @param syncAuthority the account manager type to check against or null for all types 287463584d23f747cfbbd9856b39390269342363b41Tony Mantler * @param address email address to match against 288463584d23f747cfbbd9856b39390269342363b41Tony Mantler * @return account name for match found or null 289463584d23f747cfbbd9856b39390269342363b41Tony Mantler */ 290463584d23f747cfbbd9856b39390269342363b41Tony Mantler public static String findExistingAccount(final Context context, final String syncAuthority, 291463584d23f747cfbbd9856b39390269342363b41Tony Mantler final String address) { 292463584d23f747cfbbd9856b39390269342363b41Tony Mantler final ContentResolver resolver = context.getContentResolver(); 293463584d23f747cfbbd9856b39390269342363b41Tony Mantler final Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, 294463584d23f747cfbbd9856b39390269342363b41Tony Mantler AccountColumns.EMAIL_ADDRESS + "=?", new String[] {address}, null); 295463584d23f747cfbbd9856b39390269342363b41Tony Mantler try { 296463584d23f747cfbbd9856b39390269342363b41Tony Mantler if (!c.moveToFirst()) { 297463584d23f747cfbbd9856b39390269342363b41Tony Mantler return null; 298463584d23f747cfbbd9856b39390269342363b41Tony Mantler } 299463584d23f747cfbbd9856b39390269342363b41Tony Mantler return c.getString(c.getColumnIndex(Account.DISPLAY_NAME)); 300463584d23f747cfbbd9856b39390269342363b41Tony Mantler /* 301463584d23f747cfbbd9856b39390269342363b41Tony Mantler do { 302463584d23f747cfbbd9856b39390269342363b41Tony Mantler if (syncAuthority != null) { 303463584d23f747cfbbd9856b39390269342363b41Tony Mantler // TODO: actually compare the sync authority to allow creating the same account 304463584d23f747cfbbd9856b39390269342363b41Tony Mantler // on different protocols. Sadly this code can't directly access the service info 305463584d23f747cfbbd9856b39390269342363b41Tony Mantler } else { 306463584d23f747cfbbd9856b39390269342363b41Tony Mantler final Account account = new Account(); 307463584d23f747cfbbd9856b39390269342363b41Tony Mantler account.restore(c); 308463584d23f747cfbbd9856b39390269342363b41Tony Mantler return account.mDisplayName; 309463584d23f747cfbbd9856b39390269342363b41Tony Mantler } 310463584d23f747cfbbd9856b39390269342363b41Tony Mantler } while (c.moveToNext()); 311463584d23f747cfbbd9856b39390269342363b41Tony Mantler */ 312463584d23f747cfbbd9856b39390269342363b41Tony Mantler } finally { 313463584d23f747cfbbd9856b39390269342363b41Tony Mantler c.close(); 314463584d23f747cfbbd9856b39390269342363b41Tony Mantler } 315463584d23f747cfbbd9856b39390269342363b41Tony Mantler /* 316463584d23f747cfbbd9856b39390269342363b41Tony Mantler return null; 317463584d23f747cfbbd9856b39390269342363b41Tony Mantler */ 318463584d23f747cfbbd9856b39390269342363b41Tony Mantler } 319463584d23f747cfbbd9856b39390269342363b41Tony Mantler 320463584d23f747cfbbd9856b39390269342363b41Tony Mantler /** 321eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler * Generate a random message-id header for locally-generated messages. 322eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler */ 323eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler public static String generateMessageId() { 324eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler StringBuffer sb = new StringBuffer(); 325eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append("<"); 326eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler for (int i = 0; i < 24; i++) { 327eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append(Integer.toString((int)(Math.random() * 35), 36)); 328eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler } 329eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append("."); 330eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append(Long.toString(System.currentTimeMillis())); 331eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append("@email.android.com>"); 332eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler return sb.toString(); 333eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler } 334eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler 335989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda /** 336989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * Generate a time in milliseconds from a date string that represents a date/time in GMT 337fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy * @param date string in format 20090211T180303Z (rfc2445, iCalendar). 338989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @return the time in milliseconds (since Jan 1, 1970) 339989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda */ 340989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda public static long parseDateTimeToMillis(String date) { 341989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda GregorianCalendar cal = parseDateTimeToCalendar(date); 342989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda return cal.getTimeInMillis(); 343989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda } 344989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda 345989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda /** 346989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * Generate a GregorianCalendar from a date string that represents a date/time in GMT 347fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy * @param date string in format 20090211T180303Z (rfc2445, iCalendar). 348989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @return the GregorianCalendar 349989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda */ 350989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda public static GregorianCalendar parseDateTimeToCalendar(String date) { 351989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)), 352989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)), 353989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(9, 11)), Integer.parseInt(date.substring(11, 13)), 354989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(13, 15))); 355989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda cal.setTimeZone(TimeZone.getTimeZone("GMT")); 356989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda return cal; 357989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda } 358989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda 359989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda /** 360989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * Generate a time in milliseconds from an email date string that represents a date/time in GMT 361fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy * @param date string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339) 362989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @return the time in milliseconds (since Jan 1, 1970) 363989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda */ 364989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda public static long parseEmailDateTimeToMillis(String date) { 365989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)), 366989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(5, 7)) - 1, Integer.parseInt(date.substring(8, 10)), 367989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(11, 13)), Integer.parseInt(date.substring(14, 16)), 368989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(17, 19))); 369989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda cal.setTimeZone(TimeZone.getTimeZone("GMT")); 370989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda return cal.getTimeInMillis(); 371989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda } 37220225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki 3737e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private static byte[] encode(Charset charset, String s) { 37420225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki if (s == null) { 37520225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki return null; 37620225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki } 3777e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki final ByteBuffer buffer = charset.encode(CharBuffer.wrap(s)); 37820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki final byte[] bytes = new byte[buffer.limit()]; 37920225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki buffer.get(bytes); 38020225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki return bytes; 38120225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki } 38288a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki 3837e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private static String decode(Charset charset, byte[] b) { 384fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki if (b == null) { 385fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki return null; 386fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki } 3877e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki final CharBuffer cb = charset.decode(ByteBuffer.wrap(b)); 388fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki return new String(cb.array(), 0, cb.length()); 389fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki } 390fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki 3917e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Converts a String to UTF-8 */ 3927e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static byte[] toUtf8(String s) { 3937e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return encode(UTF_8, s); 3947e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 3957e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 3967e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Builds a String from UTF-8 bytes */ 3977e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static String fromUtf8(byte[] b) { 3987e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return decode(UTF_8, b); 3997e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 4007e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 4017e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Converts a String to ASCII bytes */ 4027e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static byte[] toAscii(String s) { 4037e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return encode(ASCII, s); 4047e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 4057e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 4067e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Builds a String from ASCII bytes */ 4077e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static String fromAscii(byte[] b) { 4087e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return decode(ASCII, b); 4097e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 4107e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 41188a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki /** 41288a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki * @return true if the input is the first (or only) byte in a UTF-8 character 41388a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki */ 41488a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki public static boolean isFirstUtf8Byte(byte b) { 41588a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki // If the top 2 bits is '10', it's not a first byte. 41688a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki return (b & 0xc0) != 0x80; 41788a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki } 418dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki 419dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki public static String byteToHex(int b) { 420dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki return byteToHex(new StringBuilder(), b).toString(); 421dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki } 422dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki 423dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki public static StringBuilder byteToHex(StringBuilder sb, int b) { 424dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki b &= 0xFF; 425dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki sb.append("0123456789ABCDEF".charAt(b >> 4)); 426dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki sb.append("0123456789ABCDEF".charAt(b & 0xF)); 427dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki return sb; 428dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki } 429eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank 430eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank public static String replaceBareLfWithCrlf(String str) { 431eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank return str.replace("\r", "").replace("\n", "\r\n"); 432eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank } 43359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki 43459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki /** 43559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * Cancel an {@link AsyncTask}. If it's already running, it'll be interrupted. 43659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki */ 43759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki public static void cancelTaskInterrupt(AsyncTask<?, ?, ?> task) { 43859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki cancelTask(task, true); 43959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki } 44059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki 44159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki /** 4424a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki * Cancel an {@link EmailAsyncTask}. If it's already running, it'll be interrupted. 4434a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki */ 4444a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki public static void cancelTaskInterrupt(EmailAsyncTask<?, ?, ?> task) { 4454a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki if (task != null) { 4464a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki task.cancel(true); 4474a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki } 4484a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki } 4494a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki 4504a893e60b468e0b67db522c047fa7fab057cfd6fMakoto Onuki /** 45159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * Cancel an {@link AsyncTask}. 45259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * 45359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this 45459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * task should be interrupted; otherwise, in-progress tasks are allowed 45559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * to complete. 45659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki */ 45759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) { 45859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) { 45959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki task.cancel(mayInterruptIfRunning); 46059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki } 46159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki } 462d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki 4633a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank public static String getSmallHash(final String value) { 464d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final MessageDigest sha; 465d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki try { 466d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki sha = MessageDigest.getInstance("SHA-1"); 467d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } catch (NoSuchAlgorithmException impossible) { 468d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return null; 469d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 470697f98aea5a8344a17ca5e9b0410d484fdce6555Makoto Onuki sha.update(Utility.toUtf8(value)); 471d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final int hash = getSmallHashFromSha1(sha.digest()); 472d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return Integer.toString(hash); 473d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 474d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki 475d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki /** 476d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki * @return a non-negative integer generated from 20 byte SHA-1 hash. 477d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki */ 478d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki /* package for testing */ static int getSmallHashFromSha1(byte[] sha1) { 479d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final int offset = sha1[19] & 0xf; // SHA1 is 20 bytes. 480d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return ((sha1[offset] & 0x7f) << 24) 481d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki | ((sha1[offset + 1] & 0xff) << 16) 482d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki | ((sha1[offset + 2] & 0xff) << 8) 483d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki | ((sha1[offset + 3] & 0xff)); 484d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 485128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki 486128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki /** 487128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * Try to make a date MIME(RFC 2822/5322)-compliant. 488128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * 489128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * It fixes: 490128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700" 491128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * (4 digit zone value can't be preceded by "GMT") 492128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * We got a report saying eBay sends a date in this format 493128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki */ 494128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki public static String cleanUpMimeDate(String date) { 495128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki if (TextUtils.isEmpty(date)) { 496128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki return date; 497128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki } 498128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1"); 499128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki return date; 500128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki } 5017e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 5027e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static ByteArrayInputStream streamFromAsciiString(String ascii) { 5037e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return new ByteArrayInputStream(toAscii(ascii)); 5047e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 5057e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki 5067e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki /** 507f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki * A thread safe way to show a Toast. Can be called from any thread. 5087e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * 509f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki * @param context context 5107e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * @param resId Resource ID of the message string. 5117e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki */ 512f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki public static void showToast(Context context, int resId) { 513f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki showToast(context, context.getResources().getString(resId)); 51491d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki } 51591d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki 51691d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki /** 517f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki * A thread safe way to show a Toast. Can be called from any thread. 51891d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * 519f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki * @param context context 52091d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * @param message Message to show. 52191d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki */ 522f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki public static void showToast(final Context context, final String message) { 523f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki getMainThreadHandler().post(new Runnable() { 5245701e0a555a5c263862156c1291aa13b06850425Todd Kennedy @Override 5257e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki public void run() { 526f11295f3352f7c89fc92c73731e41b057e236969Makoto Onuki Toast.makeText(context, message, Toast.LENGTH_LONG).show(); 5277e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki } 5287e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki }); 5297e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki } 5303f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki 5313f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki /** 532b53b1501055cbf5040bfd7b88a9cda084574c398Marc Blank * Run {@code r} on a worker thread, returning the AsyncTask 533b53b1501055cbf5040bfd7b88a9cda084574c398Marc Blank * @return the AsyncTask; this is primarily for use by unit tests, which require the 534b53b1501055cbf5040bfd7b88a9cda084574c398Marc Blank * result of the task 535d72f7bdf114a21db6aac66a7e83d6b002c8e8ed5Makoto Onuki * 536d72f7bdf114a21db6aac66a7e83d6b002c8e8ed5Makoto Onuki * @deprecated use {@link EmailAsyncTask#runAsyncParallel} or 537d72f7bdf114a21db6aac66a7e83d6b002c8e8ed5Makoto Onuki * {@link EmailAsyncTask#runAsyncSerial} 5383f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki */ 539d72f7bdf114a21db6aac66a7e83d6b002c8e8ed5Makoto Onuki @Deprecated 540b53b1501055cbf5040bfd7b88a9cda084574c398Marc Blank public static AsyncTask<Void, Void, Void> runAsync(final Runnable r) { 541b53b1501055cbf5040bfd7b88a9cda084574c398Marc Blank return new AsyncTask<Void, Void, Void>() { 5423f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki @Override protected Void doInBackground(Void... params) { 5433f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki r.run(); 5443f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki return null; 5453f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki } 546d72f7bdf114a21db6aac66a7e83d6b002c8e8ed5Makoto Onuki }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 5473f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki } 54844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 54944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /** 55044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * Interface used in {@link #createUniqueFile} instead of {@link File#createNewFile()} to make 55144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * it testable. 55244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki */ 55344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /* package */ interface NewFileCreator { 55444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public static final NewFileCreator DEFAULT = new NewFileCreator() { 55544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki @Override public boolean createNewFile(File f) throws IOException { 55644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return f.createNewFile(); 55744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 55844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki }; 55944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public boolean createNewFile(File f) throws IOException ; 56044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 56144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 56244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /** 56344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * Creates a new empty file with a unique name in the given directory by appending a hyphen and 56444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * a number to the given filename. 56544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * 56644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * @return a new File object, or null if one could not be created 56744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki */ 56844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public static File createUniqueFile(File directory, String filename) throws IOException { 56944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return createUniqueFileInternal(NewFileCreator.DEFAULT, directory, filename); 57044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 57144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 57244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /* package */ static File createUniqueFileInternal(NewFileCreator nfc, 57344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki File directory, String filename) throws IOException { 57444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki File file = new File(directory, filename); 57544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (nfc.createNewFile(file)) { 57644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return file; 57744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 57844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki // Get the extension of the file, if any. 57944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki int index = filename.lastIndexOf('.'); 58044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki String format; 58144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (index != -1) { 58244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki String name = filename.substring(0, index); 58344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki String extension = filename.substring(index); 58444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki format = name + "-%d" + extension; 58544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } else { 58644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki format = filename + "-%d"; 58744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 58844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 58944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki for (int i = 2; i < Integer.MAX_VALUE; i++) { 59044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki file = new File(directory, String.format(format, i)); 59144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (nfc.createNewFile(file)) { 59244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return file; 59344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 59444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 59544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return null; 59644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 597bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki 59807597e547bc02cd2247caa866d25b94745dcd448Marc Blank public interface CursorGetter<T> { 59907597e547bc02cd2247caa866d25b94745dcd448Marc Blank T get(Cursor cursor, int column); 60007597e547bc02cd2247caa866d25b94745dcd448Marc Blank } 60107597e547bc02cd2247caa866d25b94745dcd448Marc Blank 60207597e547bc02cd2247caa866d25b94745dcd448Marc Blank private static final CursorGetter<Long> LONG_GETTER = new CursorGetter<Long>() { 6035701e0a555a5c263862156c1291aa13b06850425Todd Kennedy @Override 60407597e547bc02cd2247caa866d25b94745dcd448Marc Blank public Long get(Cursor cursor, int column) { 60507597e547bc02cd2247caa866d25b94745dcd448Marc Blank return cursor.getLong(column); 60607597e547bc02cd2247caa866d25b94745dcd448Marc Blank } 60707597e547bc02cd2247caa866d25b94745dcd448Marc Blank }; 60807597e547bc02cd2247caa866d25b94745dcd448Marc Blank 60907597e547bc02cd2247caa866d25b94745dcd448Marc Blank private static final CursorGetter<Integer> INT_GETTER = new CursorGetter<Integer>() { 6105701e0a555a5c263862156c1291aa13b06850425Todd Kennedy @Override 61107597e547bc02cd2247caa866d25b94745dcd448Marc Blank public Integer get(Cursor cursor, int column) { 61207597e547bc02cd2247caa866d25b94745dcd448Marc Blank return cursor.getInt(column); 61307597e547bc02cd2247caa866d25b94745dcd448Marc Blank } 61407597e547bc02cd2247caa866d25b94745dcd448Marc Blank }; 61507597e547bc02cd2247caa866d25b94745dcd448Marc Blank 616ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki private static final CursorGetter<String> STRING_GETTER = new CursorGetter<String>() { 6175701e0a555a5c263862156c1291aa13b06850425Todd Kennedy @Override 618ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki public String get(Cursor cursor, int column) { 619ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki return cursor.getString(column); 620ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki } 621ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki }; 622ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki 62307597e547bc02cd2247caa866d25b94745dcd448Marc Blank private static final CursorGetter<byte[]> BLOB_GETTER = new CursorGetter<byte[]>() { 6245701e0a555a5c263862156c1291aa13b06850425Todd Kennedy @Override 62507597e547bc02cd2247caa866d25b94745dcd448Marc Blank public byte[] get(Cursor cursor, int column) { 62607597e547bc02cd2247caa866d25b94745dcd448Marc Blank return cursor.getBlob(column); 62707597e547bc02cd2247caa866d25b94745dcd448Marc Blank } 62807597e547bc02cd2247caa866d25b94745dcd448Marc Blank }; 62907597e547bc02cd2247caa866d25b94745dcd448Marc Blank 630bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki /** 6317093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki * @return if {@code original} is to the EmailProvider, add "?limit=1". Otherwise just returns 6327093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki * {@code original}. 6337093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki * 6347093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki * Other providers don't support the limit param. Also, changing URI passed from other apps 6357093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki * can cause permission errors. 6367093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki */ 6377093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki /* package */ static Uri buildLimitOneUri(Uri original) { 6387093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki if ("content".equals(original.getScheme()) && 6397093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki EmailContent.AUTHORITY.equals(original.getAuthority())) { 6407093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki return EmailContent.uriWithLimit(original, 1); 6417093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki } 6427093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki return original; 6437093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki } 6447093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki 6457093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki /** 64607597e547bc02cd2247caa866d25b94745dcd448Marc Blank * @return a generic in column {@code column} of the first result row, if the query returns at 647bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki * least 1 row. Otherwise returns {@code defaultValue}. 648bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki */ 64907597e547bc02cd2247caa866d25b94745dcd448Marc Blank public static <T extends Object> T getFirstRowColumn(Context context, Uri uri, 65007597e547bc02cd2247caa866d25b94745dcd448Marc Blank String[] projection, String selection, String[] selectionArgs, String sortOrder, 65107597e547bc02cd2247caa866d25b94745dcd448Marc Blank int column, T defaultValue, CursorGetter<T> getter) { 65207597e547bc02cd2247caa866d25b94745dcd448Marc Blank // Use PARAMETER_LIMIT to restrict the query to the single row we need 6537093746dd5017b1dacb726eb7b083922de15612eMakoto Onuki uri = buildLimitOneUri(uri); 654bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki Cursor c = context.getContentResolver().query(uri, projection, selection, selectionArgs, 655bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki sortOrder); 656b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki if (c != null) { 657b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki try { 658b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki if (c.moveToFirst()) { 65907597e547bc02cd2247caa866d25b94745dcd448Marc Blank return getter.get(c, column); 660b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki } 661b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki } finally { 662b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki c.close(); 663bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 664bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 665bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki return defaultValue; 666bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 667bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki 668bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki /** 66907597e547bc02cd2247caa866d25b94745dcd448Marc Blank * {@link #getFirstRowColumn} for a Long with null as a default value. 670bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki */ 671bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki public static Long getFirstRowLong(Context context, Uri uri, String[] projection, 672bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column) { 67307597e547bc02cd2247caa866d25b94745dcd448Marc Blank return getFirstRowColumn(context, uri, projection, selection, selectionArgs, 67407597e547bc02cd2247caa866d25b94745dcd448Marc Blank sortOrder, column, null, LONG_GETTER); 675bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 676833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 677833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki /** 67807597e547bc02cd2247caa866d25b94745dcd448Marc Blank * {@link #getFirstRowColumn} for a Long with a provided default value. 67936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki */ 68007597e547bc02cd2247caa866d25b94745dcd448Marc Blank public static Long getFirstRowLong(Context context, Uri uri, String[] projection, 68136bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column, 68207597e547bc02cd2247caa866d25b94745dcd448Marc Blank Long defaultValue) { 68307597e547bc02cd2247caa866d25b94745dcd448Marc Blank return getFirstRowColumn(context, uri, projection, selection, selectionArgs, 68407597e547bc02cd2247caa866d25b94745dcd448Marc Blank sortOrder, column, defaultValue, LONG_GETTER); 68536bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki } 68636bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki 68736bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki /** 68807597e547bc02cd2247caa866d25b94745dcd448Marc Blank * {@link #getFirstRowColumn} for an Integer with null as a default value. 68936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki */ 69036bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki public static Integer getFirstRowInt(Context context, Uri uri, String[] projection, 69136bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column) { 69207597e547bc02cd2247caa866d25b94745dcd448Marc Blank return getFirstRowColumn(context, uri, projection, selection, selectionArgs, 69307597e547bc02cd2247caa866d25b94745dcd448Marc Blank sortOrder, column, null, INT_GETTER); 69407597e547bc02cd2247caa866d25b94745dcd448Marc Blank } 69507597e547bc02cd2247caa866d25b94745dcd448Marc Blank 69607597e547bc02cd2247caa866d25b94745dcd448Marc Blank /** 69707597e547bc02cd2247caa866d25b94745dcd448Marc Blank * {@link #getFirstRowColumn} for an Integer with a provided default value. 69807597e547bc02cd2247caa866d25b94745dcd448Marc Blank */ 69907597e547bc02cd2247caa866d25b94745dcd448Marc Blank public static Integer getFirstRowInt(Context context, Uri uri, String[] projection, 70007597e547bc02cd2247caa866d25b94745dcd448Marc Blank String selection, String[] selectionArgs, String sortOrder, int column, 70107597e547bc02cd2247caa866d25b94745dcd448Marc Blank Integer defaultValue) { 70207597e547bc02cd2247caa866d25b94745dcd448Marc Blank return getFirstRowColumn(context, uri, projection, selection, selectionArgs, 70307597e547bc02cd2247caa866d25b94745dcd448Marc Blank sortOrder, column, defaultValue, INT_GETTER); 70436bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki } 70536bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki 70607597e547bc02cd2247caa866d25b94745dcd448Marc Blank /** 707ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki * {@link #getFirstRowColumn} for a String with null as a default value. 708ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki */ 709ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki public static String getFirstRowString(Context context, Uri uri, String[] projection, 710ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki String selection, String[] selectionArgs, String sortOrder, int column) { 7110e00160074abffe03285b355dce8b6ae2a6e3138Makoto Onuki return getFirstRowString(context, uri, projection, selection, selectionArgs, sortOrder, 7120e00160074abffe03285b355dce8b6ae2a6e3138Makoto Onuki column, null); 713ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki } 714ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki 715ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki /** 716ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki * {@link #getFirstRowColumn} for a String with a provided default value. 717ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki */ 718ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki public static String getFirstRowString(Context context, Uri uri, String[] projection, 719ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki String selection, String[] selectionArgs, String sortOrder, int column, 720ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki String defaultValue) { 721ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki return getFirstRowColumn(context, uri, projection, selection, selectionArgs, 722ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki sortOrder, column, defaultValue, STRING_GETTER); 723ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki } 724ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki 725ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki /** 726ee7205d100eff01a75c292b80f41cd24a2b19b84Makoto Onuki * {@link #getFirstRowColumn} for a byte array with a provided default value. 72707597e547bc02cd2247caa866d25b94745dcd448Marc Blank */ 728b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki public static byte[] getFirstRowBlob(Context context, Uri uri, String[] projection, 729b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column, 730b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki byte[] defaultValue) { 73107597e547bc02cd2247caa866d25b94745dcd448Marc Blank return getFirstRowColumn(context, uri, projection, selection, selectionArgs, sortOrder, 73207597e547bc02cd2247caa866d25b94745dcd448Marc Blank column, defaultValue, BLOB_GETTER); 733b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki } 734b1ea9c3c12d8d9da5c1e49a8752076ce60861e9fMakoto Onuki 7354dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank public static boolean attachmentExists(Context context, Attachment attachment) { 7364dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank if (attachment == null) { 7374dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank return false; 7384dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank } else if (attachment.mContentBytes != null) { 7394dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank return true; 7406e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } else { 7415a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook final String cachedFile = attachment.getCachedFileUri(); 7429a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook // Try the cached file first 7439a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook if (!TextUtils.isEmpty(cachedFile)) { 7445a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook final Uri cachedFileUri = Uri.parse(cachedFile); 7459a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook try { 7465a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook final InputStream inStream = 7475a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook context.getContentResolver().openInputStream(cachedFileUri); 7489a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook try { 7499a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook inStream.close(); 7509a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook } catch (IOException e) { 7519a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook // Nothing to be done if can't close the stream 7529a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook } 7539a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook return true; 7549a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook } catch (FileNotFoundException e) { 7559a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook // We weren't able to open the file, try the content uri below 7565a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook LogUtils.e(Logging.LOG_TAG, e, "not able to open cached file"); 7579a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook } 7589a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook } 7599a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook final String contentUri = attachment.getContentUri(); 7606e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank if (TextUtils.isEmpty(contentUri)) { 7616e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank return false; 7626e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } 763d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki try { 7649a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook final Uri fileUri = Uri.parse(contentUri); 765567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki try { 7669a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook final InputStream inStream = 7679a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook context.getContentResolver().openInputStream(fileUri); 7686e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank try { 7696e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank inStream.close(); 7706e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } catch (IOException e) { 7716e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank // Nothing to be done if can't close the stream 7726e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } 7736e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank return true; 7746e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } catch (FileNotFoundException e) { 7756e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank return false; 776567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki } 7776e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } catch (RuntimeException re) { 778560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.w(Logging.LOG_TAG, "attachmentExists RuntimeException=" + re); 779567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki return false; 780d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki } 781d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki } 78209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 78309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 78409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank /** 78509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Check whether the message with a given id has unloaded attachments. If the message is 78609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * a forwarded message, we look instead at the messages's source for the attachments. If the 78709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * message or forward source can't be found, we return false 78809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * @param context the caller's context 78909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * @param messageId the id of the message 79009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * @return whether or not the message has unloaded attachments 79109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank */ 79209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static boolean hasUnloadedAttachments(Context context, long messageId) { 79309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Message msg = Message.restoreMessageWithId(context, messageId); 79409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank if (msg == null) return false; 79509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment[] atts = Attachment.restoreAttachmentsWithMessageId(context, messageId); 79609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank for (Attachment att: atts) { 7974dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank if (!attachmentExists(context, att)) { 79809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // If the attachment doesn't exist and isn't marked for download, we're in trouble 79909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // since the outbound message will be stuck indefinitely in the Outbox. Instead, 80009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // we'll just delete the attachment and continue; this is far better than the 80109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // alternative. In theory, this situation shouldn't be possible. 80209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank if ((att.mFlags & (Attachment.FLAG_DOWNLOAD_FORWARD | 80309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment.FLAG_DOWNLOAD_USER_REQUEST)) == 0) { 804560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "Unloaded attachment isn't marked for download: " + 80509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank att.mFileName + ", #" + att.mId); 80647925465841af09b60fff7027132ceacec2933b7Marc Blank Account acct = Account.restoreAccountWithId(context, msg.mAccountKey); 80747925465841af09b60fff7027132ceacec2933b7Marc Blank if (acct == null) return true; 80847925465841af09b60fff7027132ceacec2933b7Marc Blank // If smart forward is set and the message is a forward, we'll act as though 80947925465841af09b60fff7027132ceacec2933b7Marc Blank // the attachment has been loaded 81047925465841af09b60fff7027132ceacec2933b7Marc Blank // In Email1 this test wasn't necessary, as the UI handled it... 81147925465841af09b60fff7027132ceacec2933b7Marc Blank if ((msg.mFlags & Message.FLAG_TYPE_FORWARD) != 0) { 81247925465841af09b60fff7027132ceacec2933b7Marc Blank if ((acct.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) != 0) { 81347925465841af09b60fff7027132ceacec2933b7Marc Blank continue; 81447925465841af09b60fff7027132ceacec2933b7Marc Blank } 81547925465841af09b60fff7027132ceacec2933b7Marc Blank } 81609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment.delete(context, Attachment.CONTENT_URI, att.mId); 8176e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank } else if (att.getContentUri() != null) { 81809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // In this case, the attachment file is gone from the cache; let's clear the 81909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // contentUri; this should be a very unusual case 82009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank ContentValues cv = new ContentValues(); 82109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank cv.putNull(AttachmentColumns.CONTENT_URI); 82209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment.update(context, Attachment.CONTENT_URI, att.mId, cv); 82309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 82409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return true; 82509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 82609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 82709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return false; 82809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 82909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 83009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank /** 83109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Convenience method wrapping calls to retrieve columns from a single row, via EmailProvider. 83209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * The arguments are exactly the same as to contentResolver.query(). Results are returned in 83309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * an array of Strings corresponding to the columns in the projection. If the cursor has no 83409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * rows, null is returned. 83509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank */ 83609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static String[] getRowColumns(Context context, Uri contentUri, String[] projection, 83709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank String selection, String[] selectionArgs) { 83809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank String[] values = new String[projection.length]; 83909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank ContentResolver cr = context.getContentResolver(); 84009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Cursor c = cr.query(contentUri, projection, selection, selectionArgs, null); 84109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank try { 84209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank if (c.moveToFirst()) { 84309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank for (int i = 0; i < projection.length; i++) { 84409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank values[i] = c.getString(i); 84509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 84609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } else { 84709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return null; 84809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 84909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } finally { 85009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank c.close(); 85109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 85209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return values; 85309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 85409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 85509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank /** 85609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Convenience method for retrieving columns from a particular row in EmailProvider. 85709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Passed in here are a base uri (e.g. Message.CONTENT_URI), the unique id of a row, and 85809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * a projection. This method calls the previous one with the appropriate URI. 85909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank */ 86009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static String[] getRowColumns(Context context, Uri baseUri, long id, 86109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank String ... projection) { 86209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return getRowColumns(context, ContentUris.withAppendedId(baseUri, id), projection, null, 86309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank null); 86409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 86509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 86609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static boolean isExternalStorageMounted() { 86709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); 86809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 869f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 870f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki /** 871f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki * Class that supports running any operation for each account. 872f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki */ 873f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki public abstract static class ForEachAccount extends AsyncTask<Void, Void, Long[]> { 874f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki private final Context mContext; 875f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 876f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki public ForEachAccount(Context context) { 877f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki mContext = context; 878f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 879f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 880f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki @Override 881f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected final Long[] doInBackground(Void... params) { 882f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki ArrayList<Long> ids = new ArrayList<Long>(); 883f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blank Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI, 884f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blank Account.ID_PROJECTION, null, null, null); 885f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki try { 886f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki while (c.moveToNext()) { 887f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blank ids.add(c.getLong(Account.ID_PROJECTION_COLUMN)); 888f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 889f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } finally { 890f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki c.close(); 891f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 892f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki return ids.toArray(EMPTY_LONGS); 893f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 894f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 895f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki @Override 896f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected final void onPostExecute(Long[] ids) { 897f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki if (ids != null && !isCancelled()) { 898f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki for (long id : ids) { 899f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki performAction(id); 900f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 901f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 902f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki onFinished(); 903f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 904f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 905f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki /** 906f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki * This method will be called for each account. 907f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki */ 908f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected abstract void performAction(long accountId); 909f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 910f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki /** 911f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki * Called when the iteration is finished. 912f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki */ 913f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected void onFinished() { 914f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 915f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 916767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki 917767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki public static long[] toPrimitiveLongArray(Collection<Long> collection) { 9184e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki // Need to do this manually because we're converting to a primitive long array, not 9194e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki // a Long array. 920767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki final int size = collection.size(); 921767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki final long[] ret = new long[size]; 922767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki // Collection doesn't have get(i). (Iterable doesn't have size()) 923767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki int i = 0; 924767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki for (Long value : collection) { 925767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki ret[i++] = value; 926767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki } 927767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki return ret; 928767f9fe2ebcca7eee20f2a048f33a96ad4bf53daMakoto Onuki } 929082443978595bee3b7907563dc7665f908872e20Makoto Onuki 9304e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki public static Set<Long> toLongSet(long[] array) { 9314e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki // Need to do this manually because we're converting from a primitive long array, not 9324e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki // a Long array. 9334e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki final int size = array.length; 9344e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki HashSet<Long> ret = new HashSet<Long>(size); 9354e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki for (int i = 0; i < size; i++) { 9364e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki ret.add(array[i]); 9374e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki } 9384e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki return ret; 9394e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki } 9404e033e0ac79d85bc8df8e52bdfc8b1fc9ad29f3eMakoto Onuki 941082443978595bee3b7907563dc7665f908872e20Makoto Onuki /** 942082443978595bee3b7907563dc7665f908872e20Makoto Onuki * Workaround for the {@link ListView#smoothScrollToPosition} randomly scroll the view bug 943082443978595bee3b7907563dc7665f908872e20Makoto Onuki * if it's called right after {@link ListView#setAdapter}. 944082443978595bee3b7907563dc7665f908872e20Makoto Onuki */ 945082443978595bee3b7907563dc7665f908872e20Makoto Onuki public static void listViewSmoothScrollToPosition(final Activity activity, 946082443978595bee3b7907563dc7665f908872e20Makoto Onuki final ListView listView, final int position) { 947082443978595bee3b7907563dc7665f908872e20Makoto Onuki // Workarond: delay-call smoothScrollToPosition() 948082443978595bee3b7907563dc7665f908872e20Makoto Onuki new Handler().post(new Runnable() { 949082443978595bee3b7907563dc7665f908872e20Makoto Onuki @Override 950082443978595bee3b7907563dc7665f908872e20Makoto Onuki public void run() { 951082443978595bee3b7907563dc7665f908872e20Makoto Onuki if (activity.isFinishing()) { 952082443978595bee3b7907563dc7665f908872e20Makoto Onuki return; // Activity being destroyed 953082443978595bee3b7907563dc7665f908872e20Makoto Onuki } 954082443978595bee3b7907563dc7665f908872e20Makoto Onuki listView.smoothScrollToPosition(position); 955082443978595bee3b7907563dc7665f908872e20Makoto Onuki } 956082443978595bee3b7907563dc7665f908872e20Makoto Onuki }); 957082443978595bee3b7907563dc7665f908872e20Makoto Onuki } 958d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki 959d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki private static final String[] ATTACHMENT_META_NAME_PROJECTION = { 960d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki OpenableColumns.DISPLAY_NAME 961d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki }; 962d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki private static final int ATTACHMENT_META_NAME_COLUMN_DISPLAY_NAME = 0; 963d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki 964d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki /** 965d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki * @return Filename of a content of {@code contentUri}. If the provider doesn't provide the 966d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki * filename, returns the last path segment of the URI. 967d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki */ 968d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki public static String getContentFileName(Context context, Uri contentUri) { 969d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki String name = getFirstRowString(context, contentUri, ATTACHMENT_META_NAME_PROJECTION, null, 970d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki null, null, ATTACHMENT_META_NAME_COLUMN_DISPLAY_NAME); 971d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki if (name == null) { 972d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki name = contentUri.getLastPathSegment(); 973d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki } 974d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki return name; 975d1ee5b8fa5fe92df1ded5953a9e3f001b38a1ac7Makoto Onuki } 976adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 977adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki /** 978a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki * Append a bold span to a {@link SpannableStringBuilder}. 979a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki */ 980a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki public static SpannableStringBuilder appendBold(SpannableStringBuilder ssb, String text) { 981a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki if (!TextUtils.isEmpty(text)) { 982a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki SpannableString ss = new SpannableString(text); 983a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki ss.setSpan(new StyleSpan(Typeface.BOLD), 0, ss.length(), 984a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 985a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki ssb.append(ss); 986a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki } 987a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki 988a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki return ssb; 989a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki } 990a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki 991a826d3fb03f29a07ea12e44237b2c02ea1926c74Makoto Onuki /** 992adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki * Stringify a cursor for logging purpose. 993adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki */ 994adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki public static String dumpCursor(Cursor c) { 995adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki StringBuilder sb = new StringBuilder(); 996adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append("["); 997adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki while (c != null) { 998adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append(c.getClass()); // Class name may not be available if toString() is overridden 999adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append("/"); 1000adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append(c.toString()); 1001adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki if (c.isClosed()) { 1002adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append(" (closed)"); 1003adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1004adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki if (c instanceof CursorWrapper) { 1005adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki c = ((CursorWrapper) c).getWrappedCursor(); 1006adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append(", "); 1007adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } else { 1008adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki break; 1009adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1010adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1011adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki sb.append("]"); 1012adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki return sb.toString(); 1013adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1014adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1015adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki /** 1016adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki * Cursor wrapper that remembers where it was closed. 1017adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki * 1018adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki * Use {@link #get} to create a wrapped cursor. 1019adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki * USe {@link #getTraceIfAvailable} to get the stack trace. 1020adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki * Use {@link #log} to log if/where it was closed. 1021adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki */ 1022adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki public static class CloseTraceCursorWrapper extends CursorWrapper { 102380a2e9109daa5d13cd748305b4c5f83578f33728Makoto Onuki private static final boolean TRACE_ENABLED = false; 1024adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1025adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki private Exception mTrace; 1026adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1027adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki private CloseTraceCursorWrapper(Cursor cursor) { 1028adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki super(cursor); 1029adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1030adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1031adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki @Override 1032adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki public void close() { 1033adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki mTrace = new Exception("STACK TRACE"); 1034adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki super.close(); 1035adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1036adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1037adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki public static Exception getTraceIfAvailable(Cursor c) { 1038adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki if (c instanceof CloseTraceCursorWrapper) { 1039adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki return ((CloseTraceCursorWrapper) c).mTrace; 1040adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } else { 1041adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki return null; 1042adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1043adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1044adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1045adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki public static void log(Cursor c) { 1046adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki if (c == null) { 1047adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki return; 1048adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1049adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki if (c.isClosed()) { 1050560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.w(Logging.LOG_TAG, "Cursor was closed here: Cursor=" + c, 105131d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank getTraceIfAvailable(c)); 1052adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } else { 1053560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.w(Logging.LOG_TAG, "Cursor not closed. Cursor=" + c); 1054adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1055adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1056adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1057adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki public static Cursor get(Cursor original) { 1058adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki return TRACE_ENABLED ? new CloseTraceCursorWrapper(original) : original; 1059adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1060adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki 1061adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki /* package */ static CloseTraceCursorWrapper alwaysCreateForTest(Cursor original) { 1062adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki return new CloseTraceCursorWrapper(original); 1063adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 1064adbb6f8bc4df787a8e589d505b3f2e7d9c47cea4Makoto Onuki } 10650c75f83f03b3ef8e54163cba2d67ba086cf8af58Makoto Onuki 10660c75f83f03b3ef8e54163cba2d67ba086cf8af58Makoto Onuki /** 1067fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy * Test that the given strings are equal in a null-pointer safe fashion. 1068fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy */ 1069fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy public static boolean areStringsEqual(String s1, String s2) { 1070fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy return (s1 != null && s1.equals(s2)) || (s1 == null && s2 == null); 1071fe68c0e7c2672e09076038b36ad24f095633d313Todd Kennedy } 107219b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki 107319b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki public static void enableStrictMode(boolean enabled) { 107419b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki StrictMode.setThreadPolicy(enabled 107519b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki ? new StrictMode.ThreadPolicy.Builder().detectAll().build() 107619b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki : StrictMode.ThreadPolicy.LAX); 107719b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki StrictMode.setVmPolicy(enabled 107819b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki ? new StrictMode.VmPolicy.Builder().detectAll().build() 107919b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki : StrictMode.VmPolicy.LAX); 108019b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki } 1081d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki 1082d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki public static String dumpFragment(Fragment f) { 1083d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki StringWriter sw = new StringWriter(); 1084d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki PrintWriter w = new PrintWriter(sw); 1085d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki f.dump("", new FileDescriptor(), w, new String[0]); 1086d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki return sw.toString(); 1087d8a2e33998a2959d28441e609e915f7e5ad07b7fMakoto Onuki } 10888de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki 10898de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki /** 10908de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki * Builds an "in" expression for SQLite. 10918de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki * 10928de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki * e.g. "ID" + 1,2,3 -> "ID in (1,2,3)". If {@code values} is empty or null, it returns an 10938de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki * empty string. 10948de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki */ 10958de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki public static String buildInSelection(String columnName, Collection<? extends Number> values) { 10968de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki if ((values == null) || (values.size() == 0)) { 10978de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki return ""; 10988de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki } 10998de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki StringBuilder sb = new StringBuilder(); 11008de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki sb.append(columnName); 11018de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki sb.append(" in ("); 11028de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki String sep = ""; 11038de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki for (Number n : values) { 11048de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki sb.append(sep); 11058de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki sb.append(n.toString()); 11068de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki sep = ","; 11078de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki } 11088de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki sb.append(')'); 11098de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki return sb.toString(); 11108de5bda81594182757c1aa94a65cfb8c7b360b34Makoto Onuki } 111196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 1112