Handle custom labels correctly.

Previous implementation uses upper-case strings instead of original
expressions in vCard, so "X-Custom" becomes the custom type "CUSTOM"

Add test case for this fix.

Bug: 3207011
Change-Id: I287fd595a80453e18b2bb57600ea8d0bd8b78ffd
diff --git a/java/com/android/vcard/VCardEntry.java b/java/com/android/vcard/VCardEntry.java
index 66104bf..655a80f 100644
--- a/java/com/android/vcard/VCardEntry.java
+++ b/java/com/android/vcard/VCardEntry.java
@@ -871,34 +871,34 @@
             int type = -1;
             String label = "";
             boolean isPrimary = false;
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
+            final Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
             if (typeCollection != null) {
-                for (String typeString : typeCollection) {
-                    typeString = typeString.toUpperCase();
-                    if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
+                for (final String typeStringOrg : typeCollection) {
+                    final String typeStringUpperCase = typeStringOrg.toUpperCase();
+                    if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_PREF)) {
                         isPrimary = true;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) {
+                    } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_HOME)) {
                         type = StructuredPostal.TYPE_HOME;
                         label = "";
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK) ||
-                            typeString.equalsIgnoreCase(VCardConstants.PARAM_EXTRA_TYPE_COMPANY)) {
+                    } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_WORK) ||
+                            typeStringUpperCase.equalsIgnoreCase(
+                                        VCardConstants.PARAM_EXTRA_TYPE_COMPANY)) {
                         // "COMPANY" seems emitted by Windows Mobile, which is not
                         // specifically supported by vCard 2.1. We assume this is same
                         // as "WORK".
                         type = StructuredPostal.TYPE_WORK;
                         label = "";
-                    } else if (typeString.equals(VCardConstants.PARAM_ADR_TYPE_PARCEL) ||
-                            typeString.equals(VCardConstants.PARAM_ADR_TYPE_DOM) ||
-                            typeString.equals(VCardConstants.PARAM_ADR_TYPE_INTL)) {
+                    } else if (typeStringUpperCase.equals(VCardConstants.PARAM_ADR_TYPE_PARCEL) ||
+                            typeStringUpperCase.equals(VCardConstants.PARAM_ADR_TYPE_DOM) ||
+                            typeStringUpperCase.equals(VCardConstants.PARAM_ADR_TYPE_INTL)) {
                         // We do not have any appropriate way to store this information.
-                    } else {
-                        if (typeString.startsWith("X-") && type < 0) {
-                            typeString = typeString.substring(2);
-                        }
-                        // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters
-                        // emit non-standard types. We do not handle their values now.
+                    } else if (type < 0) {  // If no other type is specified before
                         type = StructuredPostal.TYPE_CUSTOM;
-                        label = typeString;
+                        if (typeStringUpperCase.startsWith("X-")) {  // If X- or x-
+                            label = typeStringOrg.substring(2);
+                        } else {
+                            label = typeStringOrg;
+                        }
                     }
                 }
             }
@@ -912,27 +912,25 @@
             int type = -1;
             String label = null;
             boolean isPrimary = false;
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
+            final Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
             if (typeCollection != null) {
-                for (String typeString : typeCollection) {
-                    typeString = typeString.toUpperCase();
-                    if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
+                for (final String typeStringOrg : typeCollection) {
+                    final String typeStringUpperCase = typeStringOrg.toUpperCase();
+                    if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_PREF)) {
                         isPrimary = true;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) {
+                    } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_HOME)) {
                         type = Email.TYPE_HOME;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK)) {
+                    } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_WORK)) {
                         type = Email.TYPE_WORK;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_CELL)) {
+                    } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_CELL)) {
                         type = Email.TYPE_MOBILE;
-                    } else {
-                        if (typeString.startsWith("X-") && type < 0) {
-                            typeString = typeString.substring(2);
+                    } else if (type < 0) {  // If no other type is specified before
+                        if (typeStringUpperCase.startsWith("X-")) {  // If X- or x-
+                            label = typeStringOrg.substring(2);
+                        } else {
+                            label = typeStringOrg;
                         }
-                        // vCard 3.0 allows iana-token.
-                        // We may have INTERNET (specified in vCard spec),
-                        // SCHOOL, etc.
                         type = Email.TYPE_CUSTOM;
-                        label = typeString;
                     }
                 }
             }
diff --git a/java/com/android/vcard/VCardUtils.java b/java/com/android/vcard/VCardUtils.java
index a6eba12..1bd35ba 100644
--- a/java/com/android/vcard/VCardUtils.java
+++ b/java/com/android/vcard/VCardUtils.java
@@ -167,23 +167,27 @@
         boolean hasPref = false;
         
         if (types != null) {
-            for (String typeString : types) {
-                if (typeString == null) {
+            for (final String typeStringOrg : types) {
+                if (typeStringOrg == null) {
                     continue;
                 }
-                typeString = typeString.toUpperCase();
-                if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
+                final String typeStringUpperCase = typeStringOrg.toUpperCase();
+                if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_PREF)) {
                     hasPref = true;
-                } else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) {
+                } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_FAX)) {
                     isFax = true;
                 } else {
-                    if (typeString.startsWith("X-") && type < 0) {
-                        typeString = typeString.substring(2);
+                    final String labelCandidate;
+                    if (typeStringUpperCase.startsWith("X-") && type < 0) {
+                        labelCandidate = typeStringOrg.substring(2);
+                    } else {
+                        labelCandidate = typeStringOrg;
                     }
-                    if (typeString.length() == 0) {
+                    if (labelCandidate.length() == 0) {
                         continue;
                     }
-                    final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
+                    // e.g. "home" -> TYPE_HOME
+                    final Integer tmp = sKnownPhoneTypeMap_StoI.get(labelCandidate.toUpperCase());
                     if (tmp != null) {
                         final int typeCandidate = tmp;
                         // TYPE_PAGER is prefered when the number contains @ surronded by
@@ -201,7 +205,7 @@
                         }
                     } else if (type < 0) {
                         type = Phone.TYPE_CUSTOM;
-                        label = typeString;
+                        label = labelCandidate;
                     }
                 }
             }
diff --git a/tests/res/raw/v21_x_param.vcf b/tests/res/raw/v21_x_param.vcf
new file mode 100644
index 0000000..e47ea5e
--- /dev/null
+++ b/tests/res/raw/v21_x_param.vcf
@@ -0,0 +1,9 @@
+BEGIN:VCARD

+VERSION:2.1

+N:Ando;Roid;

+ADR;X-custom:pobox;street

+TEL;X-CuStoMpRop:1

+TEL;custompropertywithoutx:2

+EMAIL;X-cUstomPrOperty:email@example.com

+EMAIL;CUSTOMPROPERTYWITHOUTX:email2@example.com

+END:VCARD

diff --git a/tests/src/com/android/vcard/tests/VCardImporterTests.java b/tests/src/com/android/vcard/tests/VCardImporterTests.java
index 2900106..716c866 100644
--- a/tests/src/com/android/vcard/tests/VCardImporterTests.java
+++ b/tests/src/com/android/vcard/tests/VCardImporterTests.java
@@ -1158,4 +1158,49 @@
         elem.addExpected(SipAddress.CONTENT_ITEM_TYPE)
                 .put(SipAddress.SIP_ADDRESS, "example@example.com");
     }
+
+    public void testCustomPropertyV21_Parse() {
+        mVerifier.initForImportTest(V21, R.raw.v21_x_param);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""))
+                .addExpectedNodeWithOrder("ADR", "pobox;street", Arrays.asList("pobox", "street"),
+                        new TypeSet("X-custom"))
+                .addExpectedNodeWithOrder("TEL", "1", new TypeSet("X-CuStoMpRop"))
+                .addExpectedNodeWithOrder("TEL", "2", new TypeSet("custompropertywithoutx"))
+                .addExpectedNodeWithOrder("EMAIL", "email@example.com",
+                        new TypeSet("X-cUstomPrOperty"))
+                .addExpectedNodeWithOrder("EMAIL", "email2@example.com",
+                        new TypeSet("CUSTOMPROPERTYWITHOUTX"));
+    }
+
+    public void testCustomPropertyV21() {
+        mVerifier.initForImportTest(V21, R.raw.v21_x_param);
+        final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "Ando")
+                .put(StructuredName.GIVEN_NAME, "Roid")
+                .put(StructuredName.DISPLAY_NAME, "Roid Ando");
+        elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+                .put(StructuredPostal.LABEL, "custom")
+                .put(StructuredPostal.POBOX, "pobox")
+                .put(StructuredPostal.STREET, "street")
+                .put(StructuredPostal.FORMATTED_ADDRESS, "pobox street");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "CuStoMpRop")
+                .put(Phone.NUMBER, "1");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "custompropertywithoutx")
+                .put(Phone.NUMBER, "2");
+        elem.addExpected(Email.CONTENT_ITEM_TYPE)
+                .put(Email.TYPE, Email.TYPE_CUSTOM)
+                .put(Email.LABEL, "cUstomPrOperty")
+                .put(Email.ADDRESS, "email@example.com");
+        elem.addExpected(Email.CONTENT_ITEM_TYPE)
+                .put(Email.TYPE, Email.TYPE_CUSTOM)
+                .put(Email.LABEL, "CUSTOMPROPERTYWITHOUTX")
+                .put(Email.ADDRESS, "email2@example.com");
+    }
 }