diff options
25 files changed, 455 insertions, 243 deletions
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 2d2e75f..3b39ae4 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -102,6 +102,7 @@ public class AccountManagerService private static final String ACCOUNTS_ID = "_id"; private static final String ACCOUNTS_NAME = "name"; private static final String ACCOUNTS_TYPE = "type"; + private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; private static final String ACCOUNTS_PASSWORD = "password"; private static final String TABLE_AUTHTOKENS = "authtokens"; @@ -127,6 +128,8 @@ public class AccountManagerService private static final String[] ACCOUNT_NAME_TYPE_PROJECTION = new String[]{ACCOUNTS_ID, ACCOUNTS_NAME, ACCOUNTS_TYPE}; + private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = + new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; private static final Intent ACCOUNTS_CHANGED_INTENT; private static final String COUNT_OF_MATCHING_GRANTS = "" @@ -1455,18 +1458,54 @@ public class AccountManagerService return asBinder(); } - protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { - synchronized (mSessions) { - final long now = SystemClock.elapsedRealtime(); - fout.println("AccountManagerService: " + mSessions.size() + " sessions"); - for (Session session : mSessions.values()) { - fout.println(" " + session.toDebugString(now)); + /** + * Searches array of arguments for the specified string + * @param args array of argument strings + * @param value value to search for + * @return true if the value is contained in the array + */ + private static boolean scanArgs(String[] args, String value) { + if (args != null) { + for (String arg : args) { + if (value.equals(arg)) { + return true; + } } } + return false; + } - fout.println(); + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); - mAuthenticatorCache.dump(fd, fout, args); + if (isCheckinRequest) { + // This is a checkin request. *Only* upload the account types and the count of each. + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + + Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, + null, null, ACCOUNTS_TYPE, null, null); + try { + while (cursor.moveToNext()) { + // print type,count + fout.println(cursor.getString(0) + "," + cursor.getString(1)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } else { + synchronized (mSessions) { + final long now = SystemClock.elapsedRealtime(); + fout.println("AccountManagerService: " + mSessions.size() + " sessions"); + for (Session session : mSessions.values()) { + fout.println(" " + session.toDebugString(now)); + } + } + + fout.println(); + mAuthenticatorCache.dump(fd, fout, args); + } } private void doNotification(Account account, CharSequence message, Intent intent) { diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java index 35224c7..915f09e 100644 --- a/core/java/android/pim/vcard/ContactStruct.java +++ b/core/java/android/pim/vcard/ContactStruct.java @@ -1051,8 +1051,8 @@ public class ContactStruct { List<String> nameList; switch (VCardConfig.getNameOrderType(mVCardType)) { case VCardConfig.NAME_ORDER_JAPANESE: - if (VCardUtils.containsOnlyAscii(mFamilyName) && - VCardUtils.containsOnlyAscii(mGivenName)) { + if (VCardUtils.containsOnlyPrintableAscii(mFamilyName) && + VCardUtils.containsOnlyPrintableAscii(mGivenName)) { nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix); } else { nameList = Arrays.asList(mPrefix, mFamilyName, mMiddleName, mGivenName, mSuffix); diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 40b4fc4..b0376c4 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -56,8 +56,10 @@ import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * <p> @@ -234,9 +236,8 @@ public class VCardComposer { private static final String VCARD_PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME"; // Android specific properties - private static final String VCARD_PROPERTY_X_PHONETIC_NAME = "X-PHONETIC-NAME"; + // TODO: ues extra MIME-TYPE instead of adding this kind of inflexible fields private static final String VCARD_PROPERTY_X_NICKNAME = "X-NICKNAME"; - // TODO: add properties like X-LATITUDE // Property for call log entry private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME"; @@ -396,7 +397,7 @@ public class VCardComposer { } boolean needCharset = false; - if (!(VCardUtils.containsOnlyAscii(phoneName))) { + if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) { needCharset = true; } appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, phoneName, needCharset, false); @@ -596,7 +597,7 @@ public class VCardComposer { if (TextUtils.isEmpty(name)) { name = mCursor.getString(NUMBER_COLUMN_INDEX); } - final boolean needCharset = !(VCardUtils.containsOnlyAscii(name)); + final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name)); appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, name, needCharset, false); appendVCardLine(builder, VCARD_PROPERTY_NAME, name, needCharset, false); @@ -740,213 +741,220 @@ public class VCardComposer { final Map<String, List<ContentValues>> contentValuesListMap) { final List<ContentValues> contentValuesList = contentValuesListMap .get(StructuredName.CONTENT_ITEM_TYPE); - if (contentValuesList != null) { + if (contentValuesList != null && contentValuesList.size() > 0) { appendStructuredNamesInternal(builder, contentValuesList); } else if (mIsDoCoMo) { appendVCardLine(builder, VCARD_PROPERTY_NAME, ""); + } else if (mIsV30) { + // vCard 3.0 requires "N" and "FN" properties. + appendVCardLine(builder, VCARD_PROPERTY_NAME, ""); + appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, ""); } } private void appendStructuredNamesInternal(final StringBuilder builder, final List<ContentValues> contentValuesList) { + // For safety, we'll emit just one value around StructuredName, as external importers + // may get confused with multiple "N", "FN", etc. properties, though it is valid in + // vCard spec. + ContentValues primaryContentValues = null; for (ContentValues contentValues : contentValuesList) { - final String familyName = contentValues - .getAsString(StructuredName.FAMILY_NAME); - final String middleName = contentValues - .getAsString(StructuredName.MIDDLE_NAME); - final String givenName = contentValues - .getAsString(StructuredName.GIVEN_NAME); - final String prefix = contentValues - .getAsString(StructuredName.PREFIX); - final String suffix = contentValues - .getAsString(StructuredName.SUFFIX); - final String displayName = contentValues - .getAsString(StructuredName.DISPLAY_NAME); - - // For now, some primary element is not encoded into Quoted-Printable, which is not - // valid in vCard spec strictly. In the future, we may have to have some flag to - // enable composer to encode these primary field into Quoted-Printable. - if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) { - final String encodedFamily = escapeCharacters(familyName); - final String encodedGiven = escapeCharacters(givenName); - final String encodedMiddle = escapeCharacters(middleName); - final String encodedPrefix = escapeCharacters(prefix); - final String encodedSuffix = escapeCharacters(suffix); - - // N property. This order is specified by vCard spec and does not depend on countries. - builder.append(VCARD_PROPERTY_NAME); - if (!(VCardUtils.containsOnlyAscii(familyName) && - VCardUtils.containsOnlyAscii(givenName) && - VCardUtils.containsOnlyAscii(middleName) && - VCardUtils.containsOnlyAscii(prefix) && - VCardUtils.containsOnlyAscii(suffix))) { - builder.append(VCARD_ATTR_SEPARATOR); - builder.append(mVCardAttributeCharset); - } - - builder.append(VCARD_DATA_SEPARATOR); - builder.append(encodedFamily); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(encodedGiven); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(encodedMiddle); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(encodedPrefix); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(encodedSuffix); - builder.append(VCARD_COL_SEPARATOR); - - final String encodedFullname = VCardUtils.constructNameFromElements( - VCardConfig.getNameOrderType(mVCardType), - encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix); + Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY); + if (isSuperPrimary != null && isSuperPrimary != 0) { + // We choose "super primary" ContentValues. + primaryContentValues = contentValues; + break; + } else if (primaryContentValues == null && contentValues != null) { + // We choose the first ContentValues if "super primary" ContentValues does not exist. + primaryContentValues = contentValues; + } + } - // FN property - builder.append(VCARD_PROPERTY_FULL_NAME); - if (!VCardUtils.containsOnlyAscii(encodedFullname)) { - builder.append(VCARD_ATTR_SEPARATOR); - builder.append(mVCardAttributeCharset); - } - builder.append(VCARD_DATA_SEPARATOR); - builder.append(encodedFullname); - builder.append(VCARD_COL_SEPARATOR); - } else if (!TextUtils.isEmpty(displayName)) { - builder.append(VCARD_PROPERTY_NAME); + if (primaryContentValues == null) { + Log.e(LOG_TAG, "All ContentValues given from database is empty."); + primaryContentValues = new ContentValues(); + } + + final String familyName = primaryContentValues + .getAsString(StructuredName.FAMILY_NAME); + final String middleName = primaryContentValues + .getAsString(StructuredName.MIDDLE_NAME); + final String givenName = primaryContentValues + .getAsString(StructuredName.GIVEN_NAME); + final String prefix = primaryContentValues + .getAsString(StructuredName.PREFIX); + final String suffix = primaryContentValues + .getAsString(StructuredName.SUFFIX); + final String displayName = primaryContentValues + .getAsString(StructuredName.DISPLAY_NAME); + + // For now, some primary element is not encoded into Quoted-Printable, which is not + // valid in vCard spec strictly. In the future, we may have to have some flag to + // enable composer to encode these primary field into Quoted-Printable. + if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) { + final String encodedFamily = escapeCharacters(familyName); + final String encodedGiven = escapeCharacters(givenName); + final String encodedMiddle = escapeCharacters(middleName); + final String encodedPrefix = escapeCharacters(prefix); + final String encodedSuffix = escapeCharacters(suffix); + + // N property. This order is specified by vCard spec and does not depend on countries. + builder.append(VCARD_PROPERTY_NAME); + if (!(VCardUtils.containsOnlyPrintableAscii(familyName) && + VCardUtils.containsOnlyPrintableAscii(givenName) && + VCardUtils.containsOnlyPrintableAscii(middleName) && + VCardUtils.containsOnlyPrintableAscii(prefix) && + VCardUtils.containsOnlyPrintableAscii(suffix))) { builder.append(VCARD_ATTR_SEPARATOR); builder.append(mVCardAttributeCharset); - builder.append(VCARD_DATA_SEPARATOR); - builder.append(escapeCharacters(displayName)); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(VCARD_COL_SEPARATOR); - } else if (mIsDoCoMo) { - appendVCardLine(builder, VCARD_PROPERTY_NAME, ""); } - String phoneticFamilyName = contentValues - .getAsString(StructuredName.PHONETIC_FAMILY_NAME); - String phoneticMiddleName = contentValues - .getAsString(StructuredName.PHONETIC_MIDDLE_NAME); - String phoneticGivenName = contentValues - .getAsString(StructuredName.PHONETIC_GIVEN_NAME); - if (!(TextUtils.isEmpty(phoneticFamilyName) - && TextUtils.isEmpty(phoneticMiddleName) && TextUtils - .isEmpty(phoneticGivenName))) { // if not empty - if (mIsJapaneseMobilePhone) { - phoneticFamilyName = VCardUtils - .toHalfWidthString(phoneticFamilyName); - phoneticMiddleName = VCardUtils - .toHalfWidthString(phoneticMiddleName); - phoneticGivenName = VCardUtils - .toHalfWidthString(phoneticGivenName); - } + builder.append(VCARD_DATA_SEPARATOR); + builder.append(encodedFamily); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(encodedGiven); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(encodedMiddle); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(encodedPrefix); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(encodedSuffix); + builder.append(VCARD_COL_SEPARATOR); - if (mIsV30) { - final String sortString = VCardUtils - .constructNameFromElements(mVCardType, - phoneticFamilyName, phoneticMiddleName, - phoneticGivenName); - builder.append(VCARD_PROPERTY_SORT_STRING); - - if (!VCardUtils.containsOnlyAscii(sortString)) { - // Strictly, adding charset information is NOT valid in - // VCard 3.0, - // but we'll add this info since parser side may be able to - // use the charset via - // this attribute field. - // - // e.g. Japanese mobile phones use Shift_Jis while RFC 2426 - // recommends - // UTF-8. By adding this field, parsers may be able to know - // this text - // is NOT UTF-8 but Shift_Jis. - builder.append(VCARD_ATTR_SEPARATOR); - builder.append(mVCardAttributeCharset); - } + final String encodedFullname = VCardUtils.constructNameFromElements( + VCardConfig.getNameOrderType(mVCardType), + encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix); - builder.append(VCARD_DATA_SEPARATOR); - builder.append(sortString); - builder.append(VCARD_COL_SEPARATOR); - } else { - // Note: There is no appropriate property for expressing - // phonetic name in - // VCard 2.1, while there is in VCard 3.0 (SORT-STRING). - // We chose to use DoCoMo's way since it is supported by a - // lot of - // Japanese mobile phones. + // FN property + builder.append(VCARD_PROPERTY_FULL_NAME); + if (!VCardUtils.containsOnlyPrintableAscii(encodedFullname)) { + builder.append(VCARD_ATTR_SEPARATOR); + builder.append(mVCardAttributeCharset); + } + builder.append(VCARD_DATA_SEPARATOR); + builder.append(encodedFullname); + builder.append(VCARD_COL_SEPARATOR); + } else if (!TextUtils.isEmpty(displayName)) { + builder.append(VCARD_PROPERTY_NAME); + builder.append(VCARD_ATTR_SEPARATOR); + builder.append(mVCardAttributeCharset); + builder.append(VCARD_DATA_SEPARATOR); + builder.append(escapeCharacters(displayName)); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_COL_SEPARATOR); + } else if (mIsDoCoMo) { + appendVCardLine(builder, VCARD_PROPERTY_NAME, ""); + } else if (mIsV30) { + appendVCardLine(builder, VCARD_PROPERTY_NAME, ""); + appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, ""); + } + + String phoneticFamilyName = primaryContentValues + .getAsString(StructuredName.PHONETIC_FAMILY_NAME); + String phoneticMiddleName = primaryContentValues + .getAsString(StructuredName.PHONETIC_MIDDLE_NAME); + String phoneticGivenName = primaryContentValues + .getAsString(StructuredName.PHONETIC_GIVEN_NAME); + if (!(TextUtils.isEmpty(phoneticFamilyName) + && TextUtils.isEmpty(phoneticMiddleName) && + TextUtils.isEmpty(phoneticGivenName))) { // if not empty + if (mIsJapaneseMobilePhone) { + phoneticFamilyName = VCardUtils + .toHalfWidthString(phoneticFamilyName); + phoneticMiddleName = VCardUtils + .toHalfWidthString(phoneticMiddleName); + phoneticGivenName = VCardUtils + .toHalfWidthString(phoneticGivenName); + } + + if (mIsV30) { + final String sortString = VCardUtils + .constructNameFromElements(mVCardType, + phoneticFamilyName, phoneticMiddleName, + phoneticGivenName); + builder.append(VCARD_PROPERTY_SORT_STRING); + + if (!VCardUtils.containsOnlyPrintableAscii(sortString)) { + // Strictly, adding charset information is NOT valid in + // VCard 3.0, + // but we'll add this info since parser side may be able to + // use the charset via + // this attribute field. // - // TODO: should use Quoted-Pritable? - builder.append(VCARD_PROPERTY_SOUND); - builder.append(VCARD_ATTR_SEPARATOR); - builder.append(Constants.ATTR_TYPE_X_IRMC_N); + // e.g. Japanese mobile phones use Shift_Jis while RFC 2426 + // recommends + // UTF-8. By adding this field, parsers may be able to know + // this text + // is NOT UTF-8 but Shift_Jis. builder.append(VCARD_ATTR_SEPARATOR); - - if (!(VCardUtils.containsOnlyAscii(phoneticFamilyName) && - VCardUtils.containsOnlyAscii(phoneticMiddleName) && - VCardUtils.containsOnlyAscii(phoneticGivenName))) { - builder.append(mVCardAttributeCharset); - builder.append(VCARD_DATA_SEPARATOR); - } - - builder.append(escapeCharacters(phoneticFamilyName)); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(escapeCharacters(phoneticMiddleName)); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(escapeCharacters(phoneticGivenName)); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(VCARD_ITEM_SEPARATOR); - builder.append(VCARD_COL_SEPARATOR); - - if (mUsesAndroidProperty) { - final String phoneticName = VCardUtils - .constructNameFromElements(mVCardType, - phoneticFamilyName, phoneticMiddleName, - phoneticGivenName); - builder.append(VCARD_PROPERTY_X_PHONETIC_NAME); - - if (!VCardUtils.containsOnlyAscii(phoneticName)) { - builder.append(VCARD_ATTR_SEPARATOR); - builder.append(mVCardAttributeCharset); - } - - builder.append(VCARD_DATA_SEPARATOR); - // TODO: may need to make the text quoted-printable. - builder.append(phoneticName); - builder.append(VCARD_COL_SEPARATOR); - } + builder.append(mVCardAttributeCharset); } - } else if (mIsDoCoMo) { + + builder.append(VCARD_DATA_SEPARATOR); + builder.append(sortString); + builder.append(VCARD_COL_SEPARATOR); + } else { + // Note: There is no appropriate property for expressing + // phonetic name in + // VCard 2.1, while there is in VCard 3.0 (SORT-STRING). + // We chose to use DoCoMo's way since it is supported by a + // lot of Japanese mobile phones. + // + // TODO: should use Quoted-Pritable? builder.append(VCARD_PROPERTY_SOUND); builder.append(VCARD_ATTR_SEPARATOR); builder.append(Constants.ATTR_TYPE_X_IRMC_N); - builder.append(VCARD_DATA_SEPARATOR); + builder.append(VCARD_ATTR_SEPARATOR); + + if (!(VCardUtils.containsOnlyPrintableAscii(phoneticFamilyName) && + VCardUtils.containsOnlyPrintableAscii(phoneticMiddleName) && + VCardUtils.containsOnlyPrintableAscii(phoneticGivenName))) { + builder.append(mVCardAttributeCharset); + builder.append(VCARD_DATA_SEPARATOR); + } + + builder.append(escapeCharacters(phoneticFamilyName)); builder.append(VCARD_ITEM_SEPARATOR); + builder.append(escapeCharacters(phoneticMiddleName)); builder.append(VCARD_ITEM_SEPARATOR); + builder.append(escapeCharacters(phoneticGivenName)); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_COL_SEPARATOR); } + } else if (mIsDoCoMo) { + builder.append(VCARD_PROPERTY_SOUND); + builder.append(VCARD_ATTR_SEPARATOR); + builder.append(Constants.ATTR_TYPE_X_IRMC_N); + builder.append(VCARD_DATA_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_ITEM_SEPARATOR); + builder.append(VCARD_COL_SEPARATOR); + } - if (mUsesDefactProperty) { - if (!TextUtils.isEmpty(phoneticGivenName)) { - builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME); - builder.append(VCARD_DATA_SEPARATOR); - builder.append(phoneticGivenName); - builder.append(VCARD_COL_SEPARATOR); - } - if (!TextUtils.isEmpty(phoneticMiddleName)) { - builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME); - builder.append(VCARD_DATA_SEPARATOR); - builder.append(phoneticMiddleName); - builder.append(VCARD_COL_SEPARATOR); - } - if (!TextUtils.isEmpty(phoneticFamilyName)) { - builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME); - builder.append(VCARD_DATA_SEPARATOR); - builder.append(phoneticFamilyName); - builder.append(VCARD_COL_SEPARATOR); - } + if (mUsesDefactProperty) { + if (!TextUtils.isEmpty(phoneticGivenName)) { + builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME); + builder.append(VCARD_DATA_SEPARATOR); + builder.append(phoneticGivenName); + builder.append(VCARD_COL_SEPARATOR); + } + if (!TextUtils.isEmpty(phoneticMiddleName)) { + builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME); + builder.append(VCARD_DATA_SEPARATOR); + builder.append(phoneticMiddleName); + builder.append(VCARD_COL_SEPARATOR); + } + if (!TextUtils.isEmpty(phoneticFamilyName)) { + builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME); + builder.append(VCARD_DATA_SEPARATOR); + builder.append(phoneticFamilyName); + builder.append(VCARD_COL_SEPARATOR); } } } @@ -974,7 +982,7 @@ public class VCardComposer { } builder.append(propertyNickname); - if (!VCardUtils.containsOnlyAscii(propertyNickname)) { + if (!VCardUtils.containsOnlyPrintableAscii(propertyNickname)) { // Strictly, this is not valid in vCard 3.0. See above. builder.append(VCARD_ATTR_SEPARATOR); builder.append(mVCardAttributeCharset); @@ -991,16 +999,31 @@ public class VCardComposer { final Map<String, List<ContentValues>> contentValuesListMap) { final List<ContentValues> contentValuesList = contentValuesListMap .get(Phone.CONTENT_ITEM_TYPE); + boolean phoneLineExists = false; if (contentValuesList != null) { + Set<String> phoneSet = new HashSet<String>(); for (ContentValues contentValues : contentValuesList) { - Integer phoneType = contentValues.getAsInteger(Phone.TYPE); - int phoneTypeAsPrimitive = - (phoneType == null ? Phone.TYPE_HOME : phoneType); - appendVCardTelephoneLine(builder, phoneTypeAsPrimitive, - contentValues.getAsString(Phone.LABEL), - contentValues.getAsString(Phone.NUMBER)); + final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE); + final String label = contentValues.getAsString(Phone.LABEL); + String phoneNumber = contentValues.getAsString(Phone.NUMBER); + if (phoneNumber != null) { + phoneNumber = phoneNumber.trim(); + } + if (TextUtils.isEmpty(phoneNumber)) { + continue; + } + phoneLineExists = true; + int type = (typeAsObject != null ? typeAsObject : Phone.TYPE_HOME); + // TODO: Premature, since this allows two phone numbers which are + // same from the view of phone number format (e.g. "100" v.s. "1-0-0") + if (!phoneSet.contains(phoneNumber)) { + phoneSet.add(phoneNumber); + appendVCardTelephoneLine(builder, type, label, phoneNumber); + } } - } else if (mIsDoCoMo) { + } + + if (!phoneLineExists && mIsDoCoMo) { appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", ""); } } @@ -1009,14 +1032,31 @@ public class VCardComposer { final Map<String, List<ContentValues>> contentValuesListMap) { final List<ContentValues> contentValuesList = contentValuesListMap .get(Email.CONTENT_ITEM_TYPE); + boolean emailAddressExists = false; if (contentValuesList != null) { + Set<String> addressSet = new HashSet<String>(); for (ContentValues contentValues : contentValuesList) { - appendVCardEmailLine(builder, contentValues - .getAsInteger(Email.TYPE), contentValues - .getAsString(Email.LABEL), contentValues - .getAsString(Email.DATA)); + Integer typeAsObject = contentValues.getAsInteger(Email.TYPE); + final int type = (typeAsObject != null ? + typeAsObject : Email.TYPE_OTHER); + final String label = contentValues.getAsString(Email.LABEL); + String emailAddress = contentValues.getAsString(Email.DATA); + if (emailAddress != null) { + emailAddress = emailAddress.trim(); + } + if (TextUtils.isEmpty(emailAddress)) { + continue; + } + emailAddressExists = true; + // Do not allow completely same email address line emitted into each file. + if (!addressSet.contains(emailAddress)) { + addressSet.add(emailAddress); + appendVCardEmailLine(builder, type, label, emailAddress); + } } - } else if (mIsDoCoMo) { + } + + if (!emailAddressExists && mIsDoCoMo) { appendVCardEmailLine(builder, Email.TYPE_HOME, "", ""); } } @@ -1087,7 +1127,9 @@ public class VCardComposer { for (ContentValues contentValues : contentValuesList) { final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE); final String label = contentValues.getAsString(StructuredPostal.LABEL); - appendVCardPostalLine(builder, type, label, contentValues); + if (type != null) { + appendVCardPostalLine(builder, type, label, contentValues); + } } } @@ -1099,6 +1141,12 @@ public class VCardComposer { for (ContentValues contentValues : contentValuesList) { Integer protocol = contentValues.getAsInteger(Im.PROTOCOL); String data = contentValues.getAsString(Im.DATA); + if (data != null) { + data = data.trim(); + } + if (TextUtils.isEmpty(data)) { + continue; + } if (protocol != null && protocol == Im.PROTOCOL_GOOGLE_TALK) { if (VCardConfig.usesAndroidSpecificProperty(mVCardType)) { @@ -1116,8 +1164,13 @@ public class VCardComposer { .get(Website.CONTENT_ITEM_TYPE); if (contentValuesList != null) { for (ContentValues contentValues : contentValuesList) { - final String website = contentValues.getAsString(Website.URL); - appendVCardLine(builder, VCARD_PROPERTY_URL, website); + String website = contentValues.getAsString(Website.URL); + if (website != null) { + website = website.trim(); + } + if (!TextUtils.isEmpty(website)) { + appendVCardLine(builder, VCARD_PROPERTY_URL, website); + } } } } @@ -1130,8 +1183,13 @@ public class VCardComposer { // Theoretically, there must be only one birthday for each vCard data and // we are afraid of some parse error occuring in some devices, so // we emit only one birthday entry for now. - final String birthday = contentValuesList.get(0).getAsString(Birthday.BIRTHDAY); - appendVCardLine(builder, VCARD_PROPERTY_BIRTHDAY, birthday); + String birthday = contentValuesList.get(0).getAsString(Birthday.BIRTHDAY); + if (birthday != null) { + birthday = birthday.trim(); + } + if (!TextUtils.isEmpty(birthday)) { + appendVCardLine(builder, VCARD_PROPERTY_BIRTHDAY, birthday); + } } } @@ -1141,14 +1199,29 @@ public class VCardComposer { .get(Organization.CONTENT_ITEM_TYPE); if (contentValuesList != null) { for (ContentValues contentValues : contentValuesList) { - final String company = contentValues + String company = contentValues .getAsString(Organization.COMPANY); - final String title = contentValues + if (company != null) { + company = company.trim(); + } + String title = contentValues .getAsString(Organization.TITLE); - appendVCardLine(builder, VCARD_PROPERTY_ORG, company, true, - mUsesQuotedPrintable); - appendVCardLine(builder, VCARD_PROPERTY_TITLE, title, true, - mUsesQuotedPrintable); + if (title != null) { + title = title.trim(); + } + + if (!TextUtils.isEmpty(company)) { + appendVCardLine(builder, VCARD_PROPERTY_ORG, company, + !VCardUtils.containsOnlyPrintableAscii(company), + (mUsesQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(company))); + } + if (!TextUtils.isEmpty(title)) { + appendVCardLine(builder, VCARD_PROPERTY_TITLE, title, + !VCardUtils.containsOnlyPrintableAscii(title), + (mUsesQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(title))); + } } } } @@ -1201,7 +1274,10 @@ public class VCardComposer { StringBuilder noteBuilder = new StringBuilder(); boolean first = true; for (ContentValues contentValues : contentValuesList) { - final String note = contentValues.getAsString(Note.NOTE); + String note = contentValues.getAsString(Note.NOTE); + if (note == null) { + note = ""; + } if (note.length() > 0) { if (first) { first = false; @@ -1211,14 +1287,27 @@ public class VCardComposer { noteBuilder.append(note); } } - appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteBuilder.toString(), - true, mUsesQuotedPrintable); + final String noteStr = noteBuilder.toString(); + // This means we scan noteStr completely twice, which is redundant. + // But for now, we assume this is not so time-consuming.. + final boolean shouldAppendCharsetInfo = + !VCardUtils.containsOnlyPrintableAscii(noteStr); + final boolean reallyUseQuotedPrintable = + (mUsesQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr)); + appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteStr, + shouldAppendCharsetInfo, reallyUseQuotedPrintable); } else { for (ContentValues contentValues : contentValuesList) { - final String note = contentValues.getAsString(Note.NOTE); - if (!TextUtils.isEmpty(note)) { - appendVCardLine(builder, VCARD_PROPERTY_NOTE, note, true, - mUsesQuotedPrintable); + final String noteStr = contentValues.getAsString(Note.NOTE); + if (!TextUtils.isEmpty(noteStr)) { + final boolean shouldAppendCharsetInfo = + !VCardUtils.containsOnlyPrintableAscii(noteStr); + final boolean reallyUseQuotedPrintable = + (mUsesQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr)); + appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteStr, + shouldAppendCharsetInfo, reallyUseQuotedPrintable); } } } @@ -1339,15 +1428,31 @@ public class VCardComposer { builder.append(VCARD_PROPERTY_ADR); builder.append(VCARD_ATTR_SEPARATOR); + // Note: Not sure why we need to emit "empty" line even when actual data does not exist. + // There may be some reason or may not be any. We keep safer side. + // TODO: investigate this. boolean dataExists = false; String[] dataArray = VCardUtils.getVCardPostalElements(contentValues); + boolean actuallyUseQuotedPrintable = false; + boolean shouldAppendCharset = false; + for (String data : dataArray) { + if (!TextUtils.isEmpty(data)) { + dataExists = true; + if (!shouldAppendCharset && !VCardUtils.containsOnlyPrintableAscii(data)) { + shouldAppendCharset = true; + } + if (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(data)) { + actuallyUseQuotedPrintable = true; + break; + } + } + } + int length = dataArray.length; - final boolean useQuotedPrintable = mUsesQuotedPrintable; for (int i = 0; i < length; i++) { String data = dataArray[i]; if (!TextUtils.isEmpty(data)) { - dataExists = true; - if (useQuotedPrintable) { + if (actuallyUseQuotedPrintable) { dataArray[i] = encodeQuotedPrintable(data); } else { dataArray[i] = escapeCharacters(data); @@ -1394,25 +1499,40 @@ public class VCardComposer { } } - if (typeAsString != null) { - appendTypeAttribute(builder, typeAsString); - } + // Attribute(s). - if (dataExists) { - // Strictly, vCard 3.0 does not allow exporters to emit charset information, - // but we will add it since the information should be useful for importers, - // - // Assume no parser does not emit error with this attribute in vCard 3.0. + { + boolean shouldAppendAttrSeparator = false; if (typeAsString != null) { - builder.append(VCARD_ATTR_SEPARATOR); + appendTypeAttribute(builder, typeAsString); + shouldAppendAttrSeparator = true; } - builder.append(mVCardAttributeCharset); - if (useQuotedPrintable) { - builder.append(VCARD_ATTR_SEPARATOR); - builder.append(VCARD_ATTR_ENCODING_QP); + if (dataExists) { + if (shouldAppendCharset) { + // Strictly, vCard 3.0 does not allow exporters to emit charset information, + // but we will add it since the information should be useful for importers, + // + // Assume no parser does not emit error with this attribute in vCard 3.0. + if (shouldAppendAttrSeparator) { + builder.append(VCARD_ATTR_SEPARATOR); + } + builder.append(mVCardAttributeCharset); + shouldAppendAttrSeparator = true; + } + + if (actuallyUseQuotedPrintable) { + if (shouldAppendAttrSeparator) { + builder.append(VCARD_ATTR_SEPARATOR); + } + builder.append(VCARD_ATTR_ENCODING_QP); + shouldAppendAttrSeparator = true; + } } } + + // Property values. + builder.append(VCARD_DATA_SEPARATOR); if (dataExists) { // The elements in dataArray are already encoded to quoted printable diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java index ffceade..4f50103 100644 --- a/core/java/android/pim/vcard/VCardUtils.java +++ b/core/java/android/pim/vcard/VCardUtils.java @@ -283,7 +283,7 @@ public class VCardUtils { return builder.toString(); } - public static boolean containsOnlyAscii(String str) { + public static boolean containsOnlyPrintableAscii(String str) { if (TextUtils.isEmpty(str)) { return true; } @@ -299,13 +299,35 @@ public class VCardUtils { } return true; } - + + /** + * This is useful when checking the string should be encoded into quoted-printable + * or not, which is required by vCard 2.1. + * See the definition of "7bit" in vCard 2.1 spec for more information. + */ + public static boolean containsOnlyNonCrLfPrintableAscii(String str) { + if (TextUtils.isEmpty(str)) { + return true; + } + + final int length = str.length(); + final int asciiFirst = 0x20; + final int asciiLast = 0x126; + for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { + int c = str.codePointAt(i); + if (c < asciiFirst || asciiLast < c || c == '\n' || c == '\r') { + return false; + } + } + return true; + } + /** * This is useful since vCard 3.0 often requires the ("X-") properties and groups * should contain only alphabets, digits, and hyphen. * * Note: It is already known some devices (wrongly) outputs properties with characters - * which should not be in the field. One example is "X-GOOGLE TALK". We appreciate + * which should not be in the field. One example is "X-GOOGLE TALK". We accept * such kind of input but must never output it unless the target is very specific * to the device which is able to parse the malformed input. */ diff --git a/core/java/android/widget/FasttrackBadgeWidget.java b/core/java/android/widget/FasttrackBadgeWidget.java index 8c8e054..ba8539f 100644 --- a/core/java/android/widget/FasttrackBadgeWidget.java +++ b/core/java/android/widget/FasttrackBadgeWidget.java @@ -33,6 +33,7 @@ import android.provider.ContactsContract.CommonDataKinds.Email; import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; +import com.android.internal.R; /** * Widget used to show an image with the standard fasttrack badge @@ -48,6 +49,7 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { private int mMode; private QueryHandler mQueryHandler; private Drawable mBadgeBackground; + private Drawable mNoBadgeBackground; protected String[] mExcludeMimes = null; @@ -119,7 +121,10 @@ public class FasttrackBadgeWidget extends ImageView implements OnClickListener { private void onContactUriChanged() { if (mContactUri == null && mContactEmail == null && mContactPhone == null) { - setBackgroundDrawable(null); + if (mNoBadgeBackground == null) { + mNoBadgeBackground = getResources().getDrawable(R.drawable.fasttrack_nobadge); + } + setBackgroundDrawable(mNoBadgeBackground); } else { setBackgroundDrawable(mBadgeBackground); } diff --git a/core/res/res/drawable-hdpi/contact_header_bg.9.png b/core/res/res/drawable-hdpi/contact_header_bg.9.png Binary files differnew file mode 100755 index 0000000..981b2e9 --- /dev/null +++ b/core/res/res/drawable-hdpi/contact_header_bg.9.png diff --git a/core/res/res/drawable-hdpi/ic_contact_picture_2.png b/core/res/res/drawable-hdpi/ic_contact_picture_2.png Binary files differnew file mode 100755 index 0000000..5e65276 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_contact_picture_2.png diff --git a/core/res/res/drawable-hdpi/ic_contact_picture_3.png b/core/res/res/drawable-hdpi/ic_contact_picture_3.png Binary files differnew file mode 100755 index 0000000..a8ec1e1 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_contact_picture_3.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png Binary files differnew file mode 100755 index 0000000..24e07ab --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_data_connected_h.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_h.png b/core/res/res/drawable-hdpi/stat_sys_data_in_h.png Binary files differnew file mode 100755 index 0000000..f2f6daa --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_data_in_h.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png Binary files differnew file mode 100755 index 0000000..5d6ef05 --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_data_inandout_h.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_h.png b/core/res/res/drawable-hdpi/stat_sys_data_out_h.png Binary files differnew file mode 100755 index 0000000..5e3122d --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_data_out_h.png diff --git a/core/res/res/drawable-land-hdpi/bottombar_565.png b/core/res/res/drawable-land-hdpi/bottombar_565.png Binary files differnew file mode 100755 index 0000000..9df56ca --- /dev/null +++ b/core/res/res/drawable-land-hdpi/bottombar_565.png diff --git a/core/res/res/drawable-land/bottombar_565.png b/core/res/res/drawable-land-mdpi/bottombar_565.png Binary files differindex 6121856..6121856 100644 --- a/core/res/res/drawable-land/bottombar_565.png +++ b/core/res/res/drawable-land-mdpi/bottombar_565.png diff --git a/core/res/res/drawable/contact_header_bg.9.png b/core/res/res/drawable-mdpi/contact_header_bg.9.png Binary files differindex 7f9a5a3..7f9a5a3 100644 --- a/core/res/res/drawable/contact_header_bg.9.png +++ b/core/res/res/drawable-mdpi/contact_header_bg.9.png diff --git a/core/res/res/drawable/ic_contact_picture_2.png b/core/res/res/drawable-mdpi/ic_contact_picture_2.png Binary files differindex 8b184af..8b184af 100644 --- a/core/res/res/drawable/ic_contact_picture_2.png +++ b/core/res/res/drawable-mdpi/ic_contact_picture_2.png diff --git a/core/res/res/drawable/ic_contact_picture_3.png b/core/res/res/drawable-mdpi/ic_contact_picture_3.png Binary files differindex a2d08b5..a2d08b5 100644 --- a/core/res/res/drawable/ic_contact_picture_3.png +++ b/core/res/res/drawable-mdpi/ic_contact_picture_3.png diff --git a/core/res/res/drawable/stat_sys_data_connected_h.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_h.png Binary files differindex 7d5413a..7d5413a 100644 --- a/core/res/res/drawable/stat_sys_data_connected_h.png +++ b/core/res/res/drawable-mdpi/stat_sys_data_connected_h.png diff --git a/core/res/res/drawable/stat_sys_data_in_h.png b/core/res/res/drawable-mdpi/stat_sys_data_in_h.png Binary files differindex 695b80c..695b80c 100644 --- a/core/res/res/drawable/stat_sys_data_in_h.png +++ b/core/res/res/drawable-mdpi/stat_sys_data_in_h.png diff --git a/core/res/res/drawable/stat_sys_data_inandout_h.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png Binary files differindex 467acd1..467acd1 100644 --- a/core/res/res/drawable/stat_sys_data_inandout_h.png +++ b/core/res/res/drawable-mdpi/stat_sys_data_inandout_h.png diff --git a/core/res/res/drawable/stat_sys_data_out_h.png b/core/res/res/drawable-mdpi/stat_sys_data_out_h.png Binary files differindex da50305..da50305 100644 --- a/core/res/res/drawable/stat_sys_data_out_h.png +++ b/core/res/res/drawable-mdpi/stat_sys_data_out_h.png diff --git a/core/res/res/drawable/fasttrack_nobadge.xml b/core/res/res/drawable/fasttrack_nobadge.xml new file mode 100644 index 0000000..538e165 --- /dev/null +++ b/core/res/res/drawable/fasttrack_nobadge.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* bubble_with_chats.xml +** +** Copyright 2009, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/fasttrack_nobadge_pressed" /> + <item android:state_selected="true" android:drawable="@drawable/fasttrack_nobadge_highlight" /> + <item android:state_focused="true" android:drawable="@drawable/fasttrack_nobadge_highlight" /> + <item android:state_enabled="false" android:drawable="@drawable/fasttrack_nobadge_normal" /> + <item android:drawable="@drawable/fasttrack_nobadge_normal" /> +</selector> diff --git a/core/res/res/drawable/fasttrack_nobadge_highlight.9.png b/core/res/res/drawable/fasttrack_nobadge_highlight.9.png Binary files differnew file mode 100644 index 0000000..f0f50b3 --- /dev/null +++ b/core/res/res/drawable/fasttrack_nobadge_highlight.9.png diff --git a/core/res/res/drawable/fasttrack_nobadge_normal.9.png b/core/res/res/drawable/fasttrack_nobadge_normal.9.png Binary files differnew file mode 100644 index 0000000..01cc9dc --- /dev/null +++ b/core/res/res/drawable/fasttrack_nobadge_normal.9.png diff --git a/core/res/res/drawable/fasttrack_nobadge_pressed.9.png b/core/res/res/drawable/fasttrack_nobadge_pressed.9.png Binary files differnew file mode 100644 index 0000000..6e22c87 --- /dev/null +++ b/core/res/res/drawable/fasttrack_nobadge_pressed.9.png |