am 4ee476ef: am 5e332efd: Merge change I903ce7b1 into eclair-mr2

Merge commit '4ee476ef94f7834c18ee378c788abcdb0728954f'

* commit '4ee476ef94f7834c18ee378c788abcdb0728954f':
  Fix a problem in which Android custom fields are not emitted correctly in non-Ascii languages.
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
index 3980940..09ac1fd 100644
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -1536,13 +1536,10 @@
     }
 
     public void appendAndroidSpecificProperty(final String mimeType, ContentValues contentValues) {
-        List<String> rawValueList = new ArrayList<String>();
-        rawValueList.add(mimeType);
-        final List<String> columnNameList;
         if (!sAllowedAndroidPropertySet.contains(mimeType)) {
             return;
         }
-
+        final List<String> rawValueList = new ArrayList<String>();
         for (int i = 1; i <= VCardConstants.MAX_DATA_COLUMN; i++) {
             String value = contentValues.getAsString("data" + i);
             if (value == null) {
@@ -1551,8 +1548,38 @@
             rawValueList.add(value);
         }
 
-        appendLineWithCharsetAndQPDetection(
-                VCardConstants.PROPERTY_X_ANDROID_CUSTOM, rawValueList);
+        boolean needCharset =
+            (mShouldAppendCharsetParam &&
+                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
+        boolean reallyUseQuotedPrintable =
+            (mShouldUseQuotedPrintable &&
+                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
+        mBuilder.append(VCardConstants.PROPERTY_X_ANDROID_CUSTOM);
+        if (needCharset) {
+            mBuilder.append(VCARD_PARAM_SEPARATOR);
+            mBuilder.append(mVCardCharsetParameter);
+        }
+        if (reallyUseQuotedPrintable) {
+            mBuilder.append(VCARD_PARAM_SEPARATOR);
+            mBuilder.append(VCARD_PARAM_ENCODING_QP);
+        }
+        mBuilder.append(VCARD_DATA_SEPARATOR);
+        mBuilder.append(mimeType);  // Should not be encoded.
+        for (String rawValue : rawValueList) {
+            final String encodedValue;
+            if (reallyUseQuotedPrintable) {
+                encodedValue = encodeQuotedPrintable(rawValue);
+            } else {
+                // TODO: one line may be too huge, which may be invalid in vCard 3.0
+                //        (which says "When generating a content line, lines longer than
+                //        75 characters SHOULD be folded"), though several
+                //        (even well-known) applications do not care this.
+                encodedValue = escapeCharacters(rawValue);
+            }
+            mBuilder.append(VCARD_ITEM_SEPARATOR);
+            mBuilder.append(encodedValue);
+        }
+        mBuilder.append(VCARD_END_OF_LINE);
     }
 
     public void appendLineWithCharsetAndQPDetection(final String propertyName,
@@ -1560,7 +1587,7 @@
         appendLineWithCharsetAndQPDetection(propertyName, null, rawValue);
     }
 
-    private void appendLineWithCharsetAndQPDetection(
+    public void appendLineWithCharsetAndQPDetection(
             final String propertyName, final List<String> rawValueList) {
         appendLineWithCharsetAndQPDetection(propertyName, null, rawValueList);
     }
@@ -1578,22 +1605,12 @@
 
     public void appendLineWithCharsetAndQPDetection(final String propertyName,
             final List<String> parameterList, final List<String> rawValueList) {
-        boolean needCharset = false;
-        boolean reallyUseQuotedPrintable = false;
-        for (String rawValue : rawValueList) {
-            if (!needCharset && mShouldUseQuotedPrintable &&
-                    !VCardUtils.containsOnlyPrintableAscii(rawValue)) {
-                needCharset = true;
-            }
-            if (!reallyUseQuotedPrintable &&
-                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValue)) {
-                reallyUseQuotedPrintable = true;
-            }
-            if (needCharset && reallyUseQuotedPrintable) {
-                break;
-            }
-        }
-
+        boolean needCharset =
+            (mShouldAppendCharsetParam &&
+                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
+        boolean reallyUseQuotedPrintable =
+            (mShouldUseQuotedPrintable &&
+                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
         appendLine(propertyName, parameterList, rawValueList,
                 needCharset, reallyUseQuotedPrintable);
     }
@@ -1610,8 +1627,9 @@
     }
 
     public void appendLine(final String propertyName,
-            final String rawValue, final boolean needCharset, boolean needQuotedPrintable) {
-        appendLine(propertyName, null, rawValue, needCharset, needQuotedPrintable);
+            final String rawValue, final boolean needCharset,
+            boolean reallyUseQuotedPrintable) {
+        appendLine(propertyName, null, rawValue, needCharset, reallyUseQuotedPrintable);
     }
 
     public void appendLine(final String propertyName, final List<String> parameterList,
@@ -1620,7 +1638,8 @@
     }
 
     public void appendLine(final String propertyName, final List<String> parameterList,
-            final String rawValue, final boolean needCharset, boolean needQuotedPrintable) {
+            final String rawValue, final boolean needCharset,
+            boolean reallyUseQuotedPrintable) {
         mBuilder.append(propertyName);
         if (parameterList != null && parameterList.size() > 0) {
             mBuilder.append(VCARD_PARAM_SEPARATOR);
@@ -1632,7 +1651,7 @@
         }
 
         final String encodedValue;
-        if (needQuotedPrintable) {
+        if (reallyUseQuotedPrintable) {
             mBuilder.append(VCARD_PARAM_SEPARATOR);
             mBuilder.append(VCARD_PARAM_ENCODING_QP);
             encodedValue = encodeQuotedPrintable(rawValue);
@@ -1664,14 +1683,16 @@
             mBuilder.append(VCARD_PARAM_SEPARATOR);
             mBuilder.append(mVCardCharsetParameter);
         }
+        if (needQuotedPrintable) {
+            mBuilder.append(VCARD_PARAM_SEPARATOR);
+            mBuilder.append(VCARD_PARAM_ENCODING_QP);
+        }
 
         mBuilder.append(VCARD_DATA_SEPARATOR);
         boolean first = true;
         for (String rawValue : rawValueList) {
             final String encodedValue;
             if (needQuotedPrintable) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(VCARD_PARAM_ENCODING_QP);
                 encodedValue = encodeQuotedPrintable(rawValue);
             } else {
                 // TODO: one line may be too huge, which may be invalid in vCard 3.0
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index ec569d4..e7c19cf 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -117,9 +117,6 @@
         this(detector.getEstimatedType());
     }
 
-    /**
-     * TODO: Merge detector and parser mode.
-     */
     public VCardParser_V21(int parseType) {
         super(parseType);
         if (parseType == VCardConfig.PARSE_TYPE_FOMA) {
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index 80457bb..ec75a98 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -352,6 +352,13 @@
         if (values == null) {
             return true;
         }
+        return containsOnlyPrintableAscii(Arrays.asList(values));
+    }
+
+    public static boolean containsOnlyPrintableAscii(final Collection<String> values) {
+        if (values == null) {
+            return true;
+        }
         final int asciiFirst = 0x20;
         final int asciiLast = 0x7E;  // included
         for (final String value : values) {
@@ -378,6 +385,13 @@
         if (values == null) {
             return true;
         }
+        return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values));
+    }
+
+    public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) {
+        if (values == null) {
+            return true;
+        }
         final int asciiFirst = 0x20;
         final int asciiLast = 0x7E;  // included
         for (final String value : values) {
@@ -399,32 +413,6 @@
         new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
 
     /**
-     * <P>
-     * Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
-     * </P>
-     * <P>
-     * vCard 2.1 specifies:<BR />
-     * word = &lt;any printable 7bit us-ascii except []=:., &gt;
-     * </P>
-     */
-    public static boolean isV21Word(final String value) {
-        if (TextUtils.isEmpty(value)) {
-            return true;
-        }
-        final int asciiFirst = 0x20;
-        final int asciiLast = 0x7E;  // included
-        final int length = value.length();
-        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
-            final int c = value.codePointAt(i);
-            if (!(asciiFirst <= c && c <= asciiLast) ||
-                    sUnAcceptableAsciiInV21WordSet.contains((char)c)) {
-                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.
      * 
@@ -437,6 +425,13 @@
         if (values == null) {
             return true;
         }
+        return containsOnlyAlphaDigitHyphen(Arrays.asList(values));
+    }
+
+    public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) {
+        if (values == null) {
+            return true;
+        }
         final int upperAlphabetFirst = 0x41;  // A
         final int upperAlphabetAfterLast = 0x5b;  // [
         final int lowerAlphabetFirst = 0x61;  // a
@@ -461,7 +456,33 @@
         }
         return true;
     }
-    
+
+    /**
+     * <P>
+     * Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
+     * </P>
+     * <P>
+     * vCard 2.1 specifies:<BR />
+     * word = &lt;any printable 7bit us-ascii except []=:., &gt;
+     * </P>
+     */
+    public static boolean isV21Word(final String value) {
+        if (TextUtils.isEmpty(value)) {
+            return true;
+        }
+        final int asciiFirst = 0x20;
+        final int asciiLast = 0x7E;  // included
+        final int length = value.length();
+        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
+            final int c = value.codePointAt(i);
+            if (!(asciiFirst <= c && c <= asciiLast) ||
+                    sUnAcceptableAsciiInV21WordSet.contains((char)c)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     public static String toHalfWidthString(final String orgString) {
         if (TextUtils.isEmpty(orgString)) {
             return null;
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java
index 967c22d..eea98c6 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java
@@ -18,6 +18,7 @@
 
 import android.content.ContentValues;
 import android.pim.vcard.VCardConfig;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
 import android.provider.ContactsContract.CommonDataKinds.Note;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
@@ -418,4 +419,16 @@
                 .addExpectedNode("ADR", "", new TypeSet("HOME"))
                 .addExpectedNode("NOTE", "note1\nnote2\nnote3", mContentValuesForQP);
     }
+
+    public void testAndroidCustomV21() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+        mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
+                .put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("X-ANDROID-CUSTOM",
+                        Arrays.asList(Nickname.CONTENT_ITEM_TYPE,
+                                "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC",
+                                "", "", "", "", "", "", "", "", "", "", "", "", "", ""),
+                        mContentValuesForQPAndUtf8);
+    }
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardUtilsTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardUtilsTests.java
index 592c285..9f173af 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardUtilsTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardUtilsTests.java
@@ -20,10 +20,13 @@
 
 import junit.framework.TestCase;
 
+import java.util.List;
+
 public class VCardUtilsTests extends TestCase {
     public void testContainsOnlyPrintableAscii() {
         assertTrue(VCardUtils.containsOnlyPrintableAscii((String)null));
         assertTrue(VCardUtils.containsOnlyPrintableAscii((String[])null));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii((List<String>)null));
         assertTrue(VCardUtils.containsOnlyPrintableAscii(""));
         assertTrue(VCardUtils.containsOnlyPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
         assertTrue(VCardUtils.containsOnlyPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
@@ -40,6 +43,7 @@
     public void testContainsOnlyNonCrLfPrintableAscii() {
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String)null));
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String[])null));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((List<String>)null));
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(""));
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
@@ -57,6 +61,7 @@
     public void testContainsOnlyAlphaDigitHyphen() {
         assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String)null));
         assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String[])null));
+        assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((List<String>)null));
         assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen(""));
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
         assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));