Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 592988d3 authored by Daisuke Miyakawa's avatar Daisuke Miyakawa
Browse files

Add unit tests for Japanization and fix several bugs.

Internal issue number: 2195990
parent f18a01c7
Loading
Loading
Loading
Loading
+52 −32
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@ import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
@@ -675,16 +677,53 @@ public class ContactStruct {
     */
    @SuppressWarnings("fallthrough")
    private void handlePhoneticNameFromSound(List<String> elems) {
        // Family, Given, Middle. (1-3)
        // This is not from specification but mere assumption. Some Japanese phones use this order.
        if (!(TextUtils.isEmpty(mPhoneticFamilyName) &&
                TextUtils.isEmpty(mPhoneticMiddleName) &&
                TextUtils.isEmpty(mPhoneticGivenName))) {
            // This means the other properties like "X-PHONETIC-FIRST-NAME" was already found.
            // Ignore "SOUND;X-IRMC-N".
            return;
        }

        int size;
        if (elems == null || (size = elems.size()) < 1) {
            return;
        }

        // Assume that the order is "Family, Given, Middle".
        // This is not from specification but mere assumption. Some Japanese phones use this order.
        if (size > 3) {
            size = 3;
        }

        if (elems.get(0).length() > 0) {
            boolean onlyFirstElemIsNonEmpty = true;
            for (int i = 1; i < size; i++) {
                if (elems.get(i).length() > 0) {
                    onlyFirstElemIsNonEmpty = false;
                    break;
                }
            }
            if (onlyFirstElemIsNonEmpty) {
                final String[] namesArray = elems.get(0).split(" ");
                final int nameArrayLength = namesArray.length;
                if (nameArrayLength == 3) {
                    // Assume the string is "Family Middle Given".
                    mPhoneticFamilyName = namesArray[0];
                    mPhoneticMiddleName = namesArray[1];
                    mPhoneticGivenName = namesArray[2];
                } else if (nameArrayLength == 2) {
                    // Assume the string is "Family Given" based on the Japanese mobile
                    // phones' preference.
                    mPhoneticFamilyName = namesArray[0];
                    mPhoneticGivenName = namesArray[1];
                } else {
                    mPhoneticFullName = elems.get(0);
                }
                return;
            }
        }

        switch (size) {
        // fallthrough
        case 3:
@@ -976,36 +1015,8 @@ public class ContactStruct {
        if (!TextUtils.isEmpty(mFullName)) {
            mDisplayName = mFullName;
        } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
            StringBuilder builder = new StringBuilder();
            List<String> nameList;
            switch (VCardConfig.getNameOrderType(mVCardType)) {
            case VCardConfig.NAME_ORDER_JAPANESE:
                if (VCardUtils.containsOnlyPrintableAscii(mFamilyName) &&
                        VCardUtils.containsOnlyPrintableAscii(mGivenName)) {
                    nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix);
                } else {
                    nameList = Arrays.asList(mPrefix, mFamilyName, mMiddleName, mGivenName, mSuffix);
                }
                break;
            case VCardConfig.NAME_ORDER_EUROPE:
                nameList = Arrays.asList(mPrefix, mMiddleName, mGivenName, mFamilyName, mSuffix);
                break;
            default:
                nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix);
                break;
            }
            boolean first = true;
            for (String namePart : nameList) {
                if (!TextUtils.isEmpty(namePart)) {
                    if (first) {
                        first = false;
                    } else {
                        builder.append(' ');
                    }
                    builder.append(namePart);
                }
            }
            mDisplayName = builder.toString();
            mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
                    mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
        } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) &&
                TextUtils.isEmpty(mPhoneticGivenName))) {
            mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
@@ -1282,6 +1293,15 @@ public class ContactStruct {
        }
    }

    public static ContactStruct buildFromResolver(ContentResolver resolver) {
        return buildFromResolver(resolver, Contacts.CONTENT_URI);
    }

    public static ContactStruct buildFromResolver(ContentResolver resolver, Uri uri) {

        return null;
    }

    private boolean nameFieldsAreEmpty() {
        return (TextUtils.isEmpty(mFamilyName)
                && TextUtils.isEmpty(mMiddleName)
+55 −27
Original line number Diff line number Diff line
@@ -682,10 +682,18 @@ public class VCardComposer {
        final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME);
        final String prefix = contentValues.getAsString(StructuredName.PREFIX);
        final String suffix = contentValues.getAsString(StructuredName.SUFFIX);
        final String phoneticFamilyName =
                contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
        final String phoneticMiddleName =
                contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
        final String phoneticGivenName =
                contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
        final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME);
        return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) &&
                TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) &&
                TextUtils.isEmpty(suffix) && TextUtils.isEmpty(displayName));
                TextUtils.isEmpty(suffix) && TextUtils.isEmpty(phoneticFamilyName) &&
                TextUtils.isEmpty(phoneticMiddleName) && TextUtils.isEmpty(phoneticGivenName) &&
                TextUtils.isEmpty(displayName));
    }

    private void appendStructuredNamesInternal(final StringBuilder builder,
@@ -865,9 +873,11 @@ public class VCardComposer {
            builder.append(VCARD_ITEM_SEPARATOR);
            builder.append(VCARD_ITEM_SEPARATOR);
            builder.append(VCARD_END_OF_LINE);
            if (mIsV30) {
            builder.append(Constants.PROPERTY_FN);
                // TODO: Not allowed formally...

            // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
            //       when it would be useful for external importers, assuming no external
            //       importer allows this vioration.
            if (shouldAppendCharsetParameter(displayName)) {
                builder.append(VCARD_PARAM_SEPARATOR);
                builder.append(mVCardCharsetParameter);
@@ -875,7 +885,6 @@ public class VCardComposer {
            builder.append(VCARD_DATA_SEPARATOR);
            builder.append(encodedDisplayName);
            builder.append(VCARD_END_OF_LINE);
            }
        } else if (mIsV30) {
            // vCard 3.0 specification requires these fields.
            appendVCardLine(builder, Constants.PROPERTY_N, "");
@@ -913,15 +922,12 @@ public class VCardComposer {
                        .constructNameFromElements(mVCardType,
                                phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
                builder.append(Constants.PROPERTY_SORT_STRING);

                // Do not need to care about QP, since vCard 3.0 does not allow it.
                final String encodedSortString = escapeCharacters(sortString);
                if (shouldAppendCharsetParameter(encodedSortString)) {
                if (shouldAppendCharsetParameter(sortString)) {
                    builder.append(VCARD_PARAM_SEPARATOR);
                    builder.append(mVCardCharsetParameter);
                }
                builder.append(VCARD_DATA_SEPARATOR);
                builder.append(encodedSortString);
                builder.append(escapeCharacters(sortString));
                builder.append(VCARD_END_OF_LINE);
            } else if (mIsJapaneseMobilePhone) {
                // Note: There is no appropriate property for expressing
@@ -964,11 +970,31 @@ public class VCardComposer {
                    builder.append(mVCardCharsetParameter);
                }
                builder.append(VCARD_DATA_SEPARATOR);
                // DoCoMo's specification requires vCard composer to use just the first
                // column.
                {
                    boolean first = true;
                    if (!TextUtils.isEmpty(encodedPhoneticFamilyName)) {
                        builder.append(encodedPhoneticFamilyName);
                builder.append(VCARD_ITEM_SEPARATOR);
                        first = false;
                    }
                    if (!TextUtils.isEmpty(encodedPhoneticMiddleName)) {
                        if (first) {
                            first = false;
                        } else {
                            builder.append(' ');
                        }
                        builder.append(encodedPhoneticMiddleName);
                    }
                    if (!TextUtils.isEmpty(encodedPhoneticGivenName)) {
                        if (!first) {
                            builder.append(' ');
                        }
                        builder.append(encodedPhoneticGivenName);
                    }
                }
                builder.append(VCARD_ITEM_SEPARATOR);
                builder.append(VCARD_ITEM_SEPARATOR);
                builder.append(encodedPhoneticMiddleName);
                builder.append(VCARD_ITEM_SEPARATOR);
                builder.append(VCARD_ITEM_SEPARATOR);
                builder.append(VCARD_END_OF_LINE);
@@ -988,7 +1014,8 @@ public class VCardComposer {
        if (mUsesDefactProperty) {
            if (!TextUtils.isEmpty(phoneticGivenName)) {
                final boolean reallyUseQuotedPrintable =
                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName);
                    (mUsesQuotedPrintable &&
                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName));
                final String encodedPhoneticGivenName;
                if (reallyUseQuotedPrintable) {
                    encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
@@ -996,7 +1023,7 @@ public class VCardComposer {
                    encodedPhoneticGivenName = escapeCharacters(phoneticGivenName);
                }
                builder.append(Constants.PROPERTY_X_PHONETIC_FIRST_NAME);
                if (shouldAppendCharsetParameter(encodedPhoneticGivenName)) {
                if (shouldAppendCharsetParameter(phoneticGivenName)) {
                    builder.append(VCARD_PARAM_SEPARATOR);
                    builder.append(mVCardCharsetParameter);
                }
@@ -1010,7 +1037,8 @@ public class VCardComposer {
            }
            if (!TextUtils.isEmpty(phoneticMiddleName)) {
                final boolean reallyUseQuotedPrintable =
                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName);
                    (mUsesQuotedPrintable &&
                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName));
                final String encodedPhoneticMiddleName;
                if (reallyUseQuotedPrintable) {
                    encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
@@ -1018,7 +1046,7 @@ public class VCardComposer {
                    encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName);
                }
                builder.append(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME);
                if (shouldAppendCharsetParameter(encodedPhoneticMiddleName)) {
                if (shouldAppendCharsetParameter(phoneticMiddleName)) {
                    builder.append(VCARD_PARAM_SEPARATOR);
                    builder.append(mVCardCharsetParameter);
                }
@@ -1032,7 +1060,8 @@ public class VCardComposer {
            }
            if (!TextUtils.isEmpty(phoneticFamilyName)) {
                final boolean reallyUseQuotedPrintable =
                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName);
                    (mUsesQuotedPrintable &&
                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName));
                final String encodedPhoneticFamilyName;
                if (reallyUseQuotedPrintable) {
                    encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
@@ -1040,7 +1069,7 @@ public class VCardComposer {
                    encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName);
                }
                builder.append(Constants.PROPERTY_X_PHONETIC_LAST_NAME);
                if (shouldAppendCharsetParameter(encodedPhoneticFamilyName)) {
                if (shouldAppendCharsetParameter(phoneticFamilyName)) {
                    builder.append(VCARD_PARAM_SEPARATOR);
                    builder.append(mVCardCharsetParameter);
                }
@@ -2255,8 +2284,7 @@ public class VCardComposer {
     * to know this text is NOT UTF-8 but Shift_Jis.
     */
    private boolean shouldAppendCharsetParameter(final String propertyValue) {
        return (!VCardUtils.containsOnlyPrintableAscii(propertyValue) &&
                        (!mIsV30 || !mUsesUtf8));
        return (!(mIsV30 && mUsesUtf8) && !VCardUtils.containsOnlyPrintableAscii(propertyValue));
    }

    private boolean shouldAppendCharsetParameters(final List<String> propertyValueList) {
+20 −19
Original line number Diff line number Diff line
@@ -60,7 +60,8 @@ public class VCardConfig {
    // 0x10 is reserved for safety
    
    private static final int FLAG_CHARSET_UTF8 = 0;
    private static final int FLAG_CHARSET_SHIFT_JIS = 0x20;
    private static final int FLAG_CHARSET_SHIFT_JIS = 0x100;
    private static final int FLAG_CHARSET_MASK = 0xF00;

    /**
     * The flag indicating the vCard composer will add some "X-" properties used only in Android
@@ -349,8 +350,8 @@ public class VCardConfig {
        sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
    }

    public static int getVCardTypeFromString(String vcardTypeString) {
        String loweredKey = vcardTypeString.toLowerCase();
    public static int getVCardTypeFromString(final String vcardTypeString) {
        final String loweredKey = vcardTypeString.toLowerCase();
        if (sVCardTypeMap.containsKey(loweredKey)) {
            return sVCardTypeMap.get(loweredKey);
        } else {
@@ -360,31 +361,31 @@ public class VCardConfig {
        }
    }

    public static boolean isV30(int vcardType) {
    public static boolean isV30(final int vcardType) {
        return ((vcardType & FLAG_V30) != 0);  
    }

    public static boolean usesQuotedPrintable(int vcardType) {
    public static boolean usesQuotedPrintable(final int vcardType) {
        return !isV30(vcardType);
    }

    public static boolean usesUtf8(int vcardType) {
        return ((vcardType & FLAG_CHARSET_UTF8) != 0);
    public static boolean usesUtf8(final int vcardType) {
        return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8);
    }

    public static boolean usesShiftJis(int vcardType) {
        return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0);
    public static boolean usesShiftJis(final int vcardType) {
        return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS);
    }

    public static int getNameOrderType(int vcardType) {
    public static int getNameOrderType(final int vcardType) {
        return vcardType & NAME_ORDER_MASK;
    }

    public static boolean usesAndroidSpecificProperty(int vcardType) {
    public static boolean usesAndroidSpecificProperty(final int vcardType) {
        return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0);
    }

    public static boolean usesDefactProperty(int vcardType) {
    public static boolean usesDefactProperty(final int vcardType) {
        return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
    }

@@ -392,12 +393,12 @@ public class VCardConfig {
        return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
    }

    public static boolean refrainsQPToPrimaryProperties(int vcardType) {
    public static boolean refrainsQPToPrimaryProperties(final int vcardType) {
       return (!usesQuotedPrintable(vcardType) ||
               ((vcardType & FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES) != 0));
    }

    public static boolean appendTypeParamName(int vcardType) {
    public static boolean appendTypeParamName(final int vcardType) {
        return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
    }

@@ -405,19 +406,19 @@ public class VCardConfig {
     * @return true if the device is Japanese and some Japanese convension is
     * applied to creating "formatted" something like FORMATTED_ADDRESS.
     */
    public static boolean isJapaneseDevice(int vcardType) {
    public static boolean isJapaneseDevice(final int vcardType) {
        return sJapaneseMobileTypeSet.contains(vcardType);
    }

    public static boolean needsToConvertPhoneticString(int vcardType) {
    public static boolean needsToConvertPhoneticString(final int vcardType) {
        return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0);
    }

    public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
    public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) {
        return vcardType == VCARD_TYPE_DOCOMO;
    }

    public static boolean isDoCoMo(int vcardType) {
    public static boolean isDoCoMo(final int vcardType) {
        return ((vcardType & FLAG_DOCOMO) != 0);
    }

+40 −30
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;

import java.util.ArrayList;
import java.util.Collection;
@@ -181,27 +182,37 @@ public class VCardUtils {
                sPhoneTypesSetUnknownToContacts.contains(phoneType));
    }
    
    public static String[] sortNameElements(int vcardType,
            String familyName, String middleName, String givenName) {
        String[] list = new String[3];
        switch (VCardConfig.getNameOrderType(vcardType)) {
        case VCardConfig.NAME_ORDER_JAPANESE:
            // TODO: Should handle Ascii case?
    public static String[] sortNameElements(final int vcardType,
            final String familyName, final String middleName, final String givenName) {
        final String[] list = new String[3];
        final int nameOrderType = VCardConfig.getNameOrderType(vcardType);
        switch (nameOrderType) {
            case VCardConfig.NAME_ORDER_JAPANESE: {
                if (containsOnlyPrintableAscii(familyName) &&
                        containsOnlyPrintableAscii(givenName)) {
                    list[0] = givenName;
                    list[1] = middleName;
                    list[2] = familyName;
                } else {
                    list[0] = familyName;
                    list[1] = middleName;
                    list[2] = givenName;
                }
                break;
        case VCardConfig.NAME_ORDER_EUROPE:
            }
            case VCardConfig.NAME_ORDER_EUROPE: {
                list[0] = middleName;
                list[1] = givenName;
                list[2] = familyName;
                break;
        default:
            }
            default: {
                list[0] = givenName;
                list[1] = middleName;
                list[2] = familyName;
                break;
            }
        }
        return list;
    }

@@ -302,24 +313,23 @@ public class VCardUtils {
        return dataArray;
    }
    
    public static String constructNameFromElements(int nameOrderType,
            String familyName, String middleName, String givenName) {
        return constructNameFromElements(nameOrderType, familyName, middleName, givenName,
    public static String constructNameFromElements(final int vcardType,
            final String familyName, final String middleName, final String givenName) {
        return constructNameFromElements(vcardType, familyName, middleName, givenName,
                null, null);
    }

    public static String constructNameFromElements(int nameOrderType,
            String familyName, String middleName, String givenName,
            String prefix, String suffix) {
        StringBuilder builder = new StringBuilder();
        String[] nameList = sortNameElements(nameOrderType,
                familyName, middleName, givenName);
    public static String constructNameFromElements(final int vcardType,
            final String familyName, final String middleName, final String givenName,
            final String prefix, final String suffix) {
        final StringBuilder builder = new StringBuilder();
        final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName);
        boolean first = true;
        if (!TextUtils.isEmpty(prefix)) {
            first = false;
            builder.append(prefix);
        }
        for (String namePart : nameList) {
        for (final String namePart : nameList) {
            if (!TextUtils.isEmpty(namePart)) {
                if (first) {
                    first = false;
+10 −0
Original line number Diff line number Diff line
BEGIN:vCard
VERSION:2.1
UID:357
N:;Conference Call
FN:Conference Call
# This line must be ignored.
NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->=
#<- sharp) example. This message must NOT be ignored.
# This line must be ignored too.
END:vCard
Loading