Fix missing encoding attribute on FN field.

When FN field is built from display name, the encoding attribute is missing.
Created common method to build N and FN fields so they will not be out of
sync in the future.

Bug: 7292017
Change-Id: I7faceb316b639d626a54bdd455df1e748b22face
diff --git a/java/com/android/vcard/VCardBuilder.java b/java/com/android/vcard/VCardBuilder.java
index bd1e09d..ce97f46 100644
--- a/java/com/android/vcard/VCardBuilder.java
+++ b/java/com/android/vcard/VCardBuilder.java
@@ -15,8 +15,6 @@
  */
 package com.android.vcard;
 
-import com.android.vcard.VCardUtils.PhoneNumberUtilsPort;
-
 import android.content.ContentValues;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
@@ -36,6 +34,8 @@
 import android.util.Base64;
 import android.util.Log;
 
+import com.android.vcard.VCardUtils.PhoneNumberUtilsPort;
+
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -85,7 +85,7 @@
     private static final String VCARD_DATA_PUBLIC = "PUBLIC";
 
     private static final String VCARD_PARAM_SEPARATOR = ";";
-    private static final String VCARD_END_OF_LINE = "\r\n";
+    public static final String VCARD_END_OF_LINE = "\r\n";
     private static final String VCARD_DATA_SEPARATOR = ":";
     private static final String VCARD_ITEM_SEPARATOR = ";";
     private static final String VCARD_WS = " ";
@@ -535,45 +535,13 @@
             mBuilder.append(encodedFormattedname);
             mBuilder.append(VCARD_END_OF_LINE);
         } else if (!TextUtils.isEmpty(displayName)) {
-            final boolean reallyUseQuotedPrintableToDisplayName =
-                (!mRefrainsQPToNameProperties &&
-                        !VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName));
-            final String encodedDisplayName =
-                    reallyUseQuotedPrintableToDisplayName ?
-                            encodeQuotedPrintable(displayName) :
-                                escapeCharacters(displayName);
 
             // N
-            mBuilder.append(VCardConstants.PROPERTY_N);
-            if (shouldAppendCharsetParam(displayName)) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            if (reallyUseQuotedPrintableToDisplayName) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(VCARD_PARAM_ENCODING_QP);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(encodedDisplayName);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_END_OF_LINE);
+            buildSinglePartNameField(VCardConstants.PROPERTY_N, displayName);
 
             // FN
-            mBuilder.append(VCardConstants.PROPERTY_FN);
+            buildSinglePartNameField(VCardConstants.PROPERTY_FN, displayName);
 
-            // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
-            //       when it would be useful or necessary for external importers,
-            //       assuming the external importer allows this vioration of the spec.
-            if (shouldAppendCharsetParam(displayName)) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(encodedDisplayName);
-            mBuilder.append(VCARD_END_OF_LINE);
         } else if (VCardConfig.isVersion30(mVCardType)) {
             appendLine(VCardConstants.PROPERTY_N, "");
             appendLine(VCardConstants.PROPERTY_FN, "");
@@ -585,6 +553,36 @@
         return this;
     }
 
+    private void buildSinglePartNameField(String property, String part) {
+        final boolean reallyUseQuotedPrintable =
+                (!mRefrainsQPToNameProperties &&
+                        !VCardUtils.containsOnlyNonCrLfPrintableAscii(part));
+        final String encodedPart = reallyUseQuotedPrintable ?
+                encodeQuotedPrintable(part) :
+                escapeCharacters(part);
+
+        mBuilder.append(property);
+
+        // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
+        //       when it would be useful or necessary for external importers,
+        //       assuming the external importer allows this vioration of the spec.
+        if (shouldAppendCharsetParam(part)) {
+            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(encodedPart);
+        mBuilder.append(VCARD_ITEM_SEPARATOR);
+        mBuilder.append(VCARD_ITEM_SEPARATOR);
+        mBuilder.append(VCARD_ITEM_SEPARATOR);
+        mBuilder.append(VCARD_ITEM_SEPARATOR);
+        mBuilder.append(VCARD_END_OF_LINE);
+    }
+
     /**
      * Emits SOUND;IRMC, SORT-STRING, and de-fact values for phonetic names like X-PHONETIC-FAMILY.
      */
diff --git a/tests/src/com/android/vcard/tests/VCardBuilderTest.java b/tests/src/com/android/vcard/tests/VCardBuilderTest.java
new file mode 100644
index 0000000..e86ca33
--- /dev/null
+++ b/tests/src/com/android/vcard/tests/VCardBuilderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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
+ */
+
+package com.android.vcard.tests;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+
+import com.android.vcard.VCardBuilder;
+import com.android.vcard.VCardConfig;
+import com.google.android.collect.Lists;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+/**
+ * Unit test for VCardBuilder.
+ */
+public class VCardBuilderTest extends TestCase {
+
+    public void testVCardNameFieldFromDisplayName() {
+        final ArrayList<ContentValues> contentList = Lists.newArrayList();
+
+        final ContentValues values = new ContentValues();
+        values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "ने");
+        contentList.add(values);
+
+        final VCardBuilder builder = new VCardBuilder(VCardConfig.VCARD_TYPE_DEFAULT);
+        builder.appendNameProperties(contentList);
+        final String actual = builder.toString();
+
+        final String expectedCommon = ";CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:" +
+                "=E0=A4=A8=E0=A5=87;;;;";
+
+        final String expectedName = "N" + expectedCommon;
+        final String expectedFullName = "FN" + expectedCommon;
+
+        assertTrue("Actual value:\n" + actual + " expected to contain\n" + expectedName +
+                "\nbut does not.", actual.contains(expectedName));
+        assertTrue("Actual value:\n" + actual + " expected to contain\n" + expectedFullName +
+                "\nbut does not.", actual.contains(expectedFullName));
+    }
+}
+
diff --git a/tests/src/com/android/vcard/tests/VCardTestRunner.java b/tests/src/com/android/vcard/tests/VCardTestRunner.java
index e8ccae9..a220a58 100644
--- a/tests/src/com/android/vcard/tests/VCardTestRunner.java
+++ b/tests/src/com/android/vcard/tests/VCardTestRunner.java
@@ -27,6 +27,7 @@
     @Override
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(VCardBuilderTest.class);
         suite.addTestSuite(VCardEntryTests.class);
         suite.addTestSuite(VCardParserTests.class);
         suite.addTestSuite(VCardUtilsTests.class);
@@ -42,4 +43,4 @@
     public ClassLoader getLoader() {
         return VCardTestRunner.class.getClassLoader();
     }
-}
\ No newline at end of file
+}