Utility.java revision 4dcb1c5fdaacc40309b77af2a32532bc60218523
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 1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email; 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1933c972e0c62e474d2b2f5a293b92893cac0ea47aDoug Zongkerimport com.android.email.provider.EmailContent; 20da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadlerimport com.android.email.provider.EmailContent.Account; 21da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadlerimport com.android.email.provider.EmailContent.AccountColumns; 2209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport com.android.email.provider.EmailContent.Attachment; 2309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport com.android.email.provider.EmailContent.AttachmentColumns; 24da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadlerimport com.android.email.provider.EmailContent.HostAuth; 25da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadlerimport com.android.email.provider.EmailContent.HostAuthColumns; 26e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.Mailbox; 27e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.MailboxColumns; 28e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.Message; 29e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.MessageColumns; 30e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler 317e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onukiimport android.app.Activity; 32e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.content.ContentResolver; 3309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport android.content.ContentUris; 3409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport android.content.ContentValues; 35f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.Context; 36533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onukiimport android.content.pm.ActivityInfo; 3744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onukiimport android.content.res.Resources; 38f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.res.TypedArray; 39e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.database.Cursor; 40f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.graphics.drawable.Drawable; 41bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onukiimport android.net.Uri; 4259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onukiimport android.os.AsyncTask; 4309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blankimport android.os.Environment; 447fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onukiimport android.os.Parcel; 45833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onukiimport android.os.Parcelable; 46d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.security.MessageDigest; 47d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.telephony.TelephonyManager; 48128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport android.text.TextUtils; 496cec1104fe8863fce2ee86ff5145076e6c436a00Doug Zongkerimport android.util.Base64; 50d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.util.Log; 51833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onukiimport android.widget.AbsListView; 52f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.widget.TextView; 537e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onukiimport android.widget.Toast; 54e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler 557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.io.ByteArrayInputStream; 5644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onukiimport java.io.File; 57d755cdce13217d25ac33169b5e410709636255c4Makoto Onukiimport java.io.FileNotFoundException; 5896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException; 5996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream; 6096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStreamReader; 6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.UnsupportedEncodingException; 6220225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.ByteBuffer; 6320225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.CharBuffer; 6420225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.charset.Charset; 65d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport java.security.NoSuchAlgorithmException; 66f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onukiimport java.util.ArrayList; 6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.Date; 68989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.GregorianCalendar; 69989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.TimeZone; 70128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport java.util.regex.Pattern; 7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class Utility { 7320225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki public static final Charset UTF_8 = Charset.forName("UTF-8"); 747e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static final Charset ASCII = Charset.forName("US-ASCII"); 757e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 767e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static final String[] EMPTY_STRINGS = new String[0]; 77f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki public static final Long[] EMPTY_LONGS = new Long[0]; 7820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki 79128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki // "GMT" + "+" or "-" + 4 digits 80128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE = 81128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki Pattern.compile("GMT([-+]\\d{4})$"); 82128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki 8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public final static String readInputStream(InputStream in, String encoding) throws IOException { 8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project InputStreamReader reader = new InputStreamReader(in, encoding); 8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project StringBuffer sb = new StringBuffer(); 8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int count; 8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project char[] buf = new char[512]; 8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project while ((count = reader.read(buf)) != -1) { 8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append(buf, 0, count); 9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return sb.toString(); 9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public final static boolean arrayContains(Object[] a, Object o) { 9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0, count = a.length; i < count; i++) { 9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (a[i].equals(o)) { 9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return true; 9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return false; 10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Combines the given array of Objects into a single string using the 10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * seperator character and each Object's toString() method. between each 10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * part. 10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param parts 10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param seperator 11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return 11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String combine(Object[] parts, char seperator) { 11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (parts == null) { 11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project StringBuffer sb = new StringBuffer(); 11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0; i < parts.length; i++) { 11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append(parts[i].toString()); 11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (i < parts.length - 1) { 12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append(seperator); 12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return sb.toString(); 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String base64Decode(String encoded) { 12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (encoded == null) { 12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 129f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker byte[] decoded = Base64.decode(encoded, Base64.DEFAULT); 13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return new String(decoded); 13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String base64Encode(String s) { 13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (s == null) { 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return s; 13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 137f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker return Base64.encodeToString(s.getBytes(), Base64.NO_WRAP); 13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 140e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank public static boolean isTextViewNotEmpty(TextView view) { 141e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank return !TextUtils.isEmpty(view.getText()); 14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 144e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank public static boolean isPortFieldValid(TextView view) { 145e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank CharSequence chars = view.getText(); 146e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank if (TextUtils.isEmpty(chars)) return false; 147e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank Integer port; 148e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank // In theory, we can't get an illegal value here, since the field is monitored for valid 149e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank // numeric input. But this might be used elsewhere without such a check. 150e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank try { 151e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank port = Integer.parseInt(chars.toString()); 152e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank } catch (NumberFormatException e) { 153e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank return false; 154e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank } 155e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank return port > 0 && port < 65536; 156e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank } 15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 159e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank * Ensures that the given string starts and ends with the double quote character. The string is 160e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank * not modified in any way except to add the double quote character to start and end if it's not 161e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank * already there. 162f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker * 16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * TODO: Rename this, because "quoteString()" can mean so many different things. 164f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker * 16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * sample -> "sample" 16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "sample" -> "sample" 16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * ""sample"" -> "sample" 16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "sample"" -> "sample" 16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * sa"mp"le -> "sa"mp"le" 17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "sa"mp"le" -> "sa"mp"le" 17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * (empty string) -> "" 17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * " -> "" 17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param s 17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return 17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String quoteString(String s) { 17796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (s == null) { 17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (!s.matches("^\".*\"$")) { 18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return "\"" + s + "\""; 18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else { 18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return s; 18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 187f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker 18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 189f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker * Apply quoting rules per IMAP RFC, 19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * quoted = DQUOTE *QUOTED-CHAR DQUOTE 19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials 19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * quoted-specials = DQUOTE / "\" 193f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker * 19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * This is used primarily for IMAP login, but might be useful elsewhere. 195f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker * 19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * NOTE: Not very efficient - you may wish to preflight this, or perhaps it should check 19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * for trouble chars before calling the replace functions. 198f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker * 19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param s The string to be quoted. 20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return A copy of the string, having undergone quoting as described above 20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String imapQuoted(String s) { 203f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker 20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // First, quote any backslashes by replacing \ with \\ 20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // regex Pattern: \\ (Java string const = \\\\) 20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // Substitute: \\\\ (Java string const = \\\\\\\\) 20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String result = s.replaceAll("\\\\", "\\\\\\\\"); 208f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker 20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // Then, quote any double-quotes by replacing " with \" 21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // regex Pattern: " (Java string const = \") 21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // Substitute: \\" (Java string const = \\\\\") 21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project result = result.replaceAll("\"", "\\\\\""); 213f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker 21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // return string with quotes around it 21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return "\"" + result + "\""; 21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 217f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker 21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * A fast version of URLDecoder.decode() that works only with UTF-8 and does only two 22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * allocations. This version is around 3x as fast as the standard one and I'm using it 22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * hundreds of times in places that slow down the UI, so it helps. 22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static String fastUrlDecode(String s) { 22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project byte[] bytes = s.getBytes("UTF-8"); 22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project byte ch; 22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int length = 0; 22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0, count = bytes.length; i < count; i++) { 22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project ch = bytes[i]; 23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (ch == '%') { 23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int h = (bytes[i + 1] - '0'); 23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int l = (bytes[i + 2] - '0'); 23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (h > 9) { 23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project h -= 7; 23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (l > 9) { 23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project l -= 7; 23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bytes[length] = (byte) ((h << 4) | l); 24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project i += 2; 24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else if (ch == '+') { 24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bytes[length] = ' '; 24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else { 24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project bytes[length] = bytes[i]; 24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project length++; 24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return new String(bytes, 0, length, "UTF-8"); 25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project catch (UnsupportedEncodingException uee) { 25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Returns true if the specified date is within today. Returns false otherwise. 25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param date 26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @return 26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static boolean isDateToday(Date date) { 26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // TODO But Calendar is so slowwwwwww.... 26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Date today = new Date(); 26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (date.getYear() == today.getYear() && 26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project date.getMonth() == today.getMonth() && 26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project date.getDate() == today.getDate()) { 26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return true; 26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return false; 27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * TODO disabled this method globally. It is used in all the settings screens but I just 27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * noticed that an unrelated icon was dimmed. Android must share drawables internally. 27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static void setCompoundDrawablesAlpha(TextView view, int alpha) { 27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// Drawable[] drawables = view.getCompoundDrawables(); 27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// for (Drawable drawable : drawables) { 28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// if (drawable != null) { 28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// drawable.setAlpha(alpha); 28296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// } 28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// } 28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 285e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler 286e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler // TODO: unit test this 287e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler public static String buildMailboxIdSelection(ContentResolver resolver, long mailboxId) { 288e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler // Setup default selection & args, then add to it as necessary 289e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler StringBuilder selection = new StringBuilder( 2906c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler MessageColumns.FLAG_LOADED + " IN (" 2916c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler + Message.FLAG_LOADED_PARTIAL + "," + Message.FLAG_LOADED_COMPLETE 2926c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler + ") AND "); 293e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler if (mailboxId == Mailbox.QUERY_ALL_INBOXES 294e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler || mailboxId == Mailbox.QUERY_ALL_DRAFTS 295e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler || mailboxId == Mailbox.QUERY_ALL_OUTBOX) { 296e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler // query for all mailboxes of type INBOX, DRAFTS, or OUTBOX 297e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler int type; 298e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler if (mailboxId == Mailbox.QUERY_ALL_INBOXES) { 299e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler type = Mailbox.TYPE_INBOX; 300e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) { 301e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler type = Mailbox.TYPE_DRAFTS; 302e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } else { 303e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler type = Mailbox.TYPE_OUTBOX; 304e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } 305e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler StringBuilder inboxes = new StringBuilder(); 306e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler Cursor c = resolver.query(Mailbox.CONTENT_URI, 307e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler EmailContent.ID_PROJECTION, 308e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler MailboxColumns.TYPE + "=? AND " + MailboxColumns.FLAG_VISIBLE + "=1", 309e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler new String[] { Integer.toString(type) }, null); 310e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler // build an IN (mailboxId, ...) list 311e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler // TODO do this directly in the provider 312e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler while (c.moveToNext()) { 313e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler if (inboxes.length() != 0) { 314e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler inboxes.append(","); 315e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } 316e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler inboxes.append(c.getLong(EmailContent.ID_PROJECTION_COLUMN)); 317e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } 318e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler c.close(); 319e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler selection.append(MessageColumns.MAILBOX_KEY + " IN "); 320e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler selection.append("(").append(inboxes).append(")"); 321e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } else if (mailboxId == Mailbox.QUERY_ALL_UNREAD) { 322e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler selection.append(Message.FLAG_READ + "=0"); 323e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) { 324e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler selection.append(Message.FLAG_FAVORITE + "=1"); 325e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } else { 326e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler selection.append(MessageColumns.MAILBOX_KEY + "=" + mailboxId); 327e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } 328e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler return selection.toString(); 329e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler } 330dadba9949895696108b31124fc0c6aa1a297ab1csatok 331833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // TODO When the UI is settled, cache all strings/drawables 332833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // TODO When the UI is settled, write up tests 333833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // TODO When the UI is settled, remove backward-compatibility methods 334dadba9949895696108b31124fc0c6aa1a297ab1csatok public static class FolderProperties { 335dadba9949895696108b31124fc0c6aa1a297ab1csatok 336dadba9949895696108b31124fc0c6aa1a297ab1csatok private static FolderProperties sInstance; 337dadba9949895696108b31124fc0c6aa1a297ab1csatok 338833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final Context mContext; 339833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 340dadba9949895696108b31124fc0c6aa1a297ab1csatok // Caches for frequently accessed resources. 341833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final String[] mSpecialMailbox; 342833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final TypedArray mSpecialMailboxDrawable; 343833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final Drawable mDefaultMailboxDrawable; 344833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final Drawable mSummaryStarredMailboxDrawable; 345833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final Drawable mSummaryCombinedInboxDrawable; 346dadba9949895696108b31124fc0c6aa1a297ab1csatok 347dadba9949895696108b31124fc0c6aa1a297ab1csatok private FolderProperties(Context context) { 348833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki mContext = context.getApplicationContext(); 349fa52e6c95674aef6461a5cfc670a052e1c5b7f2fAndrew Stadler mSpecialMailbox = context.getResources().getStringArray(R.array.mailbox_display_names); 350dadba9949895696108b31124fc0c6aa1a297ab1csatok for (int i = 0; i < mSpecialMailbox.length; ++i) { 351dadba9949895696108b31124fc0c6aa1a297ab1csatok if ("".equals(mSpecialMailbox[i])) { 352dadba9949895696108b31124fc0c6aa1a297ab1csatok // there is no localized name, so use the display name from the server 353dadba9949895696108b31124fc0c6aa1a297ab1csatok mSpecialMailbox[i] = null; 354dadba9949895696108b31124fc0c6aa1a297ab1csatok } 355dadba9949895696108b31124fc0c6aa1a297ab1csatok } 356dadba9949895696108b31124fc0c6aa1a297ab1csatok mSpecialMailboxDrawable = 357fa52e6c95674aef6461a5cfc670a052e1c5b7f2fAndrew Stadler context.getResources().obtainTypedArray(R.array.mailbox_display_icons); 358dadba9949895696108b31124fc0c6aa1a297ab1csatok mDefaultMailboxDrawable = 359dadba9949895696108b31124fc0c6aa1a297ab1csatok context.getResources().getDrawable(R.drawable.ic_list_folder); 3603786cab2aa1776e92fc67af3931a76cd8b848b48satok mSummaryStarredMailboxDrawable = 3613786cab2aa1776e92fc67af3931a76cd8b848b48satok context.getResources().getDrawable(R.drawable.ic_list_starred); 3623786cab2aa1776e92fc67af3931a76cd8b848b48satok mSummaryCombinedInboxDrawable = 3633786cab2aa1776e92fc67af3931a76cd8b848b48satok context.getResources().getDrawable(R.drawable.ic_list_combined_inbox); 364dadba9949895696108b31124fc0c6aa1a297ab1csatok } 365dadba9949895696108b31124fc0c6aa1a297ab1csatok 3661f2caa80957e92519258e212b5fd45fb6c168a73Makoto Onuki public static synchronized FolderProperties getInstance(Context context) { 367dadba9949895696108b31124fc0c6aa1a297ab1csatok if (sInstance == null) { 3681f2caa80957e92519258e212b5fd45fb6c168a73Makoto Onuki sInstance = new FolderProperties(context); 369dadba9949895696108b31124fc0c6aa1a297ab1csatok } 370dadba9949895696108b31124fc0c6aa1a297ab1csatok return sInstance; 371dadba9949895696108b31124fc0c6aa1a297ab1csatok } 372dadba9949895696108b31124fc0c6aa1a297ab1csatok 373833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // For backward compatibility. 374833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public String getDisplayName(int type) { 375833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki return getDisplayName(type, -1); 376833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 377833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 378833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // For backward compatibility. 379833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public Drawable getSummaryMailboxIconIds(long id) { 380833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki return getIcon(-1, id); 381833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 382833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 383833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public Drawable getIconIds(int type) { 384833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki return getIcon(type, -1); 385833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 386833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 387dadba9949895696108b31124fc0c6aa1a297ab1csatok /** 388dadba9949895696108b31124fc0c6aa1a297ab1csatok * Lookup names of localized special mailboxes 389dadba9949895696108b31124fc0c6aa1a297ab1csatok */ 390833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public String getDisplayName(int type, long mailboxId) { 391833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // Special combined mailboxes 392833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki int resId = 0; 393833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 394833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki // Can't use long for switch!? 395833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki if (mailboxId == Mailbox.QUERY_ALL_INBOXES) { 396833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki resId = R.string.account_folder_list_summary_inbox; 397833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) { 398833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki resId = R.string.account_folder_list_summary_starred; 399833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) { 400833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki resId = R.string.account_folder_list_summary_drafts; 401833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } else if (mailboxId == Mailbox.QUERY_ALL_OUTBOX) { 402833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki resId = R.string.account_folder_list_summary_outbox; 403833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 404833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki if (resId != 0) { 405833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki return mContext.getString(resId); 406833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 407833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 408dadba9949895696108b31124fc0c6aa1a297ab1csatok if (type < mSpecialMailbox.length) { 409dadba9949895696108b31124fc0c6aa1a297ab1csatok return mSpecialMailbox[type]; 410dadba9949895696108b31124fc0c6aa1a297ab1csatok } 411dadba9949895696108b31124fc0c6aa1a297ab1csatok return null; 412dadba9949895696108b31124fc0c6aa1a297ab1csatok } 413dadba9949895696108b31124fc0c6aa1a297ab1csatok 414dadba9949895696108b31124fc0c6aa1a297ab1csatok /** 415dadba9949895696108b31124fc0c6aa1a297ab1csatok * Lookup icons of special mailboxes 416dadba9949895696108b31124fc0c6aa1a297ab1csatok */ 417833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public Drawable getIcon(int type, long mailboxId) { 418833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki if (mailboxId == Mailbox.QUERY_ALL_INBOXES) { 4193786cab2aa1776e92fc67af3931a76cd8b848b48satok return mSummaryCombinedInboxDrawable; 420833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) { 4213786cab2aa1776e92fc67af3931a76cd8b848b48satok return mSummaryStarredMailboxDrawable; 422833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) { 4233786cab2aa1776e92fc67af3931a76cd8b848b48satok return mSpecialMailboxDrawable.getDrawable(Mailbox.TYPE_DRAFTS); 424833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } else if (mailboxId == Mailbox.QUERY_ALL_OUTBOX) { 4253786cab2aa1776e92fc67af3931a76cd8b848b48satok return mSpecialMailboxDrawable.getDrawable(Mailbox.TYPE_OUTBOX); 4263786cab2aa1776e92fc67af3931a76cd8b848b48satok } 427833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki if (0 <= type && type < mSpecialMailboxDrawable.length()) { 428833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki return mSpecialMailboxDrawable.getDrawable(type); 429833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 4303786cab2aa1776e92fc67af3931a76cd8b848b48satok return mDefaultMailboxDrawable; 4313786cab2aa1776e92fc67af3931a76cd8b848b48satok } 432dadba9949895696108b31124fc0c6aa1a297ab1csatok } 433da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler 434da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler private final static String HOSTAUTH_WHERE_CREDENTIALS = HostAuthColumns.ADDRESS + " like ?" 435da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler + " and " + HostAuthColumns.LOGIN + " like ?" 436da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler + " and " + HostAuthColumns.PROTOCOL + " not like \"smtp\""; 437da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler private final static String ACCOUNT_WHERE_HOSTAUTH = AccountColumns.HOST_AUTH_KEY_RECV + "=?"; 438da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler 439da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler /** 440da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * Look for an existing account with the same username & server 441da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * 442da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * @param context a system context 443da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler * @param allowAccountId this account Id will not trigger (when editing an existing account) 4449d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank * @param hostName the server's address 4459d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank * @param userLogin the user's login string 4469d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank * @result null = no matching account found. Account = matching account 447da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler */ 4489d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank public static Account findExistingAccount(Context context, long allowAccountId, 4499d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank String hostName, String userLogin) { 450da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler ContentResolver resolver = context.getContentResolver(); 451da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler Cursor c = resolver.query(HostAuth.CONTENT_URI, HostAuth.ID_PROJECTION, 452da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler HOSTAUTH_WHERE_CREDENTIALS, new String[] { hostName, userLogin }, null); 453da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler try { 454da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler while (c.moveToNext()) { 455da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler long hostAuthId = c.getLong(HostAuth.ID_PROJECTION_COLUMN); 4569d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank // Find account with matching hostauthrecv key, and return it 457da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler Cursor c2 = resolver.query(Account.CONTENT_URI, Account.ID_PROJECTION, 458da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler ACCOUNT_WHERE_HOSTAUTH, new String[] { Long.toString(hostAuthId) }, null); 459da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler try { 460da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler while (c2.moveToNext()) { 461da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler long accountId = c2.getLong(Account.ID_PROJECTION_COLUMN); 462da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler if (accountId != allowAccountId) { 463da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler Account account = Account.restoreAccountWithId(context, accountId); 464da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler if (account != null) { 4659d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank return account; 466da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 467da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 468da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 469da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } finally { 470da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler c2.close(); 471da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 472da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 473da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } finally { 474da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler c.close(); 475da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 476da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler 477da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler return null; 478da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler } 479eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler 480eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler /** 481eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler * Generate a random message-id header for locally-generated messages. 482eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler */ 483eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler public static String generateMessageId() { 484eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler StringBuffer sb = new StringBuffer(); 485eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append("<"); 486eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler for (int i = 0; i < 24; i++) { 487eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append(Integer.toString((int)(Math.random() * 35), 36)); 488eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler } 489eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append("."); 490eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append(Long.toString(System.currentTimeMillis())); 491eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler sb.append("@email.android.com>"); 492eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler return sb.toString(); 493eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler } 494eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler 495989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda /** 496989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * Generate a time in milliseconds from a date string that represents a date/time in GMT 497989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @param DateTime date string in format 20090211T180303Z (rfc2445, iCalendar). 498989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @return the time in milliseconds (since Jan 1, 1970) 499989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda */ 500989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda public static long parseDateTimeToMillis(String date) { 501989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda GregorianCalendar cal = parseDateTimeToCalendar(date); 502989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda return cal.getTimeInMillis(); 503989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda } 504989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda 505989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda /** 506989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * Generate a GregorianCalendar from a date string that represents a date/time in GMT 507989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @param DateTime date string in format 20090211T180303Z (rfc2445, iCalendar). 508989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @return the GregorianCalendar 509989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda */ 510989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda public static GregorianCalendar parseDateTimeToCalendar(String date) { 511989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)), 512989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)), 513989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(9, 11)), Integer.parseInt(date.substring(11, 13)), 514989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(13, 15))); 515989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda cal.setTimeZone(TimeZone.getTimeZone("GMT")); 516989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda return cal; 517989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda } 518989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda 519989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda /** 520989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * Generate a time in milliseconds from an email date string that represents a date/time in GMT 521989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @param Email style DateTime string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339) 522989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda * @return the time in milliseconds (since Jan 1, 1970) 523989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda */ 524989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda public static long parseEmailDateTimeToMillis(String date) { 525989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)), 526989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(5, 7)) - 1, Integer.parseInt(date.substring(8, 10)), 527989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(11, 13)), Integer.parseInt(date.substring(14, 16)), 528989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda Integer.parseInt(date.substring(17, 19))); 529989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda cal.setTimeZone(TimeZone.getTimeZone("GMT")); 530989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda return cal.getTimeInMillis(); 531989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda } 53220225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki 5337e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private static byte[] encode(Charset charset, String s) { 53420225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki if (s == null) { 53520225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki return null; 53620225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki } 5377e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki final ByteBuffer buffer = charset.encode(CharBuffer.wrap(s)); 53820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki final byte[] bytes = new byte[buffer.limit()]; 53920225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki buffer.get(bytes); 54020225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki return bytes; 54120225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki } 54288a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki 5437e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki private static String decode(Charset charset, byte[] b) { 544fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki if (b == null) { 545fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki return null; 546fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki } 5477e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki final CharBuffer cb = charset.decode(ByteBuffer.wrap(b)); 548fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki return new String(cb.array(), 0, cb.length()); 549fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki } 550fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki 5517e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Converts a String to UTF-8 */ 5527e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static byte[] toUtf8(String s) { 5537e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return encode(UTF_8, s); 5547e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 5557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 5567e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Builds a String from UTF-8 bytes */ 5577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static String fromUtf8(byte[] b) { 5587e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return decode(UTF_8, b); 5597e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 5607e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 5617e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Converts a String to ASCII bytes */ 5627e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static byte[] toAscii(String s) { 5637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return encode(ASCII, s); 5647e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 5657e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 5667e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki /** Builds a String from ASCII bytes */ 5677e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static String fromAscii(byte[] b) { 5687e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return decode(ASCII, b); 5697e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 5707e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 57188a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki /** 57288a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki * @return true if the input is the first (or only) byte in a UTF-8 character 57388a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki */ 57488a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki public static boolean isFirstUtf8Byte(byte b) { 57588a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki // If the top 2 bits is '10', it's not a first byte. 57688a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki return (b & 0xc0) != 0x80; 57788a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki } 578dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki 579dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki public static String byteToHex(int b) { 580dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki return byteToHex(new StringBuilder(), b).toString(); 581dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki } 582dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki 583dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki public static StringBuilder byteToHex(StringBuilder sb, int b) { 584dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki b &= 0xFF; 585dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki sb.append("0123456789ABCDEF".charAt(b >> 4)); 586dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki sb.append("0123456789ABCDEF".charAt(b & 0xF)); 587dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki return sb; 588dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki } 589eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank 590eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank public static String replaceBareLfWithCrlf(String str) { 591eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank return str.replace("\r", "").replace("\n", "\r\n"); 592eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank } 59359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki 59459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki /** 59559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * Cancel an {@link AsyncTask}. If it's already running, it'll be interrupted. 59659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki */ 59759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki public static void cancelTaskInterrupt(AsyncTask<?, ?, ?> task) { 59859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki cancelTask(task, true); 59959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki } 60059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki 60159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki /** 60259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * Cancel an {@link AsyncTask}. 60359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * 60459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this 60559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * task should be interrupted; otherwise, in-progress tasks are allowed 60659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki * to complete. 60759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki */ 60859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) { 60959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) { 61059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki task.cancel(mayInterruptIfRunning); 61159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki } 61259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki } 613d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki 614d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki /** 615d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki * @return Device's unique ID if available. null if the device has no unique ID. 616d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki */ 617d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki public static String getConsistentDeviceId(Context context) { 618d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final String deviceId; 619d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki try { 620d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki TelephonyManager tm = 621d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 622d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki if (tm == null) { 623d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return null; 624d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 625d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki deviceId = tm.getDeviceId(); 626d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki if (deviceId == null) { 627d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return null; 628d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 629d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } catch (Exception e) { 630d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki Log.d(Email.LOG_TAG, "Error in TelephonyManager.getDeviceId(): " + e.getMessage()); 631d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return null; 632d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 633d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final MessageDigest sha; 634d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki try { 635d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki sha = MessageDigest.getInstance("SHA-1"); 636d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } catch (NoSuchAlgorithmException impossible) { 637d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return null; 638d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 639d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki sha.update(Utility.toUtf8(deviceId)); 640d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final int hash = getSmallHashFromSha1(sha.digest()); 641d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return Integer.toString(hash); 642d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 643d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki 644d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki /** 645d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki * @return a non-negative integer generated from 20 byte SHA-1 hash. 646d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki */ 647d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki /* package for testing */ static int getSmallHashFromSha1(byte[] sha1) { 648d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki final int offset = sha1[19] & 0xf; // SHA1 is 20 bytes. 649d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki return ((sha1[offset] & 0x7f) << 24) 650d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki | ((sha1[offset + 1] & 0xff) << 16) 651d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki | ((sha1[offset + 2] & 0xff) << 8) 652d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki | ((sha1[offset + 3] & 0xff)); 653d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki } 654128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki 655128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki /** 656128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * Try to make a date MIME(RFC 2822/5322)-compliant. 657128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * 658128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * It fixes: 659128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700" 660128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * (4 digit zone value can't be preceded by "GMT") 661128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki * We got a report saying eBay sends a date in this format 662128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki */ 663128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki public static String cleanUpMimeDate(String date) { 664128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki if (TextUtils.isEmpty(date)) { 665128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki return date; 666128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki } 667128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1"); 668128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki return date; 669128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki } 6707e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki 6717e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki public static ByteArrayInputStream streamFromAsciiString(String ascii) { 6727e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki return new ByteArrayInputStream(toAscii(ascii)); 6737e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki } 6747e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki 6757e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki /** 6767e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * A thread safe way to show a Toast. This method uses {@link Activity#runOnUiThread}, so it 6777e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * can be called on any thread. 6787e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * 6797e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * @param activity Parent activity. 6807e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki * @param resId Resource ID of the message string. 6817e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki */ 68291d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki public static void showToast(Activity activity, int resId) { 68391d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki showToast(activity, activity.getResources().getString(resId)); 68491d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki } 68591d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki 68691d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki /** 68791d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * A thread safe way to show a Toast. This method uses {@link Activity#runOnUiThread}, so it 68891d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * can be called on any thread. 68991d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * 69091d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * @param activity Parent activity. 69191d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki * @param message Message to show. 69291d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki */ 69391d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki public static void showToast(final Activity activity, final String message) { 6947e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki activity.runOnUiThread(new Runnable() { 6957e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki public void run() { 69691d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); 6977e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki } 6987e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki }); 6997e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki } 7003f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki 7013f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki /** 7023f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki * Run {@code r} on a worker thread. 7033f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki */ 7043f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki public static void runAsync(final Runnable r) { 7053f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki new AsyncTask<Void, Void, Void>() { 7063f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki @Override protected Void doInBackground(Void... params) { 7073f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki r.run(); 7083f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki return null; 7093f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki } 7103f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki }.execute(); 7113f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki } 71244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 71344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /** 71444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * Formats the given size as a String in bytes, kB, MB or GB. Ex: 12,315,000 = 11 MB 71544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki */ 71644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public static String formatSize(Context context, long size) { 71744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki final Resources res = context.getResources(); 71844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki final long KB = 1024; 71944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki final long MB = (KB * 1024); 72044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki final long GB = (MB * 1024); 72144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 72244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki int resId; 72344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki int value; 72444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 72544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (size < KB) { 72644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki resId = R.plurals.message_view_attachment_bytes; 72744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki value = (int) size; 72844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } else if (size < MB) { 72944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki resId = R.plurals.message_view_attachment_kilobytes; 73044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki value = (int) (size / KB); 73144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } else if (size < GB) { 73244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki resId = R.plurals.message_view_attachment_megabytes; 73344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki value = (int) (size / MB); 73444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } else { 73544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki resId = R.plurals.message_view_attachment_gigabytes; 73644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki value = (int) (size / GB); 73744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 73844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return res.getQuantityString(resId, value, value); 73944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 74044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 74144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /** 74244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * Interface used in {@link #createUniqueFile} instead of {@link File#createNewFile()} to make 74344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * it testable. 74444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki */ 74544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /* package */ interface NewFileCreator { 74644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public static final NewFileCreator DEFAULT = new NewFileCreator() { 74744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki @Override public boolean createNewFile(File f) throws IOException { 74844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return f.createNewFile(); 74944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 75044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki }; 75144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public boolean createNewFile(File f) throws IOException ; 75244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 75344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 75444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /** 75544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * Creates a new empty file with a unique name in the given directory by appending a hyphen and 75644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * a number to the given filename. 75744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * 75844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki * @return a new File object, or null if one could not be created 75944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki */ 76044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki public static File createUniqueFile(File directory, String filename) throws IOException { 76144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return createUniqueFileInternal(NewFileCreator.DEFAULT, directory, filename); 76244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 76344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 76444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki /* package */ static File createUniqueFileInternal(NewFileCreator nfc, 76544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki File directory, String filename) throws IOException { 76644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki File file = new File(directory, filename); 76744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (nfc.createNewFile(file)) { 76844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return file; 76944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 77044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki // Get the extension of the file, if any. 77144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki int index = filename.lastIndexOf('.'); 77244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki String format; 77344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (index != -1) { 77444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki String name = filename.substring(0, index); 77544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki String extension = filename.substring(index); 77644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki format = name + "-%d" + extension; 77744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } else { 77844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki format = filename + "-%d"; 77944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 78044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki 78144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki for (int i = 2; i < Integer.MAX_VALUE; i++) { 78244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki file = new File(directory, String.format(format, i)); 78344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki if (nfc.createNewFile(file)) { 78444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return file; 78544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 78644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 78744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki return null; 78844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki } 789bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki 790bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki /** 791bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki * @return a long in column {@code column} of the first result row, if the query returns at 792bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki * least 1 row. Otherwise returns {@code defaultValue}. 793bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki */ 794bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki public static Long getFirstRowLong(Context context, Uri uri, String[] projection, 795bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column, 796bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki Long defaultValue) { 797bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki Cursor c = context.getContentResolver().query(uri, projection, selection, selectionArgs, 798bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki sortOrder); 799bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki try { 800bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki if (c.moveToFirst()) { 801bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki return c.getLong(column); 802bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 803bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } finally { 804bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki c.close(); 805bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 806bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki return defaultValue; 807bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 808bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki 809bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki /** 810bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki * {@link #getFirstRowLong} with null as a default value. 811bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki */ 812bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki public static Long getFirstRowLong(Context context, Uri uri, String[] projection, 813bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column) { 814bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki return getFirstRowLong(context, uri, projection, selection, selectionArgs, 815bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki sortOrder, column, null); 816bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki } 817833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 818833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki /** 81936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki * @return an integer in column {@code column} of the first result row, if the query returns at 82036bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki * least 1 row. Otherwise returns {@code defaultValue}. 82136bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki */ 82236bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki public static Integer getFirstRowInt(Context context, Uri uri, String[] projection, 82336bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column, 82436bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki Integer defaultValue) { 82536bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki Long longDefault = (defaultValue == null) ? null : defaultValue.longValue(); 82636bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki Long result = getFirstRowLong(context, uri, projection, selection, selectionArgs, sortOrder, 82736bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki column, longDefault); 82836bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki return (result == null) ? null : result.intValue(); 82936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki } 83036bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki 83136bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki /** 83236bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki * {@link #getFirstRowInt} with null as a default value. 83336bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki */ 83436bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki public static Integer getFirstRowInt(Context context, Uri uri, String[] projection, 83536bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki String selection, String[] selectionArgs, String sortOrder, int column) { 83636bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki return getFirstRowInt(context, uri, projection, selection, selectionArgs, 83736bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki sortOrder, column, null); 83836bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki } 83936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki 84036bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki /** 841833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki * A class used to restore ListView state (e.g. scroll position) when changing adapter. 842833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki */ 8437fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki public static class ListStateSaver implements Parcelable { 844833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki private final Parcelable mState; 845833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 8467fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki private ListStateSaver(Parcel p) { 8477fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki mState = p.readParcelable(null); 8487fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki } 8497fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki 850833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public ListStateSaver(AbsListView lv) { 851833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki mState = lv.onSaveInstanceState(); 852833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 853833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki 854833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki public void restore(AbsListView lv) { 855833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki lv.onRestoreInstanceState(mState); 856833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 8577fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki 8587fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki @Override 8597fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki public int describeContents() { 8607fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki return 0; 8617fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki } 8627fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki 8637fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki @Override 8647fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki public void writeToParcel(Parcel dest, int flags) { 8657fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki dest.writeParcelable(mState, flags); 8667fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki } 8677fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki 8687fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki public static final Parcelable.Creator<ListStateSaver> CREATOR 8697fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki = new Parcelable.Creator<ListStateSaver>() { 8707fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki public ListStateSaver createFromParcel(Parcel in) { 8717fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki return new ListStateSaver(in); 8727fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki } 8737fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki 8747fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki public ListStateSaver[] newArray(int size) { 8757fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki return new ListStateSaver[size]; 8767fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki } 8777fd307212f076fab56f575988e5e5bfaf1abdcc3Makoto Onuki }; 878833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki } 879533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki 8804dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank public static boolean attachmentExists(Context context, Attachment attachment) { 8814dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank if (attachment == null) { 8824dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank return false; 8834dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank } else if (attachment.mContentBytes != null) { 8844dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank return true; 8854dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank } else if (TextUtils.isEmpty(attachment.mContentUri)) { 886567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki Log.w(Email.LOG_TAG, "attachmentExists ContentUri null."); 887d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki return false; 888d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki } 889d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki if (Email.DEBUG) { 890d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki Log.d(Email.LOG_TAG, "attachmentExists URI=" + attachment.mContentUri); 891d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki } 892d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki try { 893567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki Uri fileUri = Uri.parse(attachment.mContentUri); 894d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki try { 895567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki InputStream inStream = context.getContentResolver().openInputStream(fileUri); 896567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki try { 897567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki inStream.close(); 898567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki } catch (IOException e) { 899567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki // Nothing to be done if can't close the stream 900567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki } 901567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki return true; 902567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki } catch (FileNotFoundException e) { 903567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki return false; 904d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki } 905567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki } catch (RuntimeException re) { 906567ed19f7b2ca4c97f022b299ff2f9b5eba8c937Makoto Onuki Log.w(Email.LOG_TAG, "attachmentExists RuntimeException=" + re); 907d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki return false; 908d755cdce13217d25ac33169b5e410709636255c4Makoto Onuki } 90909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 91009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 91109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank /** 91209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Check whether the message with a given id has unloaded attachments. If the message is 91309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * a forwarded message, we look instead at the messages's source for the attachments. If the 91409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * message or forward source can't be found, we return false 91509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * @param context the caller's context 91609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * @param messageId the id of the message 91709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * @return whether or not the message has unloaded attachments 91809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank */ 91909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static boolean hasUnloadedAttachments(Context context, long messageId) { 92009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Message msg = Message.restoreMessageWithId(context, messageId); 92109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank if (msg == null) return false; 92209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment[] atts = Attachment.restoreAttachmentsWithMessageId(context, messageId); 92309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank for (Attachment att: atts) { 9244dcb1c5fdaacc40309b77af2a32532bc60218523Marc Blank if (!attachmentExists(context, att)) { 92509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // If the attachment doesn't exist and isn't marked for download, we're in trouble 92609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // since the outbound message will be stuck indefinitely in the Outbox. Instead, 92709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // we'll just delete the attachment and continue; this is far better than the 92809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // alternative. In theory, this situation shouldn't be possible. 92909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank if ((att.mFlags & (Attachment.FLAG_DOWNLOAD_FORWARD | 93009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment.FLAG_DOWNLOAD_USER_REQUEST)) == 0) { 93109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Log.d(Email.LOG_TAG, "Unloaded attachment isn't marked for download: " + 93209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank att.mFileName + ", #" + att.mId); 93309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment.delete(context, Attachment.CONTENT_URI, att.mId); 93409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } else if (att.mContentUri != null) { 93509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // In this case, the attachment file is gone from the cache; let's clear the 93609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank // contentUri; this should be a very unusual case 93709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank ContentValues cv = new ContentValues(); 93809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank cv.putNull(AttachmentColumns.CONTENT_URI); 93909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Attachment.update(context, Attachment.CONTENT_URI, att.mId, cv); 94009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 94109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return true; 94209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 94309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 94409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return false; 94509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 94609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 94709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank /** 94809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Convenience method wrapping calls to retrieve columns from a single row, via EmailProvider. 94909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * The arguments are exactly the same as to contentResolver.query(). Results are returned in 95009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * an array of Strings corresponding to the columns in the projection. If the cursor has no 95109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * rows, null is returned. 95209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank */ 95309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static String[] getRowColumns(Context context, Uri contentUri, String[] projection, 95409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank String selection, String[] selectionArgs) { 95509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank String[] values = new String[projection.length]; 95609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank ContentResolver cr = context.getContentResolver(); 95709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank Cursor c = cr.query(contentUri, projection, selection, selectionArgs, null); 95809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank try { 95909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank if (c.moveToFirst()) { 96009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank for (int i = 0; i < projection.length; i++) { 96109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank values[i] = c.getString(i); 96209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 96309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } else { 96409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return null; 96509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 96609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } finally { 96709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank c.close(); 96809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 96909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return values; 97009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 97109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 97209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank /** 97309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Convenience method for retrieving columns from a particular row in EmailProvider. 97409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * Passed in here are a base uri (e.g. Message.CONTENT_URI), the unique id of a row, and 97509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank * a projection. This method calls the previous one with the appropriate URI. 97609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank */ 97709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static String[] getRowColumns(Context context, Uri baseUri, long id, 97809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank String ... projection) { 97909fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return getRowColumns(context, ContentUris.withAppendedId(baseUri, id), projection, null, 98009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank null); 98109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 98209fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank 98309fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank public static boolean isExternalStorageMounted() { 98409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); 98509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank } 986533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki /** 987533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki * STOPSHIP Remove this method 988533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki * Toggle between portrait and landscape. Developement use only. 989533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki */ 990533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki public static void changeOrientation(Activity activity) { 991533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki activity.setRequestedOrientation( 992533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki (activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) 993533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 994533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 995533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki } 996f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 997f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki /** 998f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki * Class that supports running any operation for each account. 999f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki */ 1000f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki public abstract static class ForEachAccount extends AsyncTask<Void, Void, Long[]> { 1001f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki private final Context mContext; 1002f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 1003f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki public ForEachAccount(Context context) { 1004f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki mContext = context; 1005f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1006f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 1007f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki @Override 1008f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected final Long[] doInBackground(Void... params) { 1009f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki ArrayList<Long> ids = new ArrayList<Long>(); 1010f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki Cursor c = mContext.getContentResolver().query(EmailContent.Account.CONTENT_URI, 1011f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki EmailContent.Account.ID_PROJECTION, null, null, null); 1012f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki try { 1013f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki while (c.moveToNext()) { 1014f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki ids.add(c.getLong(EmailContent.Account.ID_PROJECTION_COLUMN)); 1015f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1016f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } finally { 1017f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki c.close(); 1018f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1019f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki return ids.toArray(EMPTY_LONGS); 1020f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1021f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 1022f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki @Override 1023f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected final void onPostExecute(Long[] ids) { 1024f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki if (ids != null && !isCancelled()) { 1025f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki for (long id : ids) { 1026f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki performAction(id); 1027f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1028f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1029f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki onFinished(); 1030f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1031f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 1032f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki /** 1033f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki * This method will be called for each account. 1034f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki */ 1035f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected abstract void performAction(long accountId); 1036f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki 1037f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki /** 1038f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki * Called when the iteration is finished. 1039f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki */ 1040f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki protected void onFinished() { 1041f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 1042f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki } 104396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 1044