diff --git a/res/values/e_arrays.xml b/res/values/e_arrays.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a38cce56ce220eb09b4fdd4a9b123da167ba3f12
--- /dev/null
+++ b/res/values/e_arrays.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ - @string/menu_export_type_vcf_21
+ - @string/menu_export_type_vcf_30
+ - @string/menu_export_type_vcf_40
+
+
+
+ - @string/menu_export_type_vcf_21_value
+ - @string/menu_export_type_vcf_30_value
+ - @string/menu_export_type_vcf_40_value
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4c2d336f76d5c891fdce00d499af1dcaf91f3299..1d9a49b9c3f3dcc95c212bad393ea1e02a7c032a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1541,4 +1541,13 @@
-Contacts is an open source App for Android
Emergency information
+
+
+ Export vCard version
+ VCF 2.1
+ VCF 3.0
+ VCF 4.0
+ v21_generic
+ v30_generic
+ v40_generic
diff --git a/res/xml/preference_display_options.xml b/res/xml/preference_display_options.xml
index 31f2db7e1dc335bb9b5930bf8593cf7f94bd12ff..308ba49426a153461c891d2b5212e697b4cd7b41 100644
--- a/res/xml/preference_display_options.xml
+++ b/res/xml/preference_display_options.xml
@@ -111,6 +111,14 @@
android:key="export"
android:title="@string/menu_export"/>
+
+
sVCardTypeMap = new HashMap<>();
+ sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
+ sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
+ sVCardTypeMap.put(VCARD_TYPE_V40_GENERIC_STR, VCARD_TYPE_V40_GENERIC);
+
+ final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(
+ mService.getApplicationContext());
+
+ final String defVcfType = pref.getString(KEY_EXPORT_TYPE,
+ VCARD_TYPE_V40_GENERIC_STR);
+
+ final int vcardType = sVCardTypeMap.get(defVcfType);
composer = new VCardComposer(mService, vcardType, true);
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index c6fcccb8c2c9f6e63fa3afb6474c57a544774b61..bdeb35312dd7f9e3716d595e8a2013eb6380fe4a 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -30,6 +30,7 @@ import com.android.vcard.VCardInterpreter;
import com.android.vcard.VCardParser;
import com.android.vcard.VCardParser_V21;
import com.android.vcard.VCardParser_V30;
+import com.android.vcard.VCardParser_V40;
import com.android.vcard.exception.VCardException;
import com.android.vcard.exception.VCardNotSupportedException;
import com.android.vcard.exception.VCardVersionException;
@@ -135,7 +136,8 @@ public class ImportProcessor extends ProcessorBase implements VCardEntryHandler
*/
possibleVCardVersions = new int[] {
ImportVCardActivity.VCARD_VERSION_V21,
- ImportVCardActivity.VCARD_VERSION_V30
+ ImportVCardActivity.VCARD_VERSION_V30,
+ ImportVCardActivity.VCARD_VERSION_V40
};
} else {
possibleVCardVersions = new int[] {
@@ -231,9 +233,10 @@ public class ImportProcessor extends ProcessorBase implements VCardEntryHandler
// In the worst case, a user may call cancel() just before creating
// mVCardParser.
synchronized (this) {
- mVCardParser = (vcardVersion == ImportVCardActivity.VCARD_VERSION_V30 ?
- new VCardParser_V30(vcardType) :
- new VCardParser_V21(vcardType));
+ VCardParser useOldVcardParser = (vcardVersion == ImportVCardActivity.VCARD_VERSION_V30 ?
+ new VCardParser_V30(vcardType) : new VCardParser_V21(vcardType));
+ mVCardParser = (vcardVersion == ImportVCardActivity.VCARD_VERSION_V40 ?
+ new VCardParser_V40(vcardType) : useOldVcardParser);
if (isCancelled()) {
Log.i(LOG_TAG, "ImportProcessor already recieves cancel request, so " +
"send cancel request to vCard parser too.");
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index b5aa37e35c4208f2bfc81e0e54393173f06336a0..9b91f6be43bb51cda46ad86f4e0a3d4441bf2164 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -49,6 +49,7 @@ import com.android.vcard.VCardEntryCounter;
import com.android.vcard.VCardParser;
import com.android.vcard.VCardParser_V21;
import com.android.vcard.VCardParser_V30;
+import com.android.vcard.VCardParser_V40;
import com.android.vcard.VCardSourceDetector;
import com.android.vcard.exception.VCardException;
import com.android.vcard.exception.VCardNestedException;
@@ -83,6 +84,7 @@ public class ImportVCardActivity extends Activity implements ImportVCardDialogFr
/* package */ final static int VCARD_VERSION_AUTO_DETECT = 0;
/* package */ final static int VCARD_VERSION_V21 = 1;
/* package */ final static int VCARD_VERSION_V30 = 2;
+ /* package */ final static int VCARD_VERSION_V40 = 3;
private static final int REQUEST_OPEN_DOCUMENT = 100;
@@ -321,12 +323,14 @@ public class ImportVCardActivity extends Activity implements ImportVCardDialogFr
int vcardVersion = VCARD_VERSION_V21;
try {
boolean shouldUseV30 = false;
+ boolean shouldUseV40 = false;
InputStream is;
if (data != null) {
is = new ByteArrayInputStream(data);
} else {
is = resolver.openInputStream(localDataUri);
}
+
mVCardParser = new VCardParser_V21();
try {
counter = new VCardEntryCounter();
@@ -335,9 +339,11 @@ public class ImportVCardActivity extends Activity implements ImportVCardDialogFr
mVCardParser.addInterpreter(detector);
mVCardParser.parse(is);
} catch (VCardVersionException e1) {
+ Log.i(LOG_TAG, "Unable to parse as vCard v2.1, trying v3.0");
try {
is.close();
} catch (IOException e) {
+ Log.w(LOG_TAG, "Failed to close InputStream.");
}
shouldUseV30 = true;
@@ -346,6 +352,7 @@ public class ImportVCardActivity extends Activity implements ImportVCardDialogFr
} else {
is = resolver.openInputStream(localDataUri);
}
+
mVCardParser = new VCardParser_V30();
try {
counter = new VCardEntryCounter();
@@ -354,18 +361,43 @@ public class ImportVCardActivity extends Activity implements ImportVCardDialogFr
mVCardParser.addInterpreter(detector);
mVCardParser.parse(is);
} catch (VCardVersionException e2) {
- throw new VCardException("vCard with unspported version.");
+ Log.i(LOG_TAG, "Unable to parse as vCard v3.0, trying v4.0");
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Failed to close InputStream.");
+ }
+
+ shouldUseV40 = true;
+ if (data != null) {
+ is = new ByteArrayInputStream(data);
+ } else {
+ is = resolver.openInputStream(localDataUri);
+ }
+
+ mVCardParser = new VCardParser_V40();
+ try {
+ counter = new VCardEntryCounter();
+ detector = new VCardSourceDetector();
+ mVCardParser.addInterpreter(counter);
+ mVCardParser.addInterpreter(detector);
+ mVCardParser.parse(is);
+ } catch (VCardVersionException e3) {
+ throw new VCardException("vCard with unsupported version.");
+ }
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
+ Log.w(LOG_TAG, "Failed to close InputStream.");
}
}
}
- vcardVersion = shouldUseV30 ? VCARD_VERSION_V30 : VCARD_VERSION_V21;
+ int useOldVcardVersion = shouldUseV30 ? VCARD_VERSION_V30 : VCARD_VERSION_V21;
+ vcardVersion = shouldUseV40 ? VCARD_VERSION_V40 : useOldVcardVersion;
} catch (VCardNestedException e) {
Log.w(LOG_TAG, "Nested Exception is found (it may be false-positive).");
// Go through without throwing the Exception, as we may be able to detect the