13f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen/* 23f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Copyright (C) 2015 The Android Open Source Project 33f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 43f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Licensed under the Apache License, Version 2.0 (the "License"); 53f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * you may not use this file except in compliance with the License. 63f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * You may obtain a copy of the License at 73f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 83f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * http://www.apache.org/licenses/LICENSE-2.0 93f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Unless required by applicable law or agreed to in writing, software 113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * distributed under the License is distributed on an "AS IS" BASIS, 123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * See the License for the specific language governing permissions and 143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * limitations under the License. 153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenpackage com.android.phone.common.mail; 173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.os.Parcel; 193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.os.Parcelable; 203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.Html; 213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.TextUtils; 223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.util.Rfc822Token; 233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.util.Rfc822Tokenizer; 243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.internal.annotations.VisibleForTesting; 263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.utils.LogUtils; 273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.codec.EncoderUtil; 293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.decoder.DecoderUtil; 303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.ArrayList; 323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.regex.Pattern; 333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen/** 353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This class represent email address. 363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * RFC822 email address may have following format. 383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * "name" <address> (comment) 393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * "name" <address> 403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * name <address> 413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * address 423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Name and comment part should be MIME/base64 encoded in header if necessary. 433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenpublic class Address implements Parcelable { 463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static final String ADDRESS_DELIMETER = ","; 473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Address part, in the form local_part@domain_part. No surrounding angle brackets. 493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private String mAddress; 513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Name part. No surrounding double quote, and no MIME/base64 encoding. 543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This must be null if Address has no name part. 553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private String mPersonal; 573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * When personal is set, it will return the first token of the personal 603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * string. Otherwise, it will return the e-mail address up to the '@' sign. 613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private String mSimplifiedName; 633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // Regex that matches address surrounded by '<>' optionally. '^<?([^>]+)>?$' 653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final Pattern REMOVE_OPTIONAL_BRACKET = Pattern.compile("^<?([^>]+)>?$"); 663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // Regex that matches personal name surrounded by '""' optionally. '^"?([^"]+)"?$' 673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final Pattern REMOVE_OPTIONAL_DQUOTE = Pattern.compile("^\"?([^\"]*)\"?$"); 683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // Regex that matches escaped character '\\([\\"])' 693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final Pattern UNQUOTE = Pattern.compile("\\\\([\\\\\"])"); 703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // TODO: LOCAL_PART and DOMAIN_PART_PART are too permissive and can be improved. 723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // TODO: Fix this to better constrain comments. 733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** Regex for the local part of an email address. */ 743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final String LOCAL_PART = "[^@]+"; 753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** Regex for each part of the domain part, i.e. the thing between the dots. */ 763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final String DOMAIN_PART_PART = "[[\\w][\\d]\\-\\(\\)\\[\\]]+"; 773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** Regex for the domain part, which is two or more {@link #DOMAIN_PART_PART} separated by . */ 783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final String DOMAIN_PART = 793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen "(" + DOMAIN_PART_PART + "\\.)+" + DOMAIN_PART_PART; 803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** Pattern to check if an email address is valid. */ 823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final Pattern EMAIL_ADDRESS = 833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Pattern.compile("\\A" + LOCAL_PART + "@" + DOMAIN_PART + "\\z"); 843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0]; 863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // delimiters are chars that do not appear in an email address, used by fromHeader 883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final char LIST_DELIMITER_EMAIL = '\1'; 893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final char LIST_DELIMITER_PERSONAL = '\2'; 903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static final String LOG_TAG = "Email Address"; 923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public Address(String address) { 953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen setAddress(address); 963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public Address(String address, String personal) { 993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen setPersonal(personal); 1003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen setAddress(address); 1013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 1043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Returns a simplified string for this e-mail address. 1053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * When a name is known, it will return the first token of that name. Otherwise, it will 1063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * return the e-mail address up to the '@' sign. 1073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 1083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public String getSimplifiedName() { 1093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (mSimplifiedName == null) { 1103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (TextUtils.isEmpty(mPersonal) && !TextUtils.isEmpty(mAddress)) { 1113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen int atSign = mAddress.indexOf('@'); 1123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen mSimplifiedName = (atSign != -1) ? mAddress.substring(0, atSign) : ""; 1133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else if (!TextUtils.isEmpty(mPersonal)) { 1143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // TODO: use Contacts' NameSplitter for more reliable first-name extraction 1163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen int end = mPersonal.indexOf(' '); 1183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen while (end > 0 && mPersonal.charAt(end - 1) == ',') { 1193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen end--; 1203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen mSimplifiedName = (end < 1) ? mPersonal : mPersonal.substring(0, end); 1223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 1243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen LogUtils.w(LOG_TAG, "Unable to get a simplified name"); 1253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen mSimplifiedName = ""; 1263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mSimplifiedName; 1293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static synchronized Address getEmailAddress(String rawAddress) { 1323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (TextUtils.isEmpty(rawAddress)) { 1333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return null; 1343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen String name, address; 1363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(rawAddress); 1373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (tokens.length > 0) { 1383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen final String tokenizedName = tokens[0].getName(); 1393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen name = tokenizedName != null ? Html.fromHtml(tokenizedName.trim()).toString() 1403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen : ""; 1413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen address = Html.fromHtml(tokens[0].getAddress()).toString(); 1423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 1433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen name = ""; 1443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen address = rawAddress == null ? 1453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen "" : Html.fromHtml(rawAddress).toString(); 1463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return new Address(address, name); 1483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public String getAddress() { 1513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mAddress; 1523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public void setAddress(String address) { 1553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen mAddress = REMOVE_OPTIONAL_BRACKET.matcher(address).replaceAll("$1"); 1563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 1593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get name part as UTF-16 string. No surrounding double quote, and no MIME/base64 encoding. 1603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 1613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return Name part of email address. Returns null if it is omitted. 1623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 1633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public String getPersonal() { 1643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mPersonal; 1653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 1683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Set personal part from UTF-16 string. Optional surrounding double quote will be removed. 1693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * It will be also unquoted and MIME/base64 decoded. 1703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 1713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param personal name part of email address as UTF-16 string. Null is acceptable. 1723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 1733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public void setPersonal(String personal) { 1743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen mPersonal = decodeAddressPersonal(personal); 1753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 1783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Decodes name from UTF-16 string. Optional surrounding double quote will be removed. 1793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * It will be also unquoted and MIME/base64 decoded. 1803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 1813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param personal name part of email address as UTF-16 string. Null is acceptable. 1823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 1833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String decodeAddressPersonal(String personal) { 1843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (personal != null) { 1853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen personal = REMOVE_OPTIONAL_DQUOTE.matcher(personal).replaceAll("$1"); 1863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen personal = UNQUOTE.matcher(personal).replaceAll("$1"); 1873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen personal = DecoderUtil.decodeEncodedWords(personal); 1883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (personal.length() == 0) { 1893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen personal = null; 1903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return personal; 1933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 1943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 1953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 1963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This method is used to check that all the addresses that the user 1973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * entered in a list (e.g. To:) are valid, so that none is dropped. 1983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 1993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 2003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static boolean isAllValid(String addressList) { 2013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // This code mimics the parse() method below. 2023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // I don't know how to better avoid the code-duplication. 2033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addressList != null && addressList.length() > 0) { 2043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList); 2053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen for (int i = 0, length = tokens.length; i < length; ++i) { 2063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Rfc822Token token = tokens[i]; 2073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen String address = token.getAddress(); 2083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (!TextUtils.isEmpty(address) && !isValidAddress(address)) { 2093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return false; 2103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return true; 2143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 2163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 2173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Parse a comma-delimited list of addresses in RFC822 format and return an 2183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * array of Address objects. 2193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 2203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param addressList Address list in comma-delimited string. 2213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return An array of 0 or more Addresses. 2223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 2233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static Address[] parse(String addressList) { 2243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addressList == null || addressList.length() == 0) { 2253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return EMPTY_ADDRESS_ARRAY; 2263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressList); 2283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen ArrayList<Address> addresses = new ArrayList<Address>(); 2293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen for (int i = 0, length = tokens.length; i < length; ++i) { 2303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Rfc822Token token = tokens[i]; 2313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen String address = token.getAddress(); 2323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (!TextUtils.isEmpty(address)) { 2333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (isValidAddress(address)) { 2343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen String name = token.getName(); 2353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (TextUtils.isEmpty(name)) { 2363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen name = null; 2373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen addresses.add(new Address(address, name)); 2393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return addresses.toArray(new Address[addresses.size()]); 2433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 2453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 2463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Checks whether a string email address is valid. 2473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * E.g. name@domain.com is valid. 2483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 2493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 2503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen static boolean isValidAddress(final String address) { 2513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return EMAIL_ADDRESS.matcher(address).find(); 2523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 2543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 2553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public boolean equals(Object o) { 2563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (o instanceof Address) { 2573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // It seems that the spec says that the "user" part is case-sensitive, 2583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // while the domain part in case-insesitive. 2593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // So foo@yahoo.com and Foo@yahoo.com are different. 2603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // This may seem non-intuitive from the user POV, so we 2613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // may re-consider it if it creates UI trouble. 2623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // A problem case is "replyAll" sending to both 2633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // a@b.c and to A@b.c, which turn out to be the same on the server. 2643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // Leave unchanged for now (i.e. case-sensitive). 2653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return getAddress().equals(((Address) o).getAddress()); 2663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return super.equals(o); 2683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 2703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 2713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public int hashCode() { 2723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return getAddress().hashCode(); 2733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 2753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 2763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get human readable address string. 2773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Do not use this for email header. 2783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 2793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return Human readable address string. Not quoted and not encoded. 2803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 2813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 2823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public String toString() { 2833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (mPersonal != null && !mPersonal.equals(mAddress)) { 2843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (mPersonal.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) { 2853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return ensureQuotedString(mPersonal) + " <" + mAddress + ">"; 2863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 2873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mPersonal + " <" + mAddress + ">"; 2883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 2903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mAddress; 2913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 2933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 2943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 2953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Ensures that the given string starts and ends with the double quote character. The string is 2963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * not modified in any way except to add the double quote character to start and end if it's not 2973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * already there. 2983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 2993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * sample -> "sample" 3003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * "sample" -> "sample" 3013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * ""sample"" -> "sample" 3023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * "sample"" -> "sample" 3033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * sa"mp"le -> "sa"mp"le" 3043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * "sa"mp"le" -> "sa"mp"le" 3053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * (empty string) -> "" 3063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * " -> "" 3073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 3083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen private static String ensureQuotedString(String s) { 3093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (s == null) { 3103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return null; 3113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (!s.matches("^\".*\"$")) { 3133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return "\"" + s + "\""; 3143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 3153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return s; 3163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 3193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 3203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get human readable comma-delimited address string. 3213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 3223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param addresses Address array 3233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return Human readable comma-delimited address string. 3243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 3253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 3263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String toString(Address[] addresses) { 3273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return toString(addresses, ADDRESS_DELIMETER); 3283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 3303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 3313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get human readable address strings joined with the specified separator. 3323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 3333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param addresses Address array 3343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param separator Separator 3353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return Human readable comma-delimited address string. 3363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 3373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String toString(Address[] addresses, String separator) { 3383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addresses == null || addresses.length == 0) { 3393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return null; 3403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addresses.length == 1) { 3423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return addresses[0].toString(); 3433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen StringBuilder sb = new StringBuilder(addresses[0].toString()); 3453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen for (int i = 1; i < addresses.length; i++) { 3463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen sb.append(separator); 3473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // TODO: investigate why this .trim() is needed. 3483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen sb.append(addresses[i].toString().trim()); 3493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return sb.toString(); 3513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 3533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 3543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get RFC822/MIME compatible address string. 3553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 3563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return RFC822/MIME compatible address string. 3573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * It may be surrounded by double quote or quoted and MIME/base64 encoded if necessary. 3583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 3593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public String toHeader() { 3603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (mPersonal != null) { 3613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return EncoderUtil.encodeAddressDisplayName(mPersonal) + " <" + mAddress + ">"; 3623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 3633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mAddress; 3643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 3673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 3683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get RFC822/MIME compatible comma-delimited address string. 3693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 3703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param addresses Address array 3713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return RFC822/MIME compatible comma-delimited address string. 3723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * it may be surrounded by double quoted or quoted and MIME/base64 encoded if necessary. 3733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 3743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String toHeader(Address[] addresses) { 3753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addresses == null || addresses.length == 0) { 3763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return null; 3773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addresses.length == 1) { 3793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return addresses[0].toHeader(); 3803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen StringBuilder sb = new StringBuilder(addresses[0].toHeader()); 3823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen for (int i = 1; i < addresses.length; i++) { 3833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // We need space character to be able to fold line. 3843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen sb.append(", "); 3853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen sb.append(addresses[i].toHeader()); 3863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return sb.toString(); 3883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 3893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 3903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 3913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Get Human friendly address string. 3923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 3933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return the personal part of this Address, or the address part if the 3943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * personal part is not available 3953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 3963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 3973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public String toFriendly() { 3983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (mPersonal != null && mPersonal.length() > 0) { 3993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mPersonal; 4003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 4013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return mAddress; 4023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 4063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Creates a comma-delimited list of addresses in the "friendly" format (see toFriendly() for 4073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * details on the per-address conversion). 4083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 4093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param addresses Array of Address[] values 4103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return A comma-delimited string listing all of the addresses supplied. Null if source 4113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * was null or empty. 4123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 4143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String toFriendly(Address[] addresses) { 4153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addresses == null || addresses.length == 0) { 4163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return null; 4173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addresses.length == 1) { 4193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return addresses[0].toFriendly(); 4203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen StringBuilder sb = new StringBuilder(addresses[0].toFriendly()); 4223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen for (int i = 1; i < addresses.length; i++) { 4233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen sb.append(", "); 4243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen sb.append(addresses[i].toFriendly()); 4253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return sb.toString(); 4273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 4303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Returns exactly the same result as Address.toString(Address.fromHeader(addressList)). 4313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 4333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String fromHeaderToString(String addressList) { 4343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return toString(fromHeader(addressList)); 4353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 4383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Returns exactly the same result as Address.toHeader(Address.parse(addressList)). 4393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 4413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String parseToHeader(String addressList) { 4423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return Address.toHeader(Address.parse(addressList)); 4433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 4463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Returns null if the addressList has 0 addresses, otherwise returns the first address. 4473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * The same as Address.fromHeader(addressList)[0] for non-empty list. 4483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This is an utility method that offers some performance optimization opportunities. 4493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 4513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static Address firstAddress(String addressList) { 4523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Address[] array = fromHeader(addressList); 4533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return array.length > 0 ? array[0] : null; 4543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 4573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This method exists to convert an address list formatted in a deprecated legacy format to the 4583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * standard RFC822 header format. {@link #fromHeader(String)} is capable of reading the legacy 4593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * format and the RFC822 format. {@link #toHeader()} always produces the RFC822 format. 4603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * 4613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * This implementation is brute-force, and could be replaced with a more efficient version 4623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * if desired. 4633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static String reformatToHeader(String addressList) { 4653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return toHeader(fromHeader(addressList)); 4663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /** 4693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @param addressList a CSV of RFC822 addresses or the deprecated legacy string format 4703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * @return array of addresses parsed from <code>addressList</code> 4713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @VisibleForTesting 4733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static Address[] fromHeader(String addressList) { 4743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addressList == null || addressList.length() == 0) { 4753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return EMPTY_ADDRESS_ARRAY; 4763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // IF we're CSV, just parse 4783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if ((addressList.indexOf(LIST_DELIMITER_PERSONAL) == -1) && 4793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen (addressList.indexOf(LIST_DELIMITER_EMAIL) == -1)) { 4803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return Address.parse(addressList); 4813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 4823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // Otherwise, do backward-compatible unpack 4833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen ArrayList<Address> addresses = new ArrayList<Address>(); 4843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen int length = addressList.length(); 4853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen int pairStartIndex = 0; 4863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen int pairEndIndex; 4873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen /* addressEndIndex is only re-scanned (indexOf()) when a LIST_DELIMITER_PERSONAL 4893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen is used, not for every email address; i.e. not for every iteration of the while(). 4903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen This reduces the theoretical complexity from quadratic to linear, 4913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen and provides some speed-up in practice by removing redundant scans of the string. 4923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */ 4933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen int addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL); 4943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 4953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen while (pairStartIndex < length) { 4963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen pairEndIndex = addressList.indexOf(LIST_DELIMITER_EMAIL, pairStartIndex); 4973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (pairEndIndex == -1) { 4983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen pairEndIndex = length; 4993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen Address address; 5013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen if (addressEndIndex == -1 || pairEndIndex <= addressEndIndex) { 5023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // in this case the DELIMITER_PERSONAL is in a future pair, 5033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // so don't use personal, and don't update addressEndIndex 5043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen address = new Address(addressList.substring(pairStartIndex, pairEndIndex), null); 5053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } else { 5063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen address = new Address(addressList.substring(pairStartIndex, addressEndIndex), 5073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen addressList.substring(addressEndIndex + 1, pairEndIndex)); 5083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen // only update addressEndIndex when we use the LIST_DELIMITER_PERSONAL 5093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen addressEndIndex = addressList.indexOf(LIST_DELIMITER_PERSONAL, pairEndIndex + 1); 5103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen addresses.add(address); 5123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen pairStartIndex = pairEndIndex + 1; 5133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return addresses.toArray(new Address[addresses.size()]); 5153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 5173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public static final Creator<Address> CREATOR = new Creator<Address>() { 5183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 5193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public Address createFromParcel(Parcel parcel) { 5203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return new Address(parcel); 5213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 5233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 5243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public Address[] newArray(int size) { 5253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return new Address[size]; 5263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen }; 5283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 5293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public Address(Parcel in) { 5303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen setPersonal(in.readString()); 5313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen setAddress(in.readString()); 5323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 5343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 5353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public int describeContents() { 5363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen return 0; 5373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen 5393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen @Override 5403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen public void writeToParcel(Parcel out, int flags) { 5413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen out.writeString(mPersonal); 5423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen out.writeString(mAddress); 5433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen } 5443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen} 545