Utility.java revision f52afae9424fe41071cc34a8d6cbcb82b992a411
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;
22da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadlerimport com.android.email.provider.EmailContent.HostAuth;
23da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadlerimport com.android.email.provider.EmailContent.HostAuthColumns;
24e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.Mailbox;
25e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.MailboxColumns;
26e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.Message;
27e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport com.android.email.provider.EmailContent.MessageColumns;
28e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler
297e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onukiimport android.app.Activity;
30e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.content.ContentResolver;
31f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.Context;
32533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onukiimport android.content.pm.ActivityInfo;
3344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onukiimport android.content.res.Resources;
34f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.res.TypedArray;
35e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.database.Cursor;
36f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.graphics.drawable.Drawable;
37bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onukiimport android.net.Uri;
3859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onukiimport android.os.AsyncTask;
39833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onukiimport android.os.Parcelable;
40d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.security.MessageDigest;
41d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.telephony.TelephonyManager;
42128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport android.text.TextUtils;
436cec1104fe8863fce2ee86ff5145076e6c436a00Doug Zongkerimport android.util.Base64;
44d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.util.Log;
45833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onukiimport android.widget.AbsListView;
46f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.widget.TextView;
477e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onukiimport android.widget.Toast;
48e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler
497e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onukiimport java.io.ByteArrayInputStream;
5044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onukiimport java.io.File;
5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStreamReader;
5496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.UnsupportedEncodingException;
5520225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.ByteBuffer;
5620225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.CharBuffer;
5720225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.charset.Charset;
58d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport java.security.NoSuchAlgorithmException;
59f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onukiimport java.util.ArrayList;
6096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.Date;
61989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.GregorianCalendar;
62989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.TimeZone;
63128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport java.util.regex.Pattern;
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class Utility {
6620225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki    public static final Charset UTF_8 = Charset.forName("UTF-8");
677e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static final Charset ASCII = Charset.forName("US-ASCII");
687e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki
697e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static final String[] EMPTY_STRINGS = new String[0];
70f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki    public static final Long[] EMPTY_LONGS = new Long[0];
7120225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki
72128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    // "GMT" + "+" or "-" + 4 digits
73128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE =
74128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki            Pattern.compile("GMT([-+]\\d{4})$");
75128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public final static String readInputStream(InputStream in, String encoding) throws IOException {
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        InputStreamReader reader = new InputStreamReader(in, encoding);
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        StringBuffer sb = new StringBuffer();
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int count;
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        char[] buf = new char[512];
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        while ((count = reader.read(buf)) != -1) {
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            sb.append(buf, 0, count);
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return sb.toString();
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public final static boolean arrayContains(Object[] a, Object o) {
8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0, count = a.length; i < count; i++) {
8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (a[i].equals(o)) {
9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return true;
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return false;
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Combines the given array of Objects into a single string using the
9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * seperator character and each Object's toString() method. between each
9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * part.
10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *
10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param parts
10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param seperator
10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return
10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String combine(Object[] parts, char seperator) {
10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (parts == null) {
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        StringBuffer sb = new StringBuffer();
11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0; i < parts.length; i++) {
11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            sb.append(parts[i].toString());
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (i < parts.length - 1) {
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                sb.append(seperator);
11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return sb.toString();
11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String base64Decode(String encoded) {
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (encoded == null) {
12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
122f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker        byte[] decoded = Base64.decode(encoded, Base64.DEFAULT);
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new String(decoded);
12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String base64Encode(String s) {
12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (s == null) {
12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return s;
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
130f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker        return Base64.encodeToString(s.getBytes(), Base64.NO_WRAP);
13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
133e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank    public static boolean isTextViewNotEmpty(TextView view) {
134e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        return !TextUtils.isEmpty(view.getText());
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
137e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank    public static boolean isPortFieldValid(TextView view) {
138e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        CharSequence chars = view.getText();
139e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        if (TextUtils.isEmpty(chars)) return false;
140e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        Integer port;
141e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        // In theory, we can't get an illegal value here, since the field is monitored for valid
142e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        // numeric input. But this might be used elsewhere without such a check.
143e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        try {
144e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank            port = Integer.parseInt(chars.toString());
145e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        } catch (NumberFormatException e) {
146e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank            return false;
147e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        }
148e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank        return port > 0 && port < 65536;
149e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank     }
15096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
152e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank     * Ensures that the given string starts and ends with the double quote character. The string is
153e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank     * not modified in any way except to add the double quote character to start and end if it's not
154e6cc662abc0b5fffe223cda5e980b4f05a4e91ddMarc Blank     * already there.
155f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
15696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * TODO: Rename this, because "quoteString()" can mean so many different things.
157f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * sample -> "sample"
15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * "sample" -> "sample"
16096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * ""sample"" -> "sample"
16196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * "sample"" -> "sample"
16296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * sa"mp"le -> "sa"mp"le"
16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * "sa"mp"le" -> "sa"mp"le"
16496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * (empty string) -> ""
16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * " -> ""
16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param s
16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String quoteString(String s) {
17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (s == null) {
17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (!s.matches("^\".*\"$")) {
17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return "\"" + s + "\"";
17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        else {
17796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return s;
17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
180f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
182f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     * Apply quoting rules per IMAP RFC,
18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * quoted          = DQUOTE *QUOTED-CHAR DQUOTE
18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * quoted-specials = DQUOTE / "\"
186f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is used primarily for IMAP login, but might be useful elsewhere.
188f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * NOTE:  Not very efficient - you may wish to preflight this, or perhaps it should check
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * for trouble chars before calling the replace functions.
191f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param s The string to be quoted.
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return A copy of the string, having undergone quoting as described above
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String imapQuoted(String s) {
196f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // First, quote any backslashes by replacing \ with \\
19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // regex Pattern:  \\    (Java string const = \\\\)
19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // Substitute:     \\\\  (Java string const = \\\\\\\\)
20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String result = s.replaceAll("\\\\", "\\\\\\\\");
201f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // Then, quote any double-quotes by replacing " with \"
20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // regex Pattern:  "    (Java string const = \")
20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // Substitute:     \\"  (Java string const = \\\\\")
20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        result = result.replaceAll("\"", "\\\\\"");
206f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // return string with quotes around it
20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return "\"" + result + "\"";
20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
210f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * A fast version of  URLDecoder.decode() that works only with UTF-8 and does only two
21396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * allocations. This version is around 3x as fast as the standard one and I'm using it
21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * hundreds of times in places that slow down the UI, so it helps.
21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String fastUrlDecode(String s) {
21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        try {
21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            byte[] bytes = s.getBytes("UTF-8");
21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            byte ch;
22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int length = 0;
22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (int i = 0, count = bytes.length; i < count; i++) {
22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                ch = bytes[i];
22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (ch == '%') {
22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    int h = (bytes[i + 1] - '0');
22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    int l = (bytes[i + 2] - '0');
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (h > 9) {
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        h -= 7;
22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (l > 9) {
23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        l -= 7;
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    bytes[length] = (byte) ((h << 4) | l);
23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    i += 2;
23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                else if (ch == '+') {
23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    bytes[length] = ' ';
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                else {
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    bytes[length] = bytes[i];
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                length++;
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return new String(bytes, 0, length, "UTF-8");
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        catch (UnsupportedEncodingException uee) {
24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Returns true if the specified date is within today. Returns false otherwise.
25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param date
25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return
25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static boolean isDateToday(Date date) {
25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // TODO But Calendar is so slowwwwwww....
25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Date today = new Date();
25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (date.getYear() == today.getYear() &&
25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                date.getMonth() == today.getMonth() &&
26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                date.getDate() == today.getDate()) {
26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return true;
26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return false;
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /*
26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * TODO disabled this method globally. It is used in all the settings screens but I just
26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * noticed that an unrelated icon was dimmed. Android must share drawables internally.
26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static void setCompoundDrawablesAlpha(TextView view, int alpha) {
27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        Drawable[] drawables = view.getCompoundDrawables();
27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        for (Drawable drawable : drawables) {
27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//            if (drawable != null) {
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//                drawable.setAlpha(alpha);
27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//            }
27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        }
27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
278e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler
279e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler    // TODO: unit test this
280e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler    public static String buildMailboxIdSelection(ContentResolver resolver, long mailboxId) {
281e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        // Setup default selection & args, then add to it as necessary
282e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        StringBuilder selection = new StringBuilder(
2836c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                MessageColumns.FLAG_LOADED + " IN ("
2846c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                + Message.FLAG_LOADED_PARTIAL + "," + Message.FLAG_LOADED_COMPLETE
2856c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                + ") AND ");
286e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        if (mailboxId == Mailbox.QUERY_ALL_INBOXES
287e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            || mailboxId == Mailbox.QUERY_ALL_DRAFTS
288e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            || mailboxId == Mailbox.QUERY_ALL_OUTBOX) {
289e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            // query for all mailboxes of type INBOX, DRAFTS, or OUTBOX
290e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            int type;
291e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            if (mailboxId == Mailbox.QUERY_ALL_INBOXES) {
292e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                type = Mailbox.TYPE_INBOX;
293e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) {
294e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                type = Mailbox.TYPE_DRAFTS;
295e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            } else {
296e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                type = Mailbox.TYPE_OUTBOX;
297e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            }
298e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            StringBuilder inboxes = new StringBuilder();
299e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            Cursor c = resolver.query(Mailbox.CONTENT_URI,
300e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                        EmailContent.ID_PROJECTION,
301e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                        MailboxColumns.TYPE + "=? AND " + MailboxColumns.FLAG_VISIBLE + "=1",
302e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                        new String[] { Integer.toString(type) }, null);
303e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            // build an IN (mailboxId, ...) list
304e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            // TODO do this directly in the provider
305e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            while (c.moveToNext()) {
306e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                if (inboxes.length() != 0) {
307e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                    inboxes.append(",");
308e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                }
309e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                inboxes.append(c.getLong(EmailContent.ID_PROJECTION_COLUMN));
310e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            }
311e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            c.close();
312e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(MessageColumns.MAILBOX_KEY + " IN ");
313e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append("(").append(inboxes).append(")");
314e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        } else  if (mailboxId == Mailbox.QUERY_ALL_UNREAD) {
315e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(Message.FLAG_READ + "=0");
316e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) {
317e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(Message.FLAG_FAVORITE + "=1");
318e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        } else {
319e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(MessageColumns.MAILBOX_KEY + "=" + mailboxId);
320e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        }
321e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        return selection.toString();
322e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler    }
323dadba9949895696108b31124fc0c6aa1a297ab1csatok
324833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki    // TODO When the UI is settled, cache all strings/drawables
325833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki    // TODO When the UI is settled, write up tests
326833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki    // TODO When the UI is settled, remove backward-compatibility methods
327dadba9949895696108b31124fc0c6aa1a297ab1csatok    public static class FolderProperties {
328dadba9949895696108b31124fc0c6aa1a297ab1csatok
329dadba9949895696108b31124fc0c6aa1a297ab1csatok        private static FolderProperties sInstance;
330dadba9949895696108b31124fc0c6aa1a297ab1csatok
331833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final Context mContext;
332833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
333dadba9949895696108b31124fc0c6aa1a297ab1csatok        // Caches for frequently accessed resources.
334833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final String[] mSpecialMailbox;
335833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final TypedArray mSpecialMailboxDrawable;
336833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final Drawable mDefaultMailboxDrawable;
337833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final Drawable mSummaryStarredMailboxDrawable;
338833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final Drawable mSummaryCombinedInboxDrawable;
339dadba9949895696108b31124fc0c6aa1a297ab1csatok
340dadba9949895696108b31124fc0c6aa1a297ab1csatok        private FolderProperties(Context context) {
341833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            mContext = context.getApplicationContext();
342fa52e6c95674aef6461a5cfc670a052e1c5b7f2fAndrew Stadler            mSpecialMailbox = context.getResources().getStringArray(R.array.mailbox_display_names);
343dadba9949895696108b31124fc0c6aa1a297ab1csatok            for (int i = 0; i < mSpecialMailbox.length; ++i) {
344dadba9949895696108b31124fc0c6aa1a297ab1csatok                if ("".equals(mSpecialMailbox[i])) {
345dadba9949895696108b31124fc0c6aa1a297ab1csatok                    // there is no localized name, so use the display name from the server
346dadba9949895696108b31124fc0c6aa1a297ab1csatok                    mSpecialMailbox[i] = null;
347dadba9949895696108b31124fc0c6aa1a297ab1csatok                }
348dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
349dadba9949895696108b31124fc0c6aa1a297ab1csatok            mSpecialMailboxDrawable =
350fa52e6c95674aef6461a5cfc670a052e1c5b7f2fAndrew Stadler                context.getResources().obtainTypedArray(R.array.mailbox_display_icons);
351dadba9949895696108b31124fc0c6aa1a297ab1csatok            mDefaultMailboxDrawable =
352dadba9949895696108b31124fc0c6aa1a297ab1csatok                context.getResources().getDrawable(R.drawable.ic_list_folder);
3533786cab2aa1776e92fc67af3931a76cd8b848b48satok            mSummaryStarredMailboxDrawable =
3543786cab2aa1776e92fc67af3931a76cd8b848b48satok                context.getResources().getDrawable(R.drawable.ic_list_starred);
3553786cab2aa1776e92fc67af3931a76cd8b848b48satok            mSummaryCombinedInboxDrawable =
3563786cab2aa1776e92fc67af3931a76cd8b848b48satok                context.getResources().getDrawable(R.drawable.ic_list_combined_inbox);
357dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
358dadba9949895696108b31124fc0c6aa1a297ab1csatok
3591f2caa80957e92519258e212b5fd45fb6c168a73Makoto Onuki        public static synchronized FolderProperties getInstance(Context context) {
360dadba9949895696108b31124fc0c6aa1a297ab1csatok            if (sInstance == null) {
3611f2caa80957e92519258e212b5fd45fb6c168a73Makoto Onuki                sInstance = new FolderProperties(context);
362dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
363dadba9949895696108b31124fc0c6aa1a297ab1csatok            return sInstance;
364dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
365dadba9949895696108b31124fc0c6aa1a297ab1csatok
366833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        // For backward compatibility.
367833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public String getDisplayName(int type) {
368833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            return getDisplayName(type, -1);
369833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        }
370833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
371833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        // For backward compatibility.
372833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public Drawable getSummaryMailboxIconIds(long id) {
373833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            return getIcon(-1, id);
374833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        }
375833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
376833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public Drawable getIconIds(int type) {
377833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            return getIcon(type, -1);
378833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        }
379833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
380dadba9949895696108b31124fc0c6aa1a297ab1csatok        /**
381dadba9949895696108b31124fc0c6aa1a297ab1csatok         * Lookup names of localized special mailboxes
382dadba9949895696108b31124fc0c6aa1a297ab1csatok         */
383833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public String getDisplayName(int type, long mailboxId) {
384833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            // Special combined mailboxes
385833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            int resId = 0;
386833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
387833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            // Can't use long for switch!?
388833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            if (mailboxId == Mailbox.QUERY_ALL_INBOXES) {
389833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki                resId = R.string.account_folder_list_summary_inbox;
390833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) {
391833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki                resId = R.string.account_folder_list_summary_starred;
392833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) {
393833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki                resId = R.string.account_folder_list_summary_drafts;
394833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            } else if (mailboxId == Mailbox.QUERY_ALL_OUTBOX) {
395833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki                resId = R.string.account_folder_list_summary_outbox;
396833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            }
397833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            if (resId != 0) {
398833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki                return mContext.getString(resId);
399833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            }
400833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
401dadba9949895696108b31124fc0c6aa1a297ab1csatok            if (type < mSpecialMailbox.length) {
402dadba9949895696108b31124fc0c6aa1a297ab1csatok                return mSpecialMailbox[type];
403dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
404dadba9949895696108b31124fc0c6aa1a297ab1csatok            return null;
405dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
406dadba9949895696108b31124fc0c6aa1a297ab1csatok
407dadba9949895696108b31124fc0c6aa1a297ab1csatok        /**
408dadba9949895696108b31124fc0c6aa1a297ab1csatok         * Lookup icons of special mailboxes
409dadba9949895696108b31124fc0c6aa1a297ab1csatok         */
410833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public Drawable getIcon(int type, long mailboxId) {
411833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            if (mailboxId == Mailbox.QUERY_ALL_INBOXES) {
4123786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSummaryCombinedInboxDrawable;
413833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) {
4143786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSummaryStarredMailboxDrawable;
415833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) {
4163786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSpecialMailboxDrawable.getDrawable(Mailbox.TYPE_DRAFTS);
417833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            } else if (mailboxId == Mailbox.QUERY_ALL_OUTBOX) {
4183786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSpecialMailboxDrawable.getDrawable(Mailbox.TYPE_OUTBOX);
4193786cab2aa1776e92fc67af3931a76cd8b848b48satok            }
420833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            if (0 <= type && type < mSpecialMailboxDrawable.length()) {
421833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki                return mSpecialMailboxDrawable.getDrawable(type);
422833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            }
4233786cab2aa1776e92fc67af3931a76cd8b848b48satok            return mDefaultMailboxDrawable;
4243786cab2aa1776e92fc67af3931a76cd8b848b48satok        }
425dadba9949895696108b31124fc0c6aa1a297ab1csatok    }
426da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler
427da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    private final static String HOSTAUTH_WHERE_CREDENTIALS = HostAuthColumns.ADDRESS + " like ?"
428da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            + " and " + HostAuthColumns.LOGIN + " like ?"
429da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            + " and " + HostAuthColumns.PROTOCOL + " not like \"smtp\"";
430da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    private final static String ACCOUNT_WHERE_HOSTAUTH = AccountColumns.HOST_AUTH_KEY_RECV + "=?";
431da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler
432da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    /**
433da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * Look for an existing account with the same username & server
434da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     *
435da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @param context a system context
436da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @param allowAccountId this account Id will not trigger (when editing an existing account)
4379d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank     * @param hostName the server's address
4389d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank     * @param userLogin the user's login string
4399d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank     * @result null = no matching account found.  Account = matching account
440da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     */
4419d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank    public static Account findExistingAccount(Context context, long allowAccountId,
4429d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank            String hostName, String userLogin) {
443da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        ContentResolver resolver = context.getContentResolver();
444da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        Cursor c = resolver.query(HostAuth.CONTENT_URI, HostAuth.ID_PROJECTION,
445da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                HOSTAUTH_WHERE_CREDENTIALS, new String[] { hostName, userLogin }, null);
446da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        try {
447da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            while (c.moveToNext()) {
448da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                long hostAuthId = c.getLong(HostAuth.ID_PROJECTION_COLUMN);
4499d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank                // Find account with matching hostauthrecv key, and return it
450da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                Cursor c2 = resolver.query(Account.CONTENT_URI, Account.ID_PROJECTION,
451da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        ACCOUNT_WHERE_HOSTAUTH, new String[] { Long.toString(hostAuthId) }, null);
452da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                try {
453da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                    while (c2.moveToNext()) {
454da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        long accountId = c2.getLong(Account.ID_PROJECTION_COLUMN);
455da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        if (accountId != allowAccountId) {
456da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                            Account account = Account.restoreAccountWithId(context, accountId);
457da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                            if (account != null) {
4589d387ff0bb88ad952b53c956b468dcbcec248752Marc Blank                                return account;
459da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                            }
460da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        }
461da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                    }
462da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                } finally {
463da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                    c2.close();
464da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                }
465da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            }
466da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        } finally {
467da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            c.close();
468da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        }
469da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler
470da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        return null;
471da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    }
472eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler
473eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler    /**
474eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler     * Generate a random message-id header for locally-generated messages.
475eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler     */
476eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler    public static String generateMessageId() {
477eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        StringBuffer sb = new StringBuffer();
478eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append("<");
479eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        for (int i = 0; i < 24; i++) {
480eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler            sb.append(Integer.toString((int)(Math.random() * 35), 36));
481eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        }
482eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append(".");
483eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append(Long.toString(System.currentTimeMillis()));
484eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append("@email.android.com>");
485eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        return sb.toString();
486eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler    }
487eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler
488989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    /**
489989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * Generate a time in milliseconds from a date string that represents a date/time in GMT
490989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @param DateTime date string in format 20090211T180303Z (rfc2445, iCalendar).
491989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @return the time in milliseconds (since Jan 1, 1970)
492989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     */
493989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    public static long parseDateTimeToMillis(String date) {
494989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        GregorianCalendar cal = parseDateTimeToCalendar(date);
495989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        return cal.getTimeInMillis();
496989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    }
497989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda
498989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    /**
499989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * Generate a GregorianCalendar from a date string that represents a date/time in GMT
500989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @param DateTime date string in format 20090211T180303Z (rfc2445, iCalendar).
501989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @return the GregorianCalendar
502989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     */
503989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    public static GregorianCalendar parseDateTimeToCalendar(String date) {
504989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
505989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)),
506989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(9, 11)), Integer.parseInt(date.substring(11, 13)),
507989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(13, 15)));
508989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
509989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        return cal;
510989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    }
511989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda
512989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    /**
513989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * Generate a time in milliseconds from an email date string that represents a date/time in GMT
514989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @param Email style DateTime string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339)
515989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @return the time in milliseconds (since Jan 1, 1970)
516989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     */
517989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    public static long parseEmailDateTimeToMillis(String date) {
518989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
519989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(5, 7)) - 1, Integer.parseInt(date.substring(8, 10)),
520989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(11, 13)), Integer.parseInt(date.substring(14, 16)),
521989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(17, 19)));
522989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
523989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        return cal.getTimeInMillis();
524989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    }
52520225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki
5267e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    private static byte[] encode(Charset charset, String s) {
52720225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        if (s == null) {
52820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki            return null;
52920225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        }
5307e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        final ByteBuffer buffer = charset.encode(CharBuffer.wrap(s));
53120225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        final byte[] bytes = new byte[buffer.limit()];
53220225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        buffer.get(bytes);
53320225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        return bytes;
53420225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki    }
53588a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki
5367e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    private static String decode(Charset charset, byte[] b) {
537fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        if (b == null) {
538fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki            return null;
539fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        }
5407e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        final CharBuffer cb = charset.decode(ByteBuffer.wrap(b));
541fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        return new String(cb.array(), 0, cb.length());
542fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki    }
543fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki
5447e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    /** Converts a String to UTF-8 */
5457e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static byte[] toUtf8(String s) {
5467e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        return encode(UTF_8, s);
5477e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    }
5487e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki
5497e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    /** Builds a String from UTF-8 bytes */
5507e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static String fromUtf8(byte[] b) {
5517e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        return decode(UTF_8, b);
5527e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    }
5537e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki
5547e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    /** Converts a String to ASCII bytes */
5557e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static byte[] toAscii(String s) {
5567e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        return encode(ASCII, s);
5577e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    }
5587e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki
5597e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    /** Builds a String from ASCII bytes */
5607e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static String fromAscii(byte[] b) {
5617e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        return decode(ASCII, b);
5627e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    }
5637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki
56488a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki    /**
56588a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki     * @return true if the input is the first (or only) byte in a UTF-8 character
56688a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki     */
56788a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki    public static boolean isFirstUtf8Byte(byte b) {
56888a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki        // If the top 2 bits is '10', it's not a first byte.
56988a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki        return (b & 0xc0) != 0x80;
57088a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki    }
571dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
572dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    public static String byteToHex(int b) {
573dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        return byteToHex(new StringBuilder(), b).toString();
574dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    }
575dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
576dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    public static StringBuilder byteToHex(StringBuilder sb, int b) {
577dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        b &= 0xFF;
578dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        sb.append("0123456789ABCDEF".charAt(b >> 4));
579dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        sb.append("0123456789ABCDEF".charAt(b & 0xF));
580dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        return sb;
581dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    }
582eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank
583eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank    public static String replaceBareLfWithCrlf(String str) {
584eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank        return str.replace("\r", "").replace("\n", "\r\n");
585eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank    }
58659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki
58759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    /**
58859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     * Cancel an {@link AsyncTask}.  If it's already running, it'll be interrupted.
58959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     */
59059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    public static void cancelTaskInterrupt(AsyncTask<?, ?, ?> task) {
59159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki        cancelTask(task, true);
59259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    }
59359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki
59459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    /**
59559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     * Cancel an {@link AsyncTask}.
59659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     *
59759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
59859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     *        task should be interrupted; otherwise, in-progress tasks are allowed
59959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     *        to complete.
60059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     */
60159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) {
60259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki        if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
60359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki            task.cancel(mayInterruptIfRunning);
60459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki        }
60559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    }
606d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki
607d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    /**
608d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     * @return Device's unique ID if available.  null if the device has no unique ID.
609d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     */
610d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    public static String getConsistentDeviceId(Context context) {
611d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final String deviceId;
612d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        try {
613d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            TelephonyManager tm =
614d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                    (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
615d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            if (tm == null) {
616d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                return null;
617d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            }
618d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            deviceId = tm.getDeviceId();
619d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            if (deviceId == null) {
620d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                return null;
621d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            }
622d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        } catch (Exception e) {
623d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            Log.d(Email.LOG_TAG, "Error in TelephonyManager.getDeviceId(): " + e.getMessage());
624d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            return null;
625d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        }
626d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final MessageDigest sha;
627d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        try {
628d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            sha = MessageDigest.getInstance("SHA-1");
629d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        } catch (NoSuchAlgorithmException impossible) {
630d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            return null;
631d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        }
632d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        sha.update(Utility.toUtf8(deviceId));
633d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final int hash = getSmallHashFromSha1(sha.digest());
634d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        return Integer.toString(hash);
635d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    }
636d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki
637d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    /**
638d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     * @return a non-negative integer generated from 20 byte SHA-1 hash.
639d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     */
640d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    /* package for testing */ static int getSmallHashFromSha1(byte[] sha1) {
641d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final int offset = sha1[19] & 0xf; // SHA1 is 20 bytes.
642d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        return ((sha1[offset]  & 0x7f) << 24)
643d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                | ((sha1[offset + 1] & 0xff) << 16)
644d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                | ((sha1[offset + 2] & 0xff) << 8)
645d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                | ((sha1[offset + 3] & 0xff));
646d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    }
647128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki
648128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    /**
649128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     * Try to make a date MIME(RFC 2822/5322)-compliant.
650128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     *
651128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     * It fixes:
652128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700"
653128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     *   (4 digit zone value can't be preceded by "GMT")
654128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     *   We got a report saying eBay sends a date in this format
655128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     */
656128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    public static String cleanUpMimeDate(String date) {
657128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        if (TextUtils.isEmpty(date)) {
658128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki            return date;
659128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        }
660128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1");
661128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        return date;
662128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    }
6637e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki
6647e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    public static ByteArrayInputStream streamFromAsciiString(String ascii) {
6657e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki        return new ByteArrayInputStream(toAscii(ascii));
6667e5ba0e1eaee76ab6e6c7ea9362348f660796596Makoto Onuki    }
6677e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki
6687e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki    /**
6697e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki     * A thread safe way to show a Toast.  This method uses {@link Activity#runOnUiThread}, so it
6707e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki     * can be called on any thread.
6717e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki     *
6727e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki     * @param activity Parent activity.
6737e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki     * @param resId Resource ID of the message string.
6747e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki     */
67591d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki    public static void showToast(Activity activity, int resId) {
67691d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki        showToast(activity, activity.getResources().getString(resId));
67791d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki    }
67891d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki
67991d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki    /**
68091d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki     * A thread safe way to show a Toast.  This method uses {@link Activity#runOnUiThread}, so it
68191d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki     * can be called on any thread.
68291d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki     *
68391d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki     * @param activity Parent activity.
68491d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki     * @param message Message to show.
68591d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki     */
68691d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki    public static void showToast(final Activity activity, final String message) {
6877e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki        activity.runOnUiThread(new Runnable() {
6887e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki            public void run() {
68991d47ccb47e4d4e340854a091d39886fbbadbf15Makoto Onuki                Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
6907e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki            }
6917e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki        });
6927e24c6c6f9efcba98b7e26f9e387b6591acd1355Makoto Onuki    }
6933f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki
6943f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki    /**
6953f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki     * Run {@code r} on a worker thread.
6963f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki     */
6973f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki    public static void runAsync(final Runnable r) {
6983f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki        new AsyncTask<Void, Void, Void>() {
6993f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki            @Override protected Void doInBackground(Void... params) {
7003f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki                r.run();
7013f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki                return null;
7023f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki            }
7033f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki        }.execute();
7043f545a4060982b8a5d715905c7818d59056c1ee0Makoto Onuki    }
70544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
70644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    /**
70744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     * Formats the given size as a String in bytes, kB, MB or GB.  Ex: 12,315,000 = 11 MB
70844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     */
70944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    public static String formatSize(Context context, long size) {
71044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        final Resources res = context.getResources();
71144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        final long KB = 1024;
71244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        final long MB = (KB * 1024);
71344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        final long GB  = (MB * 1024);
71444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
71544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        int resId;
71644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        int value;
71744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
71844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        if (size < KB) {
71944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            resId = R.plurals.message_view_attachment_bytes;
72044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            value = (int) size;
72144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        } else if (size < MB) {
72244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            resId = R.plurals.message_view_attachment_kilobytes;
72344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            value = (int) (size / KB);
72444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        } else if (size < GB) {
72544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            resId = R.plurals.message_view_attachment_megabytes;
72644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            value = (int) (size / MB);
72744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        } else {
72844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            resId = R.plurals.message_view_attachment_gigabytes;
72944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            value = (int) (size / GB);
73044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        }
73144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        return res.getQuantityString(resId, value, value);
73244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    }
73344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
73444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    /**
73544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     * Interface used in {@link #createUniqueFile} instead of {@link File#createNewFile()} to make
73644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     * it testable.
73744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     */
73844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    /* package */ interface NewFileCreator {
73944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        public static final NewFileCreator DEFAULT = new NewFileCreator() {
74044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki                    @Override public boolean createNewFile(File f) throws IOException {
74144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki                        return f.createNewFile();
74244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki                    }
74344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        };
74444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        public boolean createNewFile(File f) throws IOException ;
74544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    }
74644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
74744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    /**
74844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     * Creates a new empty file with a unique name in the given directory by appending a hyphen and
74944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     * a number to the given filename.
75044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     *
75144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     * @return a new File object, or null if one could not be created
75244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki     */
75344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    public static File createUniqueFile(File directory, String filename) throws IOException {
75444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        return createUniqueFileInternal(NewFileCreator.DEFAULT, directory, filename);
75544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    }
75644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
75744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    /* package */ static File createUniqueFileInternal(NewFileCreator nfc,
75844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            File directory, String filename) throws IOException {
75944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        File file = new File(directory, filename);
76044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        if (nfc.createNewFile(file)) {
76144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            return file;
76244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        }
76344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        // Get the extension of the file, if any.
76444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        int index = filename.lastIndexOf('.');
76544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        String format;
76644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        if (index != -1) {
76744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            String name = filename.substring(0, index);
76844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            String extension = filename.substring(index);
76944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            format = name + "-%d" + extension;
77044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        } else {
77144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            format = filename + "-%d";
77244b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        }
77344b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki
77444b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        for (int i = 2; i < Integer.MAX_VALUE; i++) {
77544b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            file = new File(directory, String.format(format, i));
77644b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            if (nfc.createNewFile(file)) {
77744b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki                return file;
77844b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki            }
77944b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        }
78044b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki        return null;
78144b5242edd938450f5e7bc5569852fa5f793da41Makoto Onuki    }
782bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki
783bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki    /**
784bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki     * @return a long in column {@code column} of the first result row, if the query returns at
785bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki     * least 1 row.  Otherwise returns {@code defaultValue}.
786bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki     */
787bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki    public static Long getFirstRowLong(Context context, Uri uri, String[] projection,
788bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki            String selection, String[] selectionArgs, String sortOrder, int column,
789bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki            Long defaultValue) {
790bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki        Cursor c = context.getContentResolver().query(uri, projection, selection, selectionArgs,
791bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki                sortOrder);
792bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki        try {
793bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki            if (c.moveToFirst()) {
794bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki                return c.getLong(column);
795bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki            }
796bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki        } finally {
797bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki            c.close();
798bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki        }
799bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki        return defaultValue;
800bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki    }
801bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki
802bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki    /**
803bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki     * {@link #getFirstRowLong} with null as a default value.
804bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki     */
805bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki    public static Long getFirstRowLong(Context context, Uri uri, String[] projection,
806bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki            String selection, String[] selectionArgs, String sortOrder, int column) {
807bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki        return getFirstRowLong(context, uri, projection, selection, selectionArgs,
808bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki                sortOrder, column, null);
809bcf32320e2600e96c8a9e997a8903bfc3893b35eMakoto Onuki    }
810833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
811833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki    /**
81236bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki     * @return an integer in column {@code column} of the first result row, if the query returns at
81336bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki     * least 1 row.  Otherwise returns {@code defaultValue}.
81436bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki     */
81536bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki    public static Integer getFirstRowInt(Context context, Uri uri, String[] projection,
81636bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki            String selection, String[] selectionArgs, String sortOrder, int column,
81736bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki            Integer defaultValue) {
81836bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki        Long longDefault = (defaultValue == null) ? null : defaultValue.longValue();
81936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki        Long result = getFirstRowLong(context, uri, projection, selection, selectionArgs, sortOrder,
82036bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki                column, longDefault);
82136bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki        return (result == null) ? null : result.intValue();
82236bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki    }
82336bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki
82436bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki    /**
82536bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki     * {@link #getFirstRowInt} with null as a default value.
82636bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki     */
82736bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki    public static Integer getFirstRowInt(Context context, Uri uri, String[] projection,
82836bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki            String selection, String[] selectionArgs, String sortOrder, int column) {
82936bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki        return getFirstRowInt(context, uri, projection, selection, selectionArgs,
83036bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki                sortOrder, column, null);
83136bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki    }
83236bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki
83336bdeeb0e17cb6fbf08023eeb3e4f1db58b48aeaMakoto Onuki    /**
834833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki     * A class used to restore ListView state (e.g. scroll position) when changing adapter.
835833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki     *
836833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki     * TODO For some reason it doesn't always work.  Investigate and fix it.
837833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki     */
838833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki    public static class ListStateSaver {
839833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        private final Parcelable mState;
840833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
841833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public ListStateSaver(AbsListView lv) {
842833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            mState = lv.onSaveInstanceState();
843833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        }
844833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki
845833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        public void restore(AbsListView lv) {
846833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki            lv.onRestoreInstanceState(mState);
847833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki        }
848833fe73b99e62ad9cf6e80c782717c7de1ff12e4Makoto Onuki    }
849533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki
850533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki    /**
851533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki     * STOPSHIP Remove this method
852533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki     * Toggle between portrait and landscape.  Developement use only.
853533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki     */
854533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki    public static void changeOrientation(Activity activity) {
855533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki        activity.setRequestedOrientation(
856533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki                (activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
857533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki                ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
858533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki                : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
859533e0f5beb15659832b589c1d68f96e6ef1b7e40Makoto Onuki    }
860f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
861f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki    /**
862f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki     * Class that supports running any operation for each account.
863f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki     */
864f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki    public abstract static class ForEachAccount extends AsyncTask<Void, Void, Long[]> {
865f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        private final Context mContext;
866f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
867f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        public ForEachAccount(Context context) {
868f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            mContext = context;
869f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        }
870f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
871f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        @Override
872f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        protected final Long[] doInBackground(Void... params) {
873f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            ArrayList<Long> ids = new ArrayList<Long>();
874f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            Cursor c = mContext.getContentResolver().query(EmailContent.Account.CONTENT_URI,
875f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                    EmailContent.Account.ID_PROJECTION, null, null, null);
876f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            try {
877f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                while (c.moveToNext()) {
878f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                    ids.add(c.getLong(EmailContent.Account.ID_PROJECTION_COLUMN));
879f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                }
880f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            } finally {
881f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                c.close();
882f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            }
883f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            return ids.toArray(EMPTY_LONGS);
884f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        }
885f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
886f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        @Override
887f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        protected final void onPostExecute(Long[] ids) {
888f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            if (ids != null && !isCancelled()) {
889f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                for (long id : ids) {
890f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                    performAction(id);
891f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki                }
892f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            }
893f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki            onFinished();
894f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        }
895f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
896f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        /**
897f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki         * This method will be called for each account.
898f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki         */
899f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        protected abstract void performAction(long accountId);
900f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki
901f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        /**
902f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki         * Called when the iteration is finished.
903f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki         */
904f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        protected void onFinished() {
905f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki        }
906f52afae9424fe41071cc34a8d6cbcb82b992a411Makoto Onuki    }
90796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
908