Utility.java revision 128fb393e8ed613c2ce283c0bd51684af2ba444d
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
29e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.content.ContentResolver;
30f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.Context;
31f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.content.res.TypedArray;
32e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadlerimport android.database.Cursor;
33f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.graphics.drawable.Drawable;
3459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onukiimport android.os.AsyncTask;
35d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.security.MessageDigest;
36d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.telephony.TelephonyManager;
37f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.text.Editable;
38128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport android.text.TextUtils;
396cec1104fe8863fce2ee86ff5145076e6c436a00Doug Zongkerimport android.util.Base64;
40d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport android.util.Log;
41f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongkerimport android.widget.TextView;
42e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler
4396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
4496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStreamReader;
4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.UnsupportedEncodingException;
4720225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.ByteBuffer;
4820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.CharBuffer;
4920225d57609d6a5e482c088fdad60c29212d31a0Makoto Onukiimport java.nio.charset.Charset;
50d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onukiimport java.security.NoSuchAlgorithmException;
5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.Date;
52989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.GregorianCalendar;
53989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Predaimport java.util.TimeZone;
54128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onukiimport java.util.regex.Pattern;
5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
5696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class Utility {
5720225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki    public static final Charset UTF_8 = Charset.forName("UTF-8");
5820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki
59128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    // "GMT" + "+" or "-" + 4 digits
60128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE =
61128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki            Pattern.compile("GMT([-+]\\d{4})$");
62128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki
6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public final static String readInputStream(InputStream in, String encoding) throws IOException {
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        InputStreamReader reader = new InputStreamReader(in, encoding);
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        StringBuffer sb = new StringBuffer();
6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int count;
6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        char[] buf = new char[512];
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        while ((count = reader.read(buf)) != -1) {
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            sb.append(buf, 0, count);
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return sb.toString();
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public final static boolean arrayContains(Object[] a, Object o) {
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0, count = a.length; i < count; i++) {
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (a[i].equals(o)) {
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return true;
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return false;
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Combines the given array of Objects into a single string using the
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * seperator character and each Object's toString() method. between each
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * part.
8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *
8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param parts
8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param seperator
9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String combine(Object[] parts, char seperator) {
9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (parts == null) {
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        StringBuffer sb = new StringBuffer();
9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0; i < parts.length; i++) {
9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            sb.append(parts[i].toString());
9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (i < parts.length - 1) {
10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                sb.append(seperator);
10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return sb.toString();
10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String base64Decode(String encoded) {
10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (encoded == null) {
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
109f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker        byte[] decoded = Base64.decode(encoded, Base64.DEFAULT);
11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new String(decoded);
11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String base64Encode(String s) {
11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (s == null) {
11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return s;
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
117f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker        return Base64.encodeToString(s.getBytes(), Base64.NO_WRAP);
11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static boolean requiredFieldValid(TextView view) {
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return view.getText() != null && view.getText().length() > 0;
12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static boolean requiredFieldValid(Editable s) {
12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return s != null && s.length() > 0;
12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Ensures that the given string starts and ends with the double quote character. The string is not modified in any way except to add the
13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * double quote character to start and end if it's not already there.
131f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * TODO: Rename this, because "quoteString()" can mean so many different things.
133f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * sample -> "sample"
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * "sample" -> "sample"
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * ""sample"" -> "sample"
13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * "sample"" -> "sample"
13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * sa"mp"le -> "sa"mp"le"
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * "sa"mp"le" -> "sa"mp"le"
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * (empty string) -> ""
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * " -> ""
14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param s
14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return
14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String quoteString(String s) {
14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (s == null) {
14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
14896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (!s.matches("^\".*\"$")) {
15096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return "\"" + s + "\"";
15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        else {
15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return s;
15496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
15596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
156f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
158f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     * Apply quoting rules per IMAP RFC,
15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * quoted          = DQUOTE *QUOTED-CHAR DQUOTE
16096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
16196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * quoted-specials = DQUOTE / "\"
162f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is used primarily for IMAP login, but might be useful elsewhere.
164f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * NOTE:  Not very efficient - you may wish to preflight this, or perhaps it should check
16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * for trouble chars before calling the replace functions.
167f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker     *
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param s The string to be quoted.
16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return A copy of the string, having undergone quoting as described above
17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String imapQuoted(String s) {
172f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // First, quote any backslashes by replacing \ with \\
17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // regex Pattern:  \\    (Java string const = \\\\)
17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // Substitute:     \\\\  (Java string const = \\\\\\\\)
17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String result = s.replaceAll("\\\\", "\\\\\\\\");
177f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // Then, quote any double-quotes by replacing " with \"
17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // regex Pattern:  "    (Java string const = \")
18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // Substitute:     \\"  (Java string const = \\\\\")
18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        result = result.replaceAll("\"", "\\\\\"");
182f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // return string with quotes around it
18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return "\"" + result + "\"";
18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
186f44a40cda14a49d5b02636a3a8a8b2eb8c23fc00Doug Zongker
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * A fast version of  URLDecoder.decode() that works only with UTF-8 and does only two
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * allocations. This version is around 3x as fast as the standard one and I'm using it
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * hundreds of times in places that slow down the UI, so it helps.
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static String fastUrlDecode(String s) {
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        try {
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            byte[] bytes = s.getBytes("UTF-8");
19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            byte ch;
19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int length = 0;
19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (int i = 0, count = bytes.length; i < count; i++) {
19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                ch = bytes[i];
19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (ch == '%') {
20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    int h = (bytes[i + 1] - '0');
20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    int l = (bytes[i + 2] - '0');
20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (h > 9) {
20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        h -= 7;
20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (l > 9) {
20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        l -= 7;
20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    bytes[length] = (byte) ((h << 4) | l);
20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    i += 2;
21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                else if (ch == '+') {
21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    bytes[length] = ' ';
21396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                else {
21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    bytes[length] = bytes[i];
21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                length++;
21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return new String(bytes, 0, length, "UTF-8");
22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        catch (UnsupportedEncodingException uee) {
22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Returns true if the specified date is within today. Returns false otherwise.
22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param date
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return
23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static boolean isDateToday(Date date) {
23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // TODO But Calendar is so slowwwwwww....
23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Date today = new Date();
23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (date.getYear() == today.getYear() &&
23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                date.getMonth() == today.getMonth() &&
23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                date.getDate() == today.getDate()) {
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return true;
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return false;
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /*
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * TODO disabled this method globally. It is used in all the settings screens but I just
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * noticed that an unrelated icon was dimmed. Android must share drawables internally.
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static void setCompoundDrawablesAlpha(TextView view, int alpha) {
24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        Drawable[] drawables = view.getCompoundDrawables();
24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        for (Drawable drawable : drawables) {
24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//            if (drawable != null) {
25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//                drawable.setAlpha(alpha);
25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//            }
25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        }
25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
254e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler
255e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler    // TODO: unit test this
256e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler    public static String buildMailboxIdSelection(ContentResolver resolver, long mailboxId) {
257e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        // Setup default selection & args, then add to it as necessary
258e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        StringBuilder selection = new StringBuilder(
2596c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                MessageColumns.FLAG_LOADED + " IN ("
2606c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                + Message.FLAG_LOADED_PARTIAL + "," + Message.FLAG_LOADED_COMPLETE
2616c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                + ") AND ");
262e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        if (mailboxId == Mailbox.QUERY_ALL_INBOXES
263e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            || mailboxId == Mailbox.QUERY_ALL_DRAFTS
264e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            || mailboxId == Mailbox.QUERY_ALL_OUTBOX) {
265e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            // query for all mailboxes of type INBOX, DRAFTS, or OUTBOX
266e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            int type;
267e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            if (mailboxId == Mailbox.QUERY_ALL_INBOXES) {
268e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                type = Mailbox.TYPE_INBOX;
269e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            } else if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) {
270e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                type = Mailbox.TYPE_DRAFTS;
271e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            } else {
272e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                type = Mailbox.TYPE_OUTBOX;
273e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            }
274e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            StringBuilder inboxes = new StringBuilder();
275e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            Cursor c = resolver.query(Mailbox.CONTENT_URI,
276e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                        EmailContent.ID_PROJECTION,
277e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                        MailboxColumns.TYPE + "=? AND " + MailboxColumns.FLAG_VISIBLE + "=1",
278e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                        new String[] { Integer.toString(type) }, null);
279e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            // build an IN (mailboxId, ...) list
280e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            // TODO do this directly in the provider
281e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            while (c.moveToNext()) {
282e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                if (inboxes.length() != 0) {
283e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                    inboxes.append(",");
284e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                }
285e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler                inboxes.append(c.getLong(EmailContent.ID_PROJECTION_COLUMN));
286e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            }
287e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            c.close();
288e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(MessageColumns.MAILBOX_KEY + " IN ");
289e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append("(").append(inboxes).append(")");
290e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        } else  if (mailboxId == Mailbox.QUERY_ALL_UNREAD) {
291e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(Message.FLAG_READ + "=0");
292e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        } else if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) {
293e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(Message.FLAG_FAVORITE + "=1");
294e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        } else {
295e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler            selection.append(MessageColumns.MAILBOX_KEY + "=" + mailboxId);
296e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        }
297e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler        return selection.toString();
298e1f0b0a9bcc78ade7f3b2ac540705701f19cd6cdAndrew Stadler    }
299dadba9949895696108b31124fc0c6aa1a297ab1csatok
300dadba9949895696108b31124fc0c6aa1a297ab1csatok    public static class FolderProperties {
301dadba9949895696108b31124fc0c6aa1a297ab1csatok
302dadba9949895696108b31124fc0c6aa1a297ab1csatok        private static FolderProperties sInstance;
303dadba9949895696108b31124fc0c6aa1a297ab1csatok
304dadba9949895696108b31124fc0c6aa1a297ab1csatok        // Caches for frequently accessed resources.
305dadba9949895696108b31124fc0c6aa1a297ab1csatok        private String[] mSpecialMailbox = new String[] {};
306dadba9949895696108b31124fc0c6aa1a297ab1csatok        private TypedArray mSpecialMailboxDrawable;
307dadba9949895696108b31124fc0c6aa1a297ab1csatok        private Drawable mDefaultMailboxDrawable;
3083786cab2aa1776e92fc67af3931a76cd8b848b48satok        private Drawable mSummaryStarredMailboxDrawable;
3093786cab2aa1776e92fc67af3931a76cd8b848b48satok        private Drawable mSummaryCombinedInboxDrawable;
310dadba9949895696108b31124fc0c6aa1a297ab1csatok
311dadba9949895696108b31124fc0c6aa1a297ab1csatok        private FolderProperties(Context context) {
312fa52e6c95674aef6461a5cfc670a052e1c5b7f2fAndrew Stadler            mSpecialMailbox = context.getResources().getStringArray(R.array.mailbox_display_names);
313dadba9949895696108b31124fc0c6aa1a297ab1csatok            for (int i = 0; i < mSpecialMailbox.length; ++i) {
314dadba9949895696108b31124fc0c6aa1a297ab1csatok                if ("".equals(mSpecialMailbox[i])) {
315dadba9949895696108b31124fc0c6aa1a297ab1csatok                    // there is no localized name, so use the display name from the server
316dadba9949895696108b31124fc0c6aa1a297ab1csatok                    mSpecialMailbox[i] = null;
317dadba9949895696108b31124fc0c6aa1a297ab1csatok                }
318dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
319dadba9949895696108b31124fc0c6aa1a297ab1csatok            mSpecialMailboxDrawable =
320fa52e6c95674aef6461a5cfc670a052e1c5b7f2fAndrew Stadler                context.getResources().obtainTypedArray(R.array.mailbox_display_icons);
321dadba9949895696108b31124fc0c6aa1a297ab1csatok            mDefaultMailboxDrawable =
322dadba9949895696108b31124fc0c6aa1a297ab1csatok                context.getResources().getDrawable(R.drawable.ic_list_folder);
3233786cab2aa1776e92fc67af3931a76cd8b848b48satok            mSummaryStarredMailboxDrawable =
3243786cab2aa1776e92fc67af3931a76cd8b848b48satok                context.getResources().getDrawable(R.drawable.ic_list_starred);
3253786cab2aa1776e92fc67af3931a76cd8b848b48satok            mSummaryCombinedInboxDrawable =
3263786cab2aa1776e92fc67af3931a76cd8b848b48satok                context.getResources().getDrawable(R.drawable.ic_list_combined_inbox);
327dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
328dadba9949895696108b31124fc0c6aa1a297ab1csatok
3291f2caa80957e92519258e212b5fd45fb6c168a73Makoto Onuki        public static synchronized FolderProperties getInstance(Context context) {
330dadba9949895696108b31124fc0c6aa1a297ab1csatok            if (sInstance == null) {
3311f2caa80957e92519258e212b5fd45fb6c168a73Makoto Onuki                sInstance = new FolderProperties(context);
332dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
333dadba9949895696108b31124fc0c6aa1a297ab1csatok            return sInstance;
334dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
335dadba9949895696108b31124fc0c6aa1a297ab1csatok
336dadba9949895696108b31124fc0c6aa1a297ab1csatok        /**
337dadba9949895696108b31124fc0c6aa1a297ab1csatok         * Lookup names of localized special mailboxes
338dadba9949895696108b31124fc0c6aa1a297ab1csatok         * @param type
339dadba9949895696108b31124fc0c6aa1a297ab1csatok         * @return Localized strings
340dadba9949895696108b31124fc0c6aa1a297ab1csatok         */
341dadba9949895696108b31124fc0c6aa1a297ab1csatok        public String getDisplayName(int type) {
342dadba9949895696108b31124fc0c6aa1a297ab1csatok            if (type < mSpecialMailbox.length) {
343dadba9949895696108b31124fc0c6aa1a297ab1csatok                return mSpecialMailbox[type];
344dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
345dadba9949895696108b31124fc0c6aa1a297ab1csatok            return null;
346dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
347dadba9949895696108b31124fc0c6aa1a297ab1csatok
348dadba9949895696108b31124fc0c6aa1a297ab1csatok        /**
349dadba9949895696108b31124fc0c6aa1a297ab1csatok         * Lookup icons of special mailboxes
350dadba9949895696108b31124fc0c6aa1a297ab1csatok         * @param type
351dadba9949895696108b31124fc0c6aa1a297ab1csatok         * @return icon's drawable
352dadba9949895696108b31124fc0c6aa1a297ab1csatok         */
3534bb52e36b18ab5e2fb9242069611bc0ef6116c09satok        public Drawable getIconIds(int type) {
354dadba9949895696108b31124fc0c6aa1a297ab1csatok            if (type < mSpecialMailboxDrawable.length()) {
355dadba9949895696108b31124fc0c6aa1a297ab1csatok                return mSpecialMailboxDrawable.getDrawable(type);
356dadba9949895696108b31124fc0c6aa1a297ab1csatok            }
357dadba9949895696108b31124fc0c6aa1a297ab1csatok            return mDefaultMailboxDrawable;
358dadba9949895696108b31124fc0c6aa1a297ab1csatok        }
3593786cab2aa1776e92fc67af3931a76cd8b848b48satok
3603786cab2aa1776e92fc67af3931a76cd8b848b48satok        public Drawable getSummaryMailboxIconIds(long mailboxKey) {
3613786cab2aa1776e92fc67af3931a76cd8b848b48satok            if (mailboxKey == Mailbox.QUERY_ALL_INBOXES) {
3623786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSummaryCombinedInboxDrawable;
3633786cab2aa1776e92fc67af3931a76cd8b848b48satok            } else if (mailboxKey == Mailbox.QUERY_ALL_FAVORITES) {
3643786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSummaryStarredMailboxDrawable;
3653786cab2aa1776e92fc67af3931a76cd8b848b48satok            } else if (mailboxKey == Mailbox.QUERY_ALL_DRAFTS) {
3663786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSpecialMailboxDrawable.getDrawable(Mailbox.TYPE_DRAFTS);
3673786cab2aa1776e92fc67af3931a76cd8b848b48satok            } else if (mailboxKey == Mailbox.QUERY_ALL_OUTBOX) {
3683786cab2aa1776e92fc67af3931a76cd8b848b48satok                return mSpecialMailboxDrawable.getDrawable(Mailbox.TYPE_OUTBOX);
3693786cab2aa1776e92fc67af3931a76cd8b848b48satok            }
3703786cab2aa1776e92fc67af3931a76cd8b848b48satok            return mDefaultMailboxDrawable;
3713786cab2aa1776e92fc67af3931a76cd8b848b48satok        }
372dadba9949895696108b31124fc0c6aa1a297ab1csatok    }
373da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler
374da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    private final static String HOSTAUTH_WHERE_CREDENTIALS = HostAuthColumns.ADDRESS + " like ?"
375da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            + " and " + HostAuthColumns.LOGIN + " like ?"
376da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            + " and " + HostAuthColumns.PROTOCOL + " not like \"smtp\"";
377da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    private final static String ACCOUNT_WHERE_HOSTAUTH = AccountColumns.HOST_AUTH_KEY_RECV + "=?";
378da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler
379da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    /**
380da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * Look for an existing account with the same username & server
381da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     *
382da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @param context a system context
383da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @param allowAccountId this account Id will not trigger (when editing an existing account)
384da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @param hostName the server
385da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @param userLogin the user login string
386da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     * @result null = no dupes found.  non-null = dupe account's display name
387da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler     */
388da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    public static String findDuplicateAccount(Context context, long allowAccountId, String hostName,
389da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            String userLogin) {
390da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        ContentResolver resolver = context.getContentResolver();
391da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        Cursor c = resolver.query(HostAuth.CONTENT_URI, HostAuth.ID_PROJECTION,
392da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                HOSTAUTH_WHERE_CREDENTIALS, new String[] { hostName, userLogin }, null);
393da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        try {
394da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            while (c.moveToNext()) {
395da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                long hostAuthId = c.getLong(HostAuth.ID_PROJECTION_COLUMN);
396da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                // Find account with matching hostauthrecv key, and return its display name
397da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                Cursor c2 = resolver.query(Account.CONTENT_URI, Account.ID_PROJECTION,
398da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        ACCOUNT_WHERE_HOSTAUTH, new String[] { Long.toString(hostAuthId) }, null);
399da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                try {
400da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                    while (c2.moveToNext()) {
401da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        long accountId = c2.getLong(Account.ID_PROJECTION_COLUMN);
402da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        if (accountId != allowAccountId) {
403da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                            Account account = Account.restoreAccountWithId(context, accountId);
404da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                            if (account != null) {
405da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                                return account.mDisplayName;
406da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                            }
407da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                        }
408da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                    }
409da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                } finally {
410da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                    c2.close();
411da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler                }
412da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            }
413da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        } finally {
414da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler            c.close();
415da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        }
416da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler
417da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler        return null;
418da8836a76cd8a6eaa7e3693eeacc6393870b2066Andrew Stadler    }
419eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler
420eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler    /**
421eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler     * Generate a random message-id header for locally-generated messages.
422eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler     */
423eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler    public static String generateMessageId() {
424eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        StringBuffer sb = new StringBuffer();
425eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append("<");
426eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        for (int i = 0; i < 24; i++) {
427eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler            sb.append(Integer.toString((int)(Math.random() * 35), 36));
428eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        }
429eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append(".");
430eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append(Long.toString(System.currentTimeMillis()));
431eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        sb.append("@email.android.com>");
432eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler        return sb.toString();
433eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler    }
434eb7752bf695b2a93854e0bb89ddbbc2236bb9aeaAndrew Stadler
435989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    /**
436989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * Generate a time in milliseconds from a date string that represents a date/time in GMT
437989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @param DateTime date string in format 20090211T180303Z (rfc2445, iCalendar).
438989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @return the time in milliseconds (since Jan 1, 1970)
439989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     */
440989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    public static long parseDateTimeToMillis(String date) {
441989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        GregorianCalendar cal = parseDateTimeToCalendar(date);
442989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        return cal.getTimeInMillis();
443989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    }
444989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda
445989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    /**
446989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * Generate a GregorianCalendar from a date string that represents a date/time in GMT
447989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @param DateTime date string in format 20090211T180303Z (rfc2445, iCalendar).
448989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @return the GregorianCalendar
449989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     */
450989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    public static GregorianCalendar parseDateTimeToCalendar(String date) {
451989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
452989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)),
453989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(9, 11)), Integer.parseInt(date.substring(11, 13)),
454989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(13, 15)));
455989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
456989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        return cal;
457989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    }
458989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda
459989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    /**
460989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * Generate a time in milliseconds from an email date string that represents a date/time in GMT
461989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @param Email style DateTime string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339)
462989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     * @return the time in milliseconds (since Jan 1, 1970)
463989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda     */
464989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    public static long parseEmailDateTimeToMillis(String date) {
465989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
466989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(5, 7)) - 1, Integer.parseInt(date.substring(8, 10)),
467989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(11, 13)), Integer.parseInt(date.substring(14, 16)),
468989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda                Integer.parseInt(date.substring(17, 19)));
469989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
470989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda        return cal.getTimeInMillis();
471989552c10744e2d7f8fca1bdb2baef5273a8a0b9Mihai Preda    }
47220225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki
47320225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki    /** Converts a String to UTF-8 */
47420225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki    public static byte[] toUtf8(String s) {
47520225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        if (s == null) {
47620225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki            return null;
47720225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        }
47820225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        final ByteBuffer buffer = UTF_8.encode(CharBuffer.wrap(s));
47920225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        final byte[] bytes = new byte[buffer.limit()];
48020225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        buffer.get(bytes);
48120225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki        return bytes;
48220225d57609d6a5e482c088fdad60c29212d31a0Makoto Onuki    }
48388a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki
484fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki    /** Build a String from UTF-8 bytes */
485fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki    public static String fromUtf8(byte[] b) {
486fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        if (b == null) {
487fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki            return null;
488fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        }
489fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        final CharBuffer cb = Utility.UTF_8.decode(ByteBuffer.wrap(b));
490fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki        return new String(cb.array(), 0, cb.length());
491fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki    }
492fe61f358ab67cac2aa454a6dd3ea6bbf876e343cMakoto Onuki
49388a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki    /**
49488a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki     * @return true if the input is the first (or only) byte in a UTF-8 character
49588a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki     */
49688a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki    public static boolean isFirstUtf8Byte(byte b) {
49788a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki        // If the top 2 bits is '10', it's not a first byte.
49888a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki        return (b & 0xc0) != 0x80;
49988a94bca1922615564e70a27bb6ae72bca487c75Makoto Onuki    }
500dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
501dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    public static String byteToHex(int b) {
502dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        return byteToHex(new StringBuilder(), b).toString();
503dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    }
504dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
505dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    public static StringBuilder byteToHex(StringBuilder sb, int b) {
506dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        b &= 0xFF;
507dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        sb.append("0123456789ABCDEF".charAt(b >> 4));
508dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        sb.append("0123456789ABCDEF".charAt(b & 0xF));
509dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        return sb;
510dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki    }
511eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank
512eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank    public static String replaceBareLfWithCrlf(String str) {
513eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank        return str.replace("\r", "").replace("\n", "\r\n");
514eba33f8b5a0cc4a28ae5a9d6632df475c4b0a794Marc Blank    }
51559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki
51659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    /**
51759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     * Cancel an {@link AsyncTask}.  If it's already running, it'll be interrupted.
51859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     */
51959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    public static void cancelTaskInterrupt(AsyncTask<?, ?, ?> task) {
52059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki        cancelTask(task, true);
52159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    }
52259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki
52359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    /**
52459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     * Cancel an {@link AsyncTask}.
52559cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     *
52659cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
52759cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     *        task should be interrupted; otherwise, in-progress tasks are allowed
52859cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     *        to complete.
52959cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki     */
53059cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) {
53159cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki        if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
53259cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki            task.cancel(mayInterruptIfRunning);
53359cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki        }
53459cf1d05c111e3b5fb18417db41ce47b623b5b1eMakoto Onuki    }
535d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki
536d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    /**
537d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     * @return Device's unique ID if available.  null if the device has no unique ID.
538d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     */
539d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    public static String getConsistentDeviceId(Context context) {
540d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final String deviceId;
541d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        try {
542d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            TelephonyManager tm =
543d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                    (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
544d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            if (tm == null) {
545d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                return null;
546d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            }
547d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            deviceId = tm.getDeviceId();
548d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            if (deviceId == null) {
549d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                return null;
550d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            }
551d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        } catch (Exception e) {
552d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            Log.d(Email.LOG_TAG, "Error in TelephonyManager.getDeviceId(): " + e.getMessage());
553d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            return null;
554d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        }
555d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final MessageDigest sha;
556d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        try {
557d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            sha = MessageDigest.getInstance("SHA-1");
558d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        } catch (NoSuchAlgorithmException impossible) {
559d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki            return null;
560d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        }
561d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        sha.update(Utility.toUtf8(deviceId));
562d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final int hash = getSmallHashFromSha1(sha.digest());
563d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        return Integer.toString(hash);
564d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    }
565d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki
566d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    /**
567d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     * @return a non-negative integer generated from 20 byte SHA-1 hash.
568d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki     */
569d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    /* package for testing */ static int getSmallHashFromSha1(byte[] sha1) {
570d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        final int offset = sha1[19] & 0xf; // SHA1 is 20 bytes.
571d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki        return ((sha1[offset]  & 0x7f) << 24)
572d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                | ((sha1[offset + 1] & 0xff) << 16)
573d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                | ((sha1[offset + 2] & 0xff) << 8)
574d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki                | ((sha1[offset + 3] & 0xff));
575d2a0d23380a2751d82f9d1f955a812f94a301e2aMakoto Onuki    }
576128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki
577128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    /**
578128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     * Try to make a date MIME(RFC 2822/5322)-compliant.
579128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     *
580128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     * It fixes:
581128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700"
582128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     *   (4 digit zone value can't be preceded by "GMT")
583128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     *   We got a report saying eBay sends a date in this format
584128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki     */
585128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    public static String cleanUpMimeDate(String date) {
586128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        if (TextUtils.isEmpty(date)) {
587128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki            return date;
588128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        }
589128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1");
590128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki        return date;
591128fb393e8ed613c2ce283c0bd51684af2ba444dMakoto Onuki    }
59296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
593