- Make Contacts app use new vCard handling codes
- Enable "multiple vCard selection"
Internal issue id: 2030674
diff --git a/res/values/config.xml b/res/values/config.xml
index 11aec0f..38c3e53 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -43,10 +43,13 @@
<!-- How long to vibrate (in msec), if dialer key vibration is enabled. -->
<integer name="config_dialer_key_vibrate_duration">40</integer>
- <!-- The type of VCard for export. Without specifying this, generic VCard will be emitted.
- If you want to let the app emit VCard specific to some vendor (like DoCoMo),
- please specify the type.-->
- <string name="config_export_vcard_type"></string>
+ <!-- The type of vcard for improt. If the vcard importer cannot guess the exact type
+ of a vCard type, the improter uses this type. -->
+ <string name="config_import_vcard_type">default</string>
+
+ <!-- The type of VCard for export. If you want to let the app emit vCard which is
+ specific to some vendor (like DoCoMo), specify this type (e.g. "docomo") -->
+ <string name="config_export_vcard_type">default</string>
<!-- Directory in which exported VCard file is stored -->
<string name="config_export_dir">/sdcard</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 050abf9..033d3b9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -697,11 +697,14 @@
<!-- Action string for selecting SD Card for importing contacts -->
<string name="import_from_sdcard">SD Card</string>
- <!-- "Import all VCard files" -->
- <string name="import_all_vcard_string">Import all VCard files</string>
+ <!-- "Import one vCard file" -->
+ <string name="import_one_vcard_string">Import one vCard file</string>
- <!-- "Import one VCard file" -->
- <string name="import_one_vcard_string">Import one VCard file</string>
+ <!-- "Import more than one vCard -->
+ <string name="import_multiple_vcard_string">Import multiple vCard files</string>
+
+ <!-- "Import all vCard files" -->
+ <string name="import_all_vcard_string">Import all vCard files</string>
<!-- Dialog message shown when searching VCard data from SD Card -->
<string name="searching_vcard_message">Searching for VCard data on VCard</string>
@@ -713,7 +716,7 @@
<string name="scanning_sdcard_failed_message">Scanning SD Card failed</string>
<!-- The failed reason: "I/O Error" -->
- <string name="fail_reason_io_error">I/O Error</string>
+ <string name="fail_reason_io_error">I/O Error (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
<!-- The failed reason: "Failed to parse VCard data" -->
<string name="fail_reason_vcard_parse_error">Failed to parse VCard with some unexpected reason</string>
@@ -727,12 +730,20 @@
<!-- The failed reason: "There is no valid VCard entry in the file(s)" -->
<string name="fail_reason_no_vcard_entry">No valid VCard entry found for your selection</string>
+ <!-- The failed reason: "One or more files failed to be imported." -->
+ <string name="fail_reason_failed_to_read_files">One or more files failed to be imported.</string>
+
<!-- Dialog title shown when a user is asked to select VCard file -->
<string name="select_vcard_title">Select VCard file</string>
<!-- Dialog message shown when a user is asked to choose VCard file -->
<string name="select_vcard_message">Please select a VCard file to import</string>
+ <!-- The message shown while reading a vCard file/entry. The first argument is like
+ "Reading VCard" or "Reading VCard files" and the second is the display name of the current
+ data being parsed -->
+ <string name="progress_shower_message">%s\n%s</string>
+
<!-- Dialog title shown when reading VCard data -->
<string name="reading_vcard_title">Reading VCard</string>
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
index b1dd11a..725c65b 100644
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -50,10 +50,12 @@
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
import java.util.Vector;
@@ -91,7 +93,6 @@
private ProgressDialog mProgressDialog;
private Handler mHandler = new Handler();
- private boolean mLastNameComesBeforeFirstName;
private class CancelListener
implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
@@ -129,13 +130,16 @@
private class VCardReadThread extends Thread
implements DialogInterface.OnCancelListener {
- private String mCanonicalPath;
- private List<VCardFile> mVCardFileList;
private ContentResolver mResolver;
private VCardParser_V21 mVCardParser;
private boolean mCanceled;
private PowerManager.WakeLock mWakeLock;
+ private String mCanonicalPath;
+ // For reading multiple files.
+ private List<VCardFile> mVCardFileList;
+ private List<String> mErrorFileNameList;
+
public VCardReadThread(String canonicalPath) {
mCanonicalPath = canonicalPath;
mVCardFileList = null;
@@ -145,6 +149,7 @@
public VCardReadThread(List<VCardFile> vcardFileList) {
mCanonicalPath = null;
mVCardFileList = vcardFileList;
+ mErrorFileNameList = new ArrayList<String>();
init();
}
@@ -167,12 +172,13 @@
@Override
public void run() {
+ boolean shouldCallFinish = true;
mWakeLock.acquire();
// Some malicious vCard data may make this thread broken
// (e.g. OutOfMemoryError).
// Even in such cases, some should be done.
try {
- if (mCanonicalPath != null) {
+ if (mCanonicalPath != null) { // Read one file
mProgressDialog.setProgressNumberFormat("");
mProgressDialog.setProgress(0);
@@ -190,13 +196,13 @@
boolean result;
try {
result = readOneVCardFile(mCanonicalPath,
- VCardConfig.DEFAULT_CHARSET, builderCollection, null, true);
+ VCardConfig.DEFAULT_CHARSET, builderCollection, null, true, null);
} catch (VCardNestedException e) {
try {
// Assume that VCardSourceDetector was able to detect the source.
// Try again with the detector.
result = readOneVCardFile(mCanonicalPath,
- VCardConfig.DEFAULT_CHARSET, counter, detector, false);
+ VCardConfig.DEFAULT_CHARSET, counter, detector, false, null);
} catch (VCardNestedException e2) {
result = false;
Log.e(LOG_TAG, "Must not reach here. " + e2);
@@ -208,6 +214,7 @@
time + " ms");
}
if (!result) {
+ shouldCallFinish = false;
return;
}
@@ -216,12 +223,13 @@
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(counter.getCount());
String charset = detector.getEstimatedCharset();
- doActuallyReadOneVCard(charset, true, detector);
- } else {
+ doActuallyReadOneVCard(mCanonicalPath, charset, true, detector, null);
+ } else { // Read multiple files.
mProgressDialog.setProgressNumberFormat(
getString(R.string.reading_vcard_files));
mProgressDialog.setMax(mVCardFileList.size());
mProgressDialog.setProgress(0);
+
for (VCardFile vcardFile : mVCardFileList) {
if (mCanceled) {
return;
@@ -231,60 +239,80 @@
VCardSourceDetector detector = new VCardSourceDetector();
try {
if (!readOneVCardFile(canonicalPath, VCardConfig.DEFAULT_CHARSET,
- detector, null, true)) {
+ detector, null, true, mErrorFileNameList)) {
continue;
}
} catch (VCardNestedException e) {
// Assume that VCardSourceDetector was able to detect the source.
}
String charset = detector.getEstimatedCharset();
- doActuallyReadOneVCard(charset, false, detector);
+ doActuallyReadOneVCard(canonicalPath,
+ charset, false, detector, mErrorFileNameList);
mProgressDialog.incrementProgressBy(1);
}
}
} finally {
mWakeLock.release();
mProgressDialog.dismiss();
- finish();
+ // finish() is called via ErrorDisplayer() on failure.
+ if (shouldCallFinish) {
+ if (mErrorFileNameList == null || mErrorFileNameList.isEmpty()) {
+ finish();
+ } else {
+ StringBuilder builder = new StringBuilder();
+ boolean first = true;
+ for (String fileName : mErrorFileNameList) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append(fileName);
+ }
+
+ mHandler.post(new ErrorDisplayer(
+ getString(R.string.fail_reason_failed_to_read_files,
+ builder.toString())));
+ }
+ }
}
}
- private void doActuallyReadOneVCard(String charset, boolean doIncrementProgress,
- VCardSourceDetector detector) {
+ private boolean doActuallyReadOneVCard(String canonicalPath,
+ String charset, boolean showEntryParseProgress,
+ VCardSourceDetector detector, List<String> errorFileNameList) {
final Context context = ImportVCardActivity.this;
VCardDataBuilder builder;
- int nameOrderType =
- (mLastNameComesBeforeFirstName ?
- VCardConfig.NAME_ORDER_TYPE_JAPANESE :
- VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ final String currentLanguage = Locale.getDefault().getLanguage();
+ int vcardType = VCardConfig.getVCardTypeFromString(
+ context.getString(R.string.config_import_vcard_type));
if (charset != null) {
- builder = new VCardDataBuilder(charset,
- charset,
- false,
- nameOrderType);
+ builder = new VCardDataBuilder(charset, charset, false, vcardType);
} else {
charset = VCardConfig.DEFAULT_CHARSET;
- builder = new VCardDataBuilder(null,
- null,
- false,
- nameOrderType);
+ builder = new VCardDataBuilder(null, null, false, vcardType);
}
builder.addEntryHandler(new EntryCommitter(mResolver));
- builder.addEntryHandler(new ProgressShower(mProgressDialog,
- context.getString(R.string.reading_vcard_message),
- mHandler,
- doIncrementProgress));
+ if (showEntryParseProgress) {
+ builder.addEntryHandler(new ProgressShower(mProgressDialog,
+ context.getString(R.string.reading_vcard_message),
+ ImportVCardActivity.this,
+ mHandler));
+ }
try {
- readOneVCardFile(mCanonicalPath, charset, builder, detector, false);
+ if (!readOneVCardFile(canonicalPath, charset, builder, detector, false, null)) {
+ return false;
+ }
} catch (VCardNestedException e) {
Log.e(LOG_TAG, "Never reach here.");
}
+ return true;
}
private boolean readOneVCardFile(String canonicalPath, String charset,
VCardBuilder builder, VCardSourceDetector detector,
- boolean throwNestedException)
+ boolean throwNestedException, List<String> errorFileNameList)
throws VCardNestedException {
FileInputStream is;
try {
@@ -319,22 +347,34 @@
mProgressDialog.dismiss();
- mHandler.post(new ErrorDisplayer(
- getString(R.string.fail_reason_io_error) +
- " (" + e.getMessage() + ")"));
+ if (errorFileNameList != null) {
+ errorFileNameList.add(canonicalPath);
+ } else {
+ mHandler.post(new ErrorDisplayer(
+ getString(R.string.fail_reason_io_error,
+ e.getMessage())));
+ }
return false;
} catch (VCardNotSupportedException e) {
if ((e instanceof VCardNestedException) && throwNestedException) {
throw (VCardNestedException)e;
}
- mHandler.post(new ErrorDisplayer(
- getString(R.string.fail_reason_vcard_not_supported_error) +
- " (" + e.getMessage() + ")"));
+ if (errorFileNameList != null) {
+ errorFileNameList.add(canonicalPath);
+ } else {
+ mHandler.post(new ErrorDisplayer(
+ getString(R.string.fail_reason_vcard_not_supported_error) +
+ " (" + e.getMessage() + ")"));
+ }
return false;
} catch (VCardException e) {
- mHandler.post(new ErrorDisplayer(
- getString(R.string.fail_reason_vcard_parse_error) +
- " (" + e.getMessage() + ")"));
+ if (errorFileNameList != null) {
+ errorFileNameList.add(canonicalPath);
+ } else {
+ mHandler.post(new ErrorDisplayer(
+ getString(R.string.fail_reason_vcard_parse_error) +
+ " (" + e.getMessage() + ")"));
+ }
return false;
}
return true;
@@ -350,9 +390,11 @@
private class ImportTypeSelectedListener implements
DialogInterface.OnClickListener {
- public static final int IMPORT_ALL = 0;
- public static final int IMPORT_ONE = 1;
-
+ public static final int IMPORT_ONE = 0;
+ public static final int IMPORT_MULTIPLE = 1;
+ public static final int IMPORT_ALL = 2;
+ public static final int IMPORT_TYPE_SIZE = 3;
+
private List<VCardFile> mVCardFileList;
private int mCurrentIndex;
@@ -362,10 +404,15 @@
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
- if (mCurrentIndex == IMPORT_ALL) {
- importAllVCardFromSDCard(mVCardFileList);
- } else {
- showVCardFileSelectDialog(mVCardFileList);
+ switch (mCurrentIndex) {
+ case IMPORT_ALL:
+ importMultipleVCardFromSDCard(mVCardFileList);
+ break;
+ case IMPORT_MULTIPLE:
+ showVCardFileSelectDialog(mVCardFileList, true);
+ break;
+ default:
+ showVCardFileSelectDialog(mVCardFileList, false);
}
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
finish();
@@ -375,23 +422,58 @@
}
}
- private class VCardSelectedListener implements DialogInterface.OnClickListener {
+
+ private class VCardSelectedListener implements
+ DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener {
private List<VCardFile> mVCardFileList;
private int mCurrentIndex;
+ private Set<Integer> mSelectedIndexSet;
- public VCardSelectedListener(List<VCardFile> vcardFileList) {
+ public VCardSelectedListener(
+ List<VCardFile> vcardFileList, boolean multipleSelect) {
mVCardFileList = vcardFileList;
mCurrentIndex = 0;
+ if (multipleSelect) {
+ mSelectedIndexSet = new HashSet<Integer>();
+ }
}
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
- importOneVCardFromSDCard(mVCardFileList.get(mCurrentIndex).getCanonicalPath());
+ if (mSelectedIndexSet != null) {
+ List<VCardFile> selectedVCardFileList = new ArrayList<VCardFile>();
+ int size = mVCardFileList.size();
+ // We'd like to sort the files by its index, so we do not use Set iterator.
+ for (int i = 0; i < size; i++) {
+ if (mSelectedIndexSet.contains(i)) {
+ selectedVCardFileList.add(mVCardFileList.get(i));
+ }
+ }
+ importMultipleVCardFromSDCard(selectedVCardFileList);
+ } else {
+ importOneVCardFromSDCard(mVCardFileList.get(mCurrentIndex).getCanonicalPath());
+ }
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
finish();
} else {
// Some file is selected.
mCurrentIndex = which;
+ if (mSelectedIndexSet != null) {
+ if (mSelectedIndexSet.contains(which)) {
+ mSelectedIndexSet.remove(which);
+ } else {
+ mSelectedIndexSet.add(which);
+ }
+ }
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ if (mSelectedIndexSet == null || (mSelectedIndexSet.contains(which) == isChecked)) {
+ Log.e(LOG_TAG, String.format("Inconsist state in index %d (%s)", which,
+ mVCardFileList.get(which).getCanonicalPath()));
+ } else {
+ onClick(dialog, which);
}
}
}
@@ -484,14 +566,15 @@
return;
} else if (context.getResources().getBoolean(
R.bool.config_import_all_vcard_from_sdcard_automatically)) {
- importAllVCardFromSDCard(mVCardFiles);
+ importMultipleVCardFromSDCard(mVCardFiles);
} else if (size == 1) {
importOneVCardFromSDCard(mVCardFiles.get(0).getCanonicalPath());
} else if (context.getResources().getBoolean(
R.bool.config_allow_users_select_all_vcard_import)) {
showSelectImportTypeDialog(mVCardFiles);
} else {
- showVCardFileSelectDialog(mVCardFiles);
+ // Let a user to select one vCard file.
+ showVCardFileSelectDialog(mVCardFiles, false);
}
}
});
@@ -539,13 +622,13 @@
}
- private void importOneVCardFromSDCard(String canonicalPath) {
+ private void importOneVCardFromSDCard(final String canonicalPath) {
VCardReadThread thread = new VCardReadThread(canonicalPath);
showReadingVCardDialog(thread);
thread.start();
}
- private void importAllVCardFromSDCard(List<VCardFile> vcardFileList) {
+ private void importMultipleVCardFromSDCard(final List<VCardFile> vcardFileList) {
VCardReadThread thread = new VCardReadThread(vcardFileList);
showReadingVCardDialog(thread);
thread.start();
@@ -561,20 +644,23 @@
.setOnCancelListener(mCancelListener)
.setNegativeButton(android.R.string.cancel, mCancelListener);
- String[] items = new String[2];
- items[ImportTypeSelectedListener.IMPORT_ALL] =
- getString(R.string.import_all_vcard_string);
+ String[] items = new String[ImportTypeSelectedListener.IMPORT_TYPE_SIZE];
items[ImportTypeSelectedListener.IMPORT_ONE] =
getString(R.string.import_one_vcard_string);
+ items[ImportTypeSelectedListener.IMPORT_MULTIPLE] =
+ getString(R.string.import_multiple_vcard_string);
+ items[ImportTypeSelectedListener.IMPORT_ALL] =
+ getString(R.string.import_all_vcard_string);
builder.setSingleChoiceItems(items,
- ImportTypeSelectedListener.IMPORT_ALL, listener);
+ ImportTypeSelectedListener.IMPORT_ONE, listener);
builder.show();
}
- private void showVCardFileSelectDialog(List<VCardFile> vcardFileList) {
+ private void showVCardFileSelectDialog(
+ List<VCardFile> vcardFileList, boolean multipleSelect) {
int size = vcardFileList.size();
- DialogInterface.OnClickListener listener =
- new VCardSelectedListener(vcardFileList);
+ VCardSelectedListener listener =
+ new VCardSelectedListener(vcardFileList, multipleSelect);
AlertDialog.Builder builder =
new AlertDialog.Builder(this)
.setTitle(R.string.select_vcard_title)
@@ -600,7 +686,11 @@
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
items[i] = stringBuilder;
}
- builder.setSingleChoiceItems(items, 0, listener);
+ if (multipleSelect) {
+ builder.setMultiChoiceItems(items, (boolean[])null, listener);
+ } else {
+ builder.setSingleChoiceItems(items, 0, listener);
+ }
builder.show();
}
@@ -619,9 +709,6 @@
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
- mLastNameComesBeforeFirstName = getResources().getBoolean(
- com.android.internal.R.bool.config_lastname_comes_before_firstname);
-
startImportVCardFromSdCard();
}
diff --git a/src/com/android/contacts/ProgressShower.java b/src/com/android/contacts/ProgressShower.java
index 9498569..c1a2493 100644
--- a/src/com/android/contacts/ProgressShower.java
+++ b/src/com/android/contacts/ProgressShower.java
@@ -16,6 +16,7 @@
package com.android.contacts;
import android.app.ProgressDialog;
+import android.content.Context;
import android.os.Handler;
import android.pim.vcard.ContactStruct;
import android.pim.vcard.EntryHandler;
@@ -25,10 +26,10 @@
public class ProgressShower implements EntryHandler {
public static final String LOG_TAG = "vcard.ProgressShower";
+ private final Context mContext;
private final Handler mHandler;
private final ProgressDialog mProgressDialog;
private final String mProgressMessage;
- private final boolean mIncrementProgress;
private long mTime;
@@ -40,24 +41,25 @@
}
public void run() {
- mProgressDialog.setMessage(mProgressMessage + "\n" +
- mContact.displayString());
- if (mIncrementProgress) {
- mProgressDialog.incrementProgressBy(1);
- }
+ mProgressDialog.setMessage( mProgressMessage + "\n" +
+ mContact.getDisplayName());
+ mProgressDialog.incrementProgressBy(1);
}
}
public ProgressShower(ProgressDialog progressDialog,
String progressMessage,
- Handler handler,
- boolean incrementProgress) {
+ Context context,
+ Handler handler) {
+ mContext = context;
mHandler = handler;
mProgressDialog = progressDialog;
mProgressMessage = progressMessage;
- mIncrementProgress = incrementProgress;
}
-
+
+ public void onParsingStart() {
+ }
+
public void onEntryCreated(ContactStruct contactStruct) {
long start = System.currentTimeMillis();
@@ -66,8 +68,9 @@
if (mHandler != null) {
mHandler.post(new ShowProgressRunnable(contactStruct));
} else {
- mProgressDialog.setMessage(mProgressMessage + "\n" +
- contactStruct.displayString());
+ mProgressDialog.setMessage(mContext.getString(R.string.progress_shower_message,
+ mProgressMessage,
+ contactStruct.getDisplayName()));
}
}
}
@@ -75,10 +78,10 @@
mTime += System.currentTimeMillis() - start;
}
- public void onFinal() {
+ public void onParsingEnd() {
if (VCardConfig.showPerformanceLog()) {
Log.d(LOG_TAG,
- String.format("Time to progress a dialog: %ld ms", mTime));
+ String.format("Time to progress a dialog: %d ms", mTime));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/contacts/VCardExporter.java b/src/com/android/contacts/VCardExporter.java
index 2b3c4eb..2e9ab2c 100644
--- a/src/com/android/contacts/VCardExporter.java
+++ b/src/com/android/contacts/VCardExporter.java
@@ -17,35 +17,20 @@
import android.app.AlertDialog;
import android.app.ProgressDialog;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
import android.os.Handler;
import android.os.PowerManager;
-import android.provider.Contacts;
-import android.provider.Contacts.People;
-import android.syncml.pim.PropertyNode;
-import android.telephony.TelephonyManager;
+import android.pim.vcard.VCardComposer;
import android.text.TextUtils;
-import android.util.CharsetUtils;
import android.util.Log;
-import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
public class VCardExporter {
@@ -59,7 +44,7 @@
private final int mFileIndexMinimum;
private final int mFileIndexMaximum;
private final String mFileNameExtension;
- private final String mVCardType;
+ private final String mVCardTypeStr;
private final Set<String> mExtensionsToConsider;
private Context mParentContext;
@@ -114,7 +99,7 @@
@Override
public void run() {
mWakeLock.acquire();
- VCardExporterImpl exporterImpl = null;
+ VCardComposer composer = null;
try {
OutputStream outputStream = null;
try {
@@ -126,43 +111,41 @@
return;
}
- TelephonyManager telephonyManager =
- (TelephonyManager)mParentContext.getSystemService(
- Context.TELEPHONY_SERVICE);
+ composer = new VCardComposer(mParentContext, mVCardTypeStr, true);
+ // composer = new VCardComposer(mParentContext,
+ // VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8, true);
+ composer.addHandler(composer.new HandlerForOutputStream(outputStream));
- exporterImpl = new VCardExporterImpl(mParentContext.getContentResolver(),
- outputStream, mVCardType);
-
- if (!exporterImpl.init()) {
+ if (!composer.init()) {
String reason = getString(R.string.fail_reason_could_not_initialize_exporter,
- exporterImpl.getErrorReason());
+ composer.getErrorReason());
mParentHandler.post(new ErrorMessageDisplayRunnable(reason));
return;
}
- int size = exporterImpl.getCount();
+ int size = composer.getCount();
mProgressDialog.setProgressNumberFormat(
getString(R.string.exporting_contact_list_progress));
mProgressDialog.setMax(size);
mProgressDialog.setProgress(0);
- while (!exporterImpl.isAfterLast()) {
+ while (!composer.isAfterLast()) {
if (mCanceled) {
return;
}
- if (!exporterImpl.exportOneContactData()) {
+ if (!composer.createOneEntry()) {
Log.e(LOG_TAG, "Failed to read a contact.");
String reason = getString(R.string.fail_reason_error_occurred_during_export,
- exporterImpl.getErrorReason());
+ composer.getErrorReason());
mParentHandler.post(new ErrorMessageDisplayRunnable(reason));
return;
}
mProgressDialog.incrementProgressBy(1);
}
} finally {
- if (exporterImpl != null) {
- exporterImpl.terminate();
+ if (composer != null) {
+ composer.terminate();
}
mWakeLock.release();
mProgressDialog.dismiss();
@@ -192,7 +175,7 @@
mFileNamePrefix = getString(R.string.config_export_file_prefix);
mFileNameSuffix = getString(R.string.config_export_file_suffix);
mFileNameExtension = getString(R.string.config_export_file_extension);
- mVCardType = getString(R.string.config_export_vcard_type);
+ mVCardTypeStr = getString(R.string.config_export_vcard_type);
mExtensionsToConsider = new HashSet<String>();
mExtensionsToConsider.add(mFileNameExtension);
@@ -329,1346 +312,4 @@
private String getString(int resId) {
return mParentContext.getString(resId);
}
-}
-
-// TODO: This class should be splitted into two parts; exporter part and composer part.
-class VCardExporterImpl {
- private static final String LOG_TAG = "VCardExporterImpl";
-
- /* Type of exporting VCard. */
- // General VCard. Use UTF-8 and do not care vendor specific things.
- public static int VCARD_TYPE_GENERIC = 0;
- // VCard format used in DoCoMo. Shift_Jis is used as charset.
- public static int VCARD_TYPE_DOCOMO = 1;
- public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
-
- private static final String VCARD_PROPERTY_ADR = "ADR";
- private static final String VCARD_PROPERTY_BEGIN = "BEGIN";
- private static final String VCARD_PROPERTY_EMAIL = "EMAIL";
- private static final String VCARD_PROPERTY_END = "END";
- private static final String VCARD_PROPERTY_NAME = "N";
- private static final String VCARD_PROPERTY_NOTE = "NOTE";
- private static final String VCARD_PROPERTY_ORG = "ORG";
- private static final String VCARD_PROPERTY_SOUND = "SOUND";
- private static final String VCARD_PROPERTY_TEL = "TEL";
- private static final String VCARD_PROPERTY_TITLE = "TITLE";
- private static final String VCARD_PROPERTY_PHOTO = "PHOTO";
- private static final String VCARD_PROPERTY_VERSION = "VERSION";
- private static final String VCARD_PROPERTY_BDAY = "BDAY";
- private static final String VCARD_PROPERTY_URL = "URL";
-
- // Properties for DoCoMo vCard.
- private static final String VCARD_PROPERTY_X_CLASS = "X-CLASS";
- private static final String VCARD_PROPERTY_X_REDUCTION = "X-REDUCTION";
- private static final String VCARD_PROPERTY_X_NO = "X-NO";
- private static final String VCARD_PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
-
- private static final String VCARD_DATA_VCARD = "VCARD";
- private static final String VCARD_DATA_VERSION_V21 = "2.1";
- private static final String VCARD_DATA_PUBLIC = "PUBLIC";
-
- private static final String VCARD_ATTR_SEPARATOR = ";";
- private static final String VCARD_COL_SEPARATOR = "\r\n";
- private static final String VCARD_DATA_SEPARATOR = ":";
- private static final String VCARD_ITEM_SEPARATOR = ";";
- private static final String VCARD_WS = " ";
-
- private static final String VCARD_ATTR_VOICE = "VOICE";
- private static final String VCARD_ATTR_CELL = "CELL";
- private static final String VCARD_ATTR_WORK = "WORK";
- private static final String VCARD_ATTR_HOME = "HOME";
- private static final String VCARD_ATTR_FAX = "FAX";
- private static final String VCARD_ATTR_INTERNET = "INTERNET";
- private static final String VCARD_ATTR_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";
- private static final String VCARD_ATTR_ENCODING_BASE64_V21 = "ENCODING=BASE64";
- // This is just a reminder: in VCard 3.0, do not use the string "BASE64"
- @SuppressWarnings("unused")
- private static final String VCARD_ATTR_ENCODING_BASE64_V30 = "ENCODING=b";
-
- // DoCoMo specific attribute.Used with "SOUND" property.
- private static final String VCARD_ATTR_X_IRMC_N = "X-IRMC-N";
-
- private static final String SHIFT_JIS = "SHIFT_JIS";
-
- private Cursor mCursor;
- private int mIdColumn;
- private int mNameColumn;
- private int mNotesColumn;
- private int mPhoneticNameColumn;
- private ContentResolver mContentResolver;
-
- private int mVCardType;
- private String mCharsetString;
- private static String mVCardAttributeCharset;
- private OutputStream mOutputStream; // mWriter will close this.
- private Writer mWriter;
- private boolean mTerminateIsCalled;
-
- private String mErrorReason = "No error";
-
- /**
- * @param resolver
- * @param outputStream close() must not be called outside.
- * @param vcardType
- */
- public VCardExporterImpl(ContentResolver resolver, OutputStream outputStream, int vcardType) {
- mContentResolver = resolver;
- mOutputStream = outputStream;
-
- mVCardType = vcardType;
- if (vcardType == VCARD_TYPE_DOCOMO) {
- mCharsetString = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
- mVCardAttributeCharset = "CHARSET=" + SHIFT_JIS;
- } else {
- mCharsetString = "UTF-8";
- mVCardAttributeCharset = "CHARSET=UTF-8";
- }
- }
-
- public VCardExporterImpl(ContentResolver resolver, OutputStream outputStream, String vcardType) {
- this(resolver, outputStream,
- (vcardType.equalsIgnoreCase(VCARD_TYPE_STRING_DOCOMO) ?
- VCARD_TYPE_DOCOMO : VCARD_TYPE_GENERIC));
- }
-
- /**
- * @return Returns true when initialization is successful and all the other methods are
- * available. Returns false otherwise.
- */
- public boolean init() {
- try {
- mWriter = new BufferedWriter(
- new OutputStreamWriter(mOutputStream, mCharsetString));
- } catch (UnsupportedEncodingException e1) {
- Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString);
- mErrorReason = "Encoding is not supported (usually this does not happen!): " +
- mCharsetString;
- return false;
- }
-
- final String[] projection = new String[] {
- People._ID,
- People.NAME,
- People.NOTES,
- People.PHONETIC_NAME,
- };
-
- mCursor = mContentResolver.query(People.CONTENT_URI, projection, null, null, null);
- if (mCursor == null || !mCursor.moveToFirst()) {
- if (mCursor != null) {
- try {
- mCursor.close();
- } catch (SQLiteException e) {
- }
- mCursor = null;
- }
- mErrorReason = "Getting database information failed.";
- return false;
- }
-
- mIdColumn = mCursor.getColumnIndex(People._ID);
- mNameColumn = mCursor.getColumnIndex(People.NAME);
- mNotesColumn = mCursor.getColumnIndex(People.NOTES);
- mPhoneticNameColumn = mCursor.getColumnIndex(People.PHONETIC_NAME);
-
- if (mVCardType == VCARD_TYPE_DOCOMO) {
- try {
- mWriter.write(convertContactToVCard(new ContactData()));
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException occurred during exportOneContactData: " +
- e.getMessage());
- mErrorReason = "IOException occurred: " + e.getMessage();
- }
- }
-
- return true;
- }
-
- @Override
- public void finalize() {
- if (!mTerminateIsCalled) {
- terminate();
- }
- }
-
- public void terminate() {
- if (mWriter != null) {
- try {
- // Flush and sync the data so that a user is able to pull the SDCard just after the
- // export.
- mWriter.flush();
- if (mOutputStream != null && mOutputStream instanceof FileOutputStream) {
- try {
- ((FileOutputStream)mOutputStream).getFD().sync();
- } catch (IOException e) {
- }
- }
- mWriter.close();
- } catch (IOException e) {
- }
- }
- if (mCursor != null) {
- try {
- mCursor.close();
- } catch (SQLiteException e) {
- }
- mCursor = null;
- }
- }
-
- public int getCount() {
- if (mCursor == null) {
- return 0;
- }
- return mCursor.getCount();
- }
-
- public boolean isAfterLast() {
- if (mCursor == null) {
- return false;
- }
- return mCursor.isAfterLast();
- }
-
- public boolean exportOneContactData() {
- if (mCursor == null || mCursor.isAfterLast()) {
- mErrorReason = "Not initialized or database has some problem.";
- return false;
- }
- String name = null;
- try {
- ContactData contactData = new ContactData();
- int personId = mCursor.getInt(mIdColumn);
- name = contactData.mName = mCursor.getString(mNameColumn);
- contactData.mNote = mCursor.getString(mNotesColumn);
- contactData.mPhoneticName = mCursor.getString(mPhoneticNameColumn);
-
- readAllPhones(contactData, personId);
- readAllAddresses(contactData, personId);
- readAllOrgs(contactData, personId);
- readAllPhotos(contactData, personId);
- readAllExtensions(contactData, personId);
-
- mCursor.moveToNext();
-
- String vcardString = convertContactToVCard(contactData);
- try {
- mWriter.write(vcardString);
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException occurred during exportOneContactData: " +
- e.getMessage());
- mErrorReason = "IOException occurred: " + e.getMessage();
- return false;
- }
- } catch (OutOfMemoryError error) {
- // Maybe some data (e.g. photo) is too big to have in memory. But it should be rare.
- Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry: " + name);
- System.gc();
- }
-
- return true;
- }
-
- /**
- * @return Return the error reason if possible.
- */
- public String getErrorReason() {
- return mErrorReason;
- }
-
- private void readAllPhones(ContactData contact, int personId) {
- final String[] projection = new String[] {
- Contacts.Phones.TYPE,
- Contacts.Phones.LABEL,
- Contacts.Phones.NUMBER,
- Contacts.Phones.LABEL,
- };
- String selection = String.format("%s=%d", Contacts.Phones.PERSON_ID, personId);
- Cursor cursor = null;
- try {
- cursor = mContentResolver.query(Contacts.Phones.CONTENT_URI,
- projection, selection, null, null);
- if ((cursor != null) && (cursor.moveToFirst())) {
- int typeColumn = cursor.getColumnIndex(Contacts.Phones.TYPE);
- int labelColumn = cursor.getColumnIndex(Contacts.Phones.LABEL);
- int numberColumn = cursor.getColumnIndex(Contacts.Phones.NUMBER);
- do {
- TelData telData = new TelData(cursor.getInt(typeColumn),
- cursor.getString(labelColumn), cursor.getString(numberColumn));
- contact.mTel.add(telData);
- } while (cursor.moveToNext());
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void readAllPhotos(ContactData contact, int personId) {
- final String[] projection = new String[] {
- Contacts.Photos.DATA,
- };
- String selection = String.format("%s=%d", Contacts.Photos.PERSON_ID, personId);
- Cursor cursor = null;
- try {
- cursor = mContentResolver.query(Contacts.Photos.CONTENT_URI,
- projection, selection, null, null);
- if ((cursor != null) && (cursor.moveToFirst())) {
- int dataColumn = cursor.getColumnIndex(Contacts.Photos.DATA);
-
- byte[] data;
- do {
- data = cursor.getBlob(dataColumn);
- // Use some heuristics for guessing the format of the image.
- if (data != null && data.length > 0) {
- if (data.length >= 3 &&
- data[0] == 'G' && data[1] == 'I' && data[2] == 'F') {
- contact.mPhotoType = "GIF";
- } else if (data.length >= 4 &&
- data[0] == (byte)0x89 && data[1] == 'P' && data[2] == 'N' &&
- data[3] == 'G') {
- // Note: vCard 2.1 officially does not support PNG, but we may have it
- // and using X- word like "X-PNG" may not let importers know it is
- // PNG. So we use the String "PNG" as is...
- contact.mPhotoType = "PNG";
- } else if (data.length >= 2 &&
- data[0] == (byte)0xff && data[1] == (byte)0xd8) {
- contact.mPhotoType = "JPEG";
- } else {
- // TODO: vCard specification requires the other formats like TIFF...
- Log.d(LOG_TAG, "Unknown photo type. Ignore.");
- continue;
- }
- }
- String photoData = encodeBase64(data);
- if (photoData.length() > 0) {
- contact.mPhoto = photoData;
- }
- } while (cursor.moveToNext());
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void readAllAddresses(ContactData contact, int personId) {
- final String[] projection = new String[] {
- Contacts.ContactMethods.TYPE,
- Contacts.ContactMethods.LABEL,
- Contacts.ContactMethods.DATA,
- Contacts.ContactMethods.KIND,
- };
- String selection = String.format("%s=%d AND %s IN (1,2)",
- Contacts.ContactMethods.PERSON_ID, personId, Contacts.ContactMethods.KIND);
- Cursor cursor = null;
- try {
- cursor = mContentResolver.query(Contacts.ContactMethods.CONTENT_URI,
- projection, selection, null, null);
- if ((cursor != null) && (cursor.moveToFirst())) {
- int typeColumn = cursor.getColumnIndex(Contacts.ContactMethods.TYPE);
- int labelColumn = cursor.getColumnIndex(Contacts.ContactMethods.LABEL);
- int dataColumn = cursor.getColumnIndex(Contacts.ContactMethods.DATA);
- int kindColumn = cursor.getColumnIndex(Contacts.ContactMethods.KIND);
- do {
- int kind = cursor.getInt(kindColumn);
-
- switch(kind) {
- case Contacts.KIND_EMAIL:
- EmailData emailData = new EmailData(cursor.getInt(typeColumn),
- cursor.getString(labelColumn), cursor.getString(dataColumn));
- contact.mEmail.add(emailData);
- break;
- case Contacts.KIND_POSTAL:
- AddressData addr = new AddressData(cursor.getInt(typeColumn),
- cursor.getString(labelColumn), cursor.getString(dataColumn));
- contact.mAddr.add(addr);
- break;
- default:
- break;
- }
- } while (cursor.moveToNext());
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void readAllOrgs(ContactData contactData, int personId) {
- final String[] projection = new String[] {
- Contacts.Organizations.COMPANY,
- Contacts.Organizations.TITLE,
- };
- String selection = String.format("%s=%d", Contacts.ContactMethods.PERSON_ID, personId);
- Cursor cursor = null;
- try {
- cursor = mContentResolver.query(Contacts.Organizations.CONTENT_URI,
- projection, selection, null, null);
- if ((cursor != null) && (cursor.moveToFirst())) {
- int companyColumn = cursor.getColumnIndex(Contacts.Organizations.COMPANY);
- int titleColumn = cursor.getColumnIndex(Contacts.Organizations.TITLE);
- do {
- contactData.mOrg = cursor.getString(companyColumn);
- contactData.mTitle = cursor.getString(titleColumn);
- } while (cursor.moveToNext());
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private void readAllExtensions(ContactData contactData, int personId) {
- final String[] projection = new String[] {
- Contacts.Extensions.NAME,
- Contacts.Extensions.VALUE,
- };
- String selection = String.format("%s=%d", Contacts.Extensions.PERSON_ID, personId);
- Cursor cursor = null;
- try {
- cursor = mContentResolver.query(Contacts.Extensions.CONTENT_URI,
- projection, selection, null, null);
- if ((cursor != null) && (cursor.moveToFirst())) {
- int nameColumn = cursor.getColumnIndex(Contacts.Extensions.NAME);
- int valueColumn = cursor.getColumnIndex(Contacts.Extensions.VALUE);
- do {
- contactData.mExtensions.put(
- cursor.getString(nameColumn),
- cursor.getString(valueColumn));
- } while (cursor.moveToNext());
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private String toHalfWidthString(String orgString) {
- StringBuilder builder = new StringBuilder();
- int length = orgString.length();
- for (int i = 0; i < length; i++) {
- // All Japanese character is able to be expressed by char.
- // Do not need to use String#codepPointAt().
- char ch = orgString.charAt(i);
- CharSequence halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
- if (halfWidthText != null) {
- builder.append(halfWidthText);
- } else {
- builder.append(ch);
- }
- }
- return builder.toString();
- }
-
- private String encodeSomeCharacters(String str) {
- char[] strArray = str.toCharArray();
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < strArray.length; i++) {
- char ch = strArray[i];
- switch (ch) {
- case ';':
- builder.append('\\');
- builder.append(';');
- break;
- case '\r':
- case '\n':
- // ignore
- break;
- case '\\':
- case '<':
- case '>':
- if (mVCardType == VCARD_TYPE_DOCOMO) {
- builder.append('\\');
- builder.append(ch);
- }
- break;
- default:
- builder.append(ch);
- break;
- }
- }
- return builder.toString();
- }
-
- private String convertContactToVCard(ContactData contactData) {
- // Some DoCoMo mobile devices cannot parse a VCard data which does not have empty field.
- final boolean isDoCoMo = (mVCardType == VCARD_TYPE_DOCOMO);
- StringBuilder builder = new StringBuilder();
- appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD);
- appendVCardLine(builder, VCARD_PROPERTY_VERSION, VCARD_DATA_VERSION_V21);
-
- if (!TextUtils.isEmpty(contactData.mName)) {
- builder.append(VCARD_PROPERTY_NAME);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(mVCardAttributeCharset);
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(encodeSomeCharacters(contactData.mName));
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_COL_SEPARATOR);
- } else if (isDoCoMo) {
- appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
- }
-
- if (!TextUtils.isEmpty(contactData.mPhoneticName)) {
- // Note: There is no appropriate property for expressing phonetic name in VCard 2.1,
- // while there is in VCard 3.0 (SORT-STRING).
- // We choose to use DoCoMo's way since it is supported by Japanese mobile phones.
- builder.append(VCARD_PROPERTY_SOUND);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_X_IRMC_N);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(mVCardAttributeCharset);
- builder.append(VCARD_DATA_SEPARATOR);
- // TODO: Not only DoCoMo but also other Japanese mobile careers requires this.
- String phoneticName =
- (isDoCoMo ? toHalfWidthString(contactData.mPhoneticName) :
- contactData.mPhoneticName);
- builder.append(encodeSomeCharacters(phoneticName));
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_COL_SEPARATOR);
- } else if (isDoCoMo) {
- // VCARD_ITEM_SEPARATOR should be inserted for DoCoMo devices.
- builder.append(VCARD_PROPERTY_SOUND);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_X_IRMC_N);
- builder.append(VCARD_DATA_SEPARATOR);
- // Empty data.
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- if (contactData.mTel.size() > 0) {
- for (TelData telData : contactData.mTel) {
- appendVCardTelephoneLine(builder, telData.mType,
- telData.mLabel, telData.mValue);
- }
- } else if (isDoCoMo) {
- appendVCardTelephoneLine(builder, Contacts.Phones.TYPE_HOME, "", "");
- }
-
- if (contactData.mEmail.size() > 0) {
- for (EmailData emailData : contactData.mEmail) {
- appendVCardEmailLine(builder, emailData.mType,
- emailData.mLabel, emailData.mValue);
- }
- } else if (isDoCoMo) {
- appendVCardEmailLine(builder, Contacts.ContactMethods.TYPE_HOME, "", "");
- }
-
- if (isDoCoMo) {
- appendVCardAddressLinesForDoCoMo(builder, contactData);
- } else {
- appendVCardAddressLinesForGeneric(builder, contactData);
- }
-
- if (!TextUtils.isEmpty(contactData.mOrg)) {
- appendVCardLine(builder, VCARD_PROPERTY_ORG, contactData.mOrg, true, true);
- }
-
- if (!TextUtils.isEmpty(contactData.mTitle)) {
- appendVCardLine(builder, VCARD_PROPERTY_TITLE, contactData.mTitle, true, true);
- }
-
- if (!TextUtils.isEmpty(contactData.mNote)) {
- appendVCardLine(builder, VCARD_PROPERTY_NOTE, contactData.mNote, true, true);
- }
-
- if ((contactData.mPhoto != null) && (contactData.mPhoto.length() > 0)) {
- // Note that contactData.mPhoto is already BASE64-encoded.
- appendVCardPhotoLine(builder, contactData.mPhoto, contactData.mPhotoType);
- }
-
- appendVCardExtension(builder, contactData, VCARD_PROPERTY_BDAY, isDoCoMo);
-
- // XXX: URL may have non-ascii chars. Should we add charset?
- appendVCardExtension(builder, contactData, VCARD_PROPERTY_URL, isDoCoMo);
-
- if (isDoCoMo) {
- if (contactData.mExtensions.containsKey(VCARD_PROPERTY_X_CLASS)) {
- appendVCardExtension(builder, contactData, VCARD_PROPERTY_X_CLASS, true);
- } else {
- appendVCardLine(builder, VCARD_PROPERTY_X_CLASS, VCARD_DATA_PUBLIC);
- }
- appendVCardExtension(builder, contactData, VCARD_PROPERTY_X_REDUCTION, true);
- appendVCardExtension(builder, contactData, VCARD_PROPERTY_X_NO, true);
- appendVCardExtension(builder, contactData, VCARD_PROPERTY_X_DCM_HMN_MODE, true);
- }
-
- appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD);
-
- return builder.toString();
- }
-
- private void appendVCardAddressLinesForGeneric(StringBuilder builder, ContactData contactData) {
- for (AddressData addr : contactData.mAddr) {
- appendVCardAddressLine(builder, addr.mType, addr.mLabel, addr.mValue);
- }
- }
-
- private void appendVCardAddressLinesForDoCoMo(StringBuilder builder, ContactData contactData) {
- boolean isAddrSet = false;
- for (AddressData addr : contactData.mAddr) {
- if ((!isAddrSet) && (addr.mType == Contacts.ContactMethods.TYPE_HOME)) {
- appendVCardAddressLine(builder, addr.mType, addr.mLabel,
- addr.mValue);
- isAddrSet = true;
- break;
- }
- }
- if (!isAddrSet) {
- for (AddressData addr : contactData.mAddr) {
- if ((!isAddrSet) && (addr.mType == Contacts.ContactMethods.TYPE_WORK)) {
- appendVCardAddressLine(builder, addr.mType, addr.mLabel,
- addr.mValue);
- isAddrSet = true;
- break;
- }
- }
- }
- if (!isAddrSet) {
- for (AddressData addr : contactData.mAddr) {
- if ((!isAddrSet) && (addr.mType == Contacts.ContactMethods.TYPE_OTHER)) {
- appendVCardAddressLine(builder, addr.mType, addr.mLabel,
- addr.mValue);
- isAddrSet = true;
- break;
- }
- }
- }
- if (!isAddrSet) {
- for (AddressData addr : contactData.mAddr) {
- if ((!isAddrSet) && (addr.mType == Contacts.ContactMethods.TYPE_CUSTOM)) {
- appendVCardAddressLine(builder, addr.mType, addr.mLabel,
- addr.mValue);
- isAddrSet = true;
- break;
- }
- }
- }
- if (!isAddrSet) {
- appendVCardAddressLine(builder, Contacts.ContactMethods.TYPE_HOME, "", "");
- }
- }
-
- private void appendVCardPhotoLine(StringBuilder builder, String encodedData, String type) {
- StringBuilder tmpBuilder = new StringBuilder();
- tmpBuilder.append(VCARD_PROPERTY_PHOTO);
- tmpBuilder.append(VCARD_ATTR_SEPARATOR);
- tmpBuilder.append(VCARD_ATTR_ENCODING_BASE64_V21);
- tmpBuilder.append(VCARD_ATTR_SEPARATOR);
- tmpBuilder.append("TYPE=");
- tmpBuilder.append(type);
- tmpBuilder.append(VCARD_DATA_SEPARATOR);
- tmpBuilder.append(encodedData);
-
- String tmpStr = tmpBuilder.toString();
- tmpBuilder = new StringBuilder();
- int lineCount = 0;
- for (int i = 0; i < tmpStr.length(); i++) {
- tmpBuilder.append(tmpStr.charAt(i));
- lineCount++;
- if (lineCount > 72) {
- tmpBuilder.append(VCARD_COL_SEPARATOR);
- tmpBuilder.append(VCARD_WS);
- lineCount = 0;
- }
- }
- builder.append(tmpBuilder.toString());
- builder.append(VCARD_COL_SEPARATOR);
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- private void appendVCardAddressLine(StringBuilder builder,
- int type, String label, String rawData) {
- builder.append(VCARD_PROPERTY_ADR);
- builder.append(VCARD_ATTR_SEPARATOR);
-
- boolean dataExists = !TextUtils.isEmpty(rawData);
-
- switch(type) {
- case Contacts.ContactMethods.TYPE_HOME:
- builder.append(VCARD_ATTR_HOME);
- if (dataExists) {
- builder.append(VCARD_ATTR_SEPARATOR);
- }
- break;
- case Contacts.ContactMethods.TYPE_WORK:
- builder.append(VCARD_ATTR_WORK);
- if (dataExists) {
- builder.append(VCARD_ATTR_SEPARATOR);
- }
- break;
- case Contacts.ContactMethods.TYPE_CUSTOM:
- // Ignore custom value since
- // - it may violate vCard spec
- // - it may contain non-ASCII characters
- // TODO: fix this.
- //
- // builder.append(label);
- // builder.append(VCARD_DATA_SEPARATOR);
- // break;
- case Contacts.ContactMethods.TYPE_OTHER:
- default:
- // Ignore other methods.
- // TODO: fix this.
- break;
- }
-
- if (dataExists) {
- builder.append(mVCardAttributeCharset);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_ENCODING_QP);
- }
- builder.append(VCARD_DATA_SEPARATOR);
- if (dataExists) {
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(encodeQuotedPrintable(rawData));
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(VCARD_ITEM_SEPARATOR);
- }
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- private void appendVCardEmailLine(StringBuilder builder, int type, String label, String data) {
- builder.append(VCARD_PROPERTY_EMAIL);
- builder.append(VCARD_ATTR_SEPARATOR);
-
- switch(type) {
- case Contacts.ContactMethods.TYPE_CUSTOM:
- if (label.equals(Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME)) {
- builder.append(VCARD_ATTR_CELL);
- } else {
- // Ignore custom value.
- builder.append(VCARD_ATTR_INTERNET);
- }
- break;
- case Contacts.ContactMethods.TYPE_HOME:
- builder.append(VCARD_ATTR_HOME);
- break;
- case Contacts.ContactMethods.TYPE_WORK:
- builder.append(VCARD_ATTR_WORK);
- break;
- case Contacts.ContactMethods.TYPE_OTHER:
- default:
- builder.append(VCARD_ATTR_INTERNET);
- break;
- }
-
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(data);
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- private void appendVCardTelephoneLine(StringBuilder builder,
- int type, String label, String data) {
- builder.append(VCARD_PROPERTY_TEL);
- builder.append(VCARD_ATTR_SEPARATOR);
-
- switch(type) {
- case Contacts.Phones.TYPE_CUSTOM:
- // Ignore custom label.
- builder.append(VCARD_ATTR_VOICE);
- break;
- case Contacts.Phones.TYPE_HOME:
- builder.append(VCARD_ATTR_HOME);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_VOICE);
- break;
- case Contacts.Phones.TYPE_MOBILE:
- builder.append(VCARD_ATTR_CELL);
- break;
- case Contacts.Phones.TYPE_WORK:
- builder.append(VCARD_ATTR_WORK);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_VOICE);
- break;
- case Contacts.Phones.TYPE_FAX_WORK:
- builder.append(VCARD_ATTR_WORK);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_FAX);
- break;
- case Contacts.Phones.TYPE_FAX_HOME:
- builder.append(VCARD_ATTR_HOME);
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_FAX);
- break;
- case Contacts.Phones.TYPE_PAGER:
- builder.append(VCARD_ATTR_VOICE);
- break;
- case Contacts.Phones.TYPE_OTHER:
- builder.append(VCARD_ATTR_VOICE);
- break;
- default:
- builder.append(VCARD_ATTR_VOICE);
- break;
- }
-
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(data);
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- private void appendVCardExtension(StringBuilder builder, ContactData contactData,
- String propertyName, boolean mustEmitSomething) {
- if (contactData.mExtensions.containsKey(propertyName)) {
- PropertyNode propertyNode =
- PropertyNode.decode(contactData.mExtensions.get(propertyName));
- appendVCardLine(builder, propertyName, propertyNode.propValue);
- } else if (mustEmitSomething) {
- appendVCardLine(builder, propertyName, "");
- }
- }
-
- private void appendVCardLine(StringBuilder builder, String propertyName, String data) {
- appendVCardLine(builder, propertyName, data, false, false);
- }
-
- private void appendVCardLine(StringBuilder builder, String field, String data,
- boolean needCharset, boolean needQuotedPrintable) {
- builder.append(field);
- if (needCharset) {
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(mVCardAttributeCharset);
- }
-
- if (needQuotedPrintable) {
- builder.append(VCARD_ATTR_SEPARATOR);
- builder.append(VCARD_ATTR_ENCODING_QP);
- data = encodeQuotedPrintable(data);
- }
-
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(data);
- builder.append(VCARD_COL_SEPARATOR);
- }
-
- // TODO: Replace with the method in Base64 class.
- private static char PAD = '=';
- private static final char[] ENCODE64 = {
- 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
- 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
- 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
- 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
- };
-
- private String encodeBase64(byte[] data) {
- if (data == null) {
- return "";
- }
-
- char[] charBuffer = new char[(data.length + 2) / 3 * 4];
- int position = 0;
- int _3byte = 0;
- for (int i=0; i<data.length-2; i+=3) {
- _3byte = ((data[i] & 0xFF) << 16) + ((data[i+1] & 0xFF) << 8) + (data[i+2] & 0xFF);
- charBuffer[position++] = ENCODE64[_3byte >> 18];
- charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
- charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
- charBuffer[position++] = ENCODE64[_3byte & 0x3F];
- }
- switch(data.length % 3) {
- case 1: // [111111][11 0000][0000 00][000000]
- _3byte = ((data[data.length-1] & 0xFF) << 16);
- charBuffer[position++] = ENCODE64[_3byte >> 18];
- charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
- charBuffer[position++] = PAD;
- charBuffer[position++] = PAD;
- break;
- case 2: // [111111][11 1111][1111 00][000000]
- _3byte = ((data[data.length-2] & 0xFF) << 16) + ((data[data.length-1] & 0xFF) << 8);
- charBuffer[position++] = ENCODE64[_3byte >> 18];
- charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
- charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
- charBuffer[position++] = PAD;
- break;
- }
-
- return new String(charBuffer);
- }
-
- private String encodeQuotedPrintable(String str) {
- {
- // Replace "\n" and "\r" with "\r\n".
- StringBuilder tmpBuilder = new StringBuilder();
- int length = str.length();
- for (int i = 0; i < length; i++) {
- char ch = str.charAt(i);
- if (ch == '\r') {
- if (i + 1 < length && str.charAt(i + 1) == '\n') {
- i++;
- }
- tmpBuilder.append("\r\n");
- } else if (ch == '\n') {
- tmpBuilder.append("\r\n");
- } else {
- tmpBuilder.append(str.charAt(i));
- }
- }
- str = tmpBuilder.toString();
- }
-
- StringBuilder builder = new StringBuilder();
- int index = 0;
- int lineCount = 0;
- byte[] strArray = null;
-
- try {
- strArray = str.getBytes(mCharsetString);
- } catch (UnsupportedEncodingException e) {
- Log.e(LOG_TAG, "Charset " + mCharsetString + " cannot be used. " +
- "Try default charset");
- strArray = str.getBytes();
- }
- while (index < strArray.length) {
- builder.append(String.format("=%02X", strArray[index]));
- index += 1;
- lineCount += 3;
-
- if (lineCount >= 67) {
- // Specification requires CRLF must be inserted before the length of the line
- // becomes more than 76.
- // Assuming that the next character is a multi-byte character, it will become
- // 6 bytes.
- // 76 - 6 - 3 = 67
- builder.append("=\r\n");
- lineCount = 0;
- }
- }
-
- return builder.toString();
- }
-
- // TODO: replace this with ContactStruct
- public class ContactData {
- private String mName = "";
- private String mPhoneticName = "";
- private ArrayList<TelData> mTel = new ArrayList<TelData>();
- private ArrayList<EmailData> mEmail = new ArrayList<EmailData>();
- private ArrayList<AddressData> mAddr = new ArrayList<AddressData>();
- private String mOrg = "";
- private String mTitle = "";
- private String mNote = "";
- private String mPhoto = "";
- private String mPhotoType = "JPG"; // Default
- private Map<String, String> mExtensions = new HashMap<String, String>();
-
- public boolean isEmptyName() {
- return TextUtils.isEmpty(mName);
- }
- }
-
- private class AddressData {
- private int mType;
- private String mLabel;
- private String mValue;
-
- public AddressData(int type, String label, String value) {
- mType = type;
- mLabel = label;
- mValue = value;
- }
- }
-
- private class EmailData {
- private int mType;
- private String mLabel;
- private String mValue;
-
- public EmailData(int type, String label, String value) {
- mType = type;
- mLabel = label;
- mValue = value;
- }
- }
-
- private class TelData {
- private int mType;
- private String mLabel;
- private String mValue;
-
- public TelData(int type, String label, String value) {
- mType = type;
- mLabel = label;
- mValue = value;
- }
- }
-}
-
-/**
- * TextUtils especially for Japanese.
- * TODO: make this in android.text in the future
- */
-class JapaneseUtils {
- static private final Map<Character, String> sHalfWidthMap =
- new HashMap<Character, String>();
-
- static {
- // There's no logical mapping rule in Unicode. Sigh.
- sHalfWidthMap.put('\u3001', "\uFF64");
- sHalfWidthMap.put('\u3002', "\uFF61");
- sHalfWidthMap.put('\u300C', "\uFF62");
- sHalfWidthMap.put('\u300D', "\uFF63");
- sHalfWidthMap.put('\u301C', "~");
- sHalfWidthMap.put('\u3041', "\uFF67");
- sHalfWidthMap.put('\u3042', "\uFF71");
- sHalfWidthMap.put('\u3043', "\uFF68");
- sHalfWidthMap.put('\u3044', "\uFF72");
- sHalfWidthMap.put('\u3045', "\uFF69");
- sHalfWidthMap.put('\u3046', "\uFF73");
- sHalfWidthMap.put('\u3047', "\uFF6A");
- sHalfWidthMap.put('\u3048', "\uFF74");
- sHalfWidthMap.put('\u3049', "\uFF6B");
- sHalfWidthMap.put('\u304A', "\uFF75");
- sHalfWidthMap.put('\u304B', "\uFF76");
- sHalfWidthMap.put('\u304C', "\uFF76\uFF9E");
- sHalfWidthMap.put('\u304D', "\uFF77");
- sHalfWidthMap.put('\u304E', "\uFF77\uFF9E");
- sHalfWidthMap.put('\u304F', "\uFF78");
- sHalfWidthMap.put('\u3050', "\uFF78\uFF9E");
- sHalfWidthMap.put('\u3051', "\uFF79");
- sHalfWidthMap.put('\u3052', "\uFF79\uFF9E");
- sHalfWidthMap.put('\u3053', "\uFF7A");
- sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E");
- sHalfWidthMap.put('\u3055', "\uFF7B");
- sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E");
- sHalfWidthMap.put('\u3057', "\uFF7C");
- sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E");
- sHalfWidthMap.put('\u3059', "\uFF7D");
- sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E");
- sHalfWidthMap.put('\u305B', "\uFF7E");
- sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E");
- sHalfWidthMap.put('\u305D', "\uFF7F");
- sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E");
- sHalfWidthMap.put('\u305F', "\uFF80");
- sHalfWidthMap.put('\u3060', "\uFF80\uFF9E");
- sHalfWidthMap.put('\u3061', "\uFF81");
- sHalfWidthMap.put('\u3062', "\uFF81\uFF9E");
- sHalfWidthMap.put('\u3063', "\uFF6F");
- sHalfWidthMap.put('\u3064', "\uFF82");
- sHalfWidthMap.put('\u3065', "\uFF82\uFF9E");
- sHalfWidthMap.put('\u3066', "\uFF83");
- sHalfWidthMap.put('\u3067', "\uFF83\uFF9E");
- sHalfWidthMap.put('\u3068', "\uFF84");
- sHalfWidthMap.put('\u3069', "\uFF84\uFF9E");
- sHalfWidthMap.put('\u306A', "\uFF85");
- sHalfWidthMap.put('\u306B', "\uFF86");
- sHalfWidthMap.put('\u306C', "\uFF87");
- sHalfWidthMap.put('\u306D', "\uFF88");
- sHalfWidthMap.put('\u306E', "\uFF89");
- sHalfWidthMap.put('\u306F', "\uFF8A");
- sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E");
- sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F");
- sHalfWidthMap.put('\u3072', "\uFF8B");
- sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E");
- sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F");
- sHalfWidthMap.put('\u3075', "\uFF8C");
- sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E");
- sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F");
- sHalfWidthMap.put('\u3078', "\uFF8D");
- sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E");
- sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F");
- sHalfWidthMap.put('\u307B', "\uFF8E");
- sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E");
- sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F");
- sHalfWidthMap.put('\u307E', "\uFF8F");
- sHalfWidthMap.put('\u307F', "\uFF90");
- sHalfWidthMap.put('\u3080', "\uFF91");
- sHalfWidthMap.put('\u3081', "\uFF92");
- sHalfWidthMap.put('\u3082', "\uFF93");
- sHalfWidthMap.put('\u3083', "\uFF6C");
- sHalfWidthMap.put('\u3084', "\uFF94");
- sHalfWidthMap.put('\u3085', "\uFF6D");
- sHalfWidthMap.put('\u3086', "\uFF95");
- sHalfWidthMap.put('\u3087', "\uFF6E");
- sHalfWidthMap.put('\u3088', "\uFF96");
- sHalfWidthMap.put('\u3089', "\uFF97");
- sHalfWidthMap.put('\u308A', "\uFF98");
- sHalfWidthMap.put('\u308B', "\uFF99");
- sHalfWidthMap.put('\u308C', "\uFF9A");
- sHalfWidthMap.put('\u308D', "\uFF9B");
- sHalfWidthMap.put('\u308E', "\uFF9C");
- sHalfWidthMap.put('\u308F', "\uFF9C");
- sHalfWidthMap.put('\u3090', "\uFF72");
- sHalfWidthMap.put('\u3091', "\uFF74");
- sHalfWidthMap.put('\u3092', "\uFF66");
- sHalfWidthMap.put('\u3093', "\uFF9D");
- sHalfWidthMap.put('\u309B', "\uFF9E");
- sHalfWidthMap.put('\u309C', "\uFF9F");
- sHalfWidthMap.put('\u30A1', "\uFF67");
- sHalfWidthMap.put('\u30A2', "\uFF71");
- sHalfWidthMap.put('\u30A3', "\uFF68");
- sHalfWidthMap.put('\u30A4', "\uFF72");
- sHalfWidthMap.put('\u30A5', "\uFF69");
- sHalfWidthMap.put('\u30A6', "\uFF73");
- sHalfWidthMap.put('\u30A7', "\uFF6A");
- sHalfWidthMap.put('\u30A8', "\uFF74");
- sHalfWidthMap.put('\u30A9', "\uFF6B");
- sHalfWidthMap.put('\u30AA', "\uFF75");
- sHalfWidthMap.put('\u30AB', "\uFF76");
- sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E");
- sHalfWidthMap.put('\u30AD', "\uFF77");
- sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E");
- sHalfWidthMap.put('\u30AF', "\uFF78");
- sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E");
- sHalfWidthMap.put('\u30B1', "\uFF79");
- sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E");
- sHalfWidthMap.put('\u30B3', "\uFF7A");
- sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E");
- sHalfWidthMap.put('\u30B5', "\uFF7B");
- sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E");
- sHalfWidthMap.put('\u30B7', "\uFF7C");
- sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E");
- sHalfWidthMap.put('\u30B9', "\uFF7D");
- sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E");
- sHalfWidthMap.put('\u30BB', "\uFF7E");
- sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E");
- sHalfWidthMap.put('\u30BD', "\uFF7F");
- sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E");
- sHalfWidthMap.put('\u30BF', "\uFF80");
- sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E");
- sHalfWidthMap.put('\u30C1', "\uFF81");
- sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E");
- sHalfWidthMap.put('\u30C3', "\uFF6F");
- sHalfWidthMap.put('\u30C4', "\uFF82");
- sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E");
- sHalfWidthMap.put('\u30C6', "\uFF83");
- sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E");
- sHalfWidthMap.put('\u30C8', "\uFF84");
- sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E");
- sHalfWidthMap.put('\u30CA', "\uFF85");
- sHalfWidthMap.put('\u30CB', "\uFF86");
- sHalfWidthMap.put('\u30CC', "\uFF87");
- sHalfWidthMap.put('\u30CD', "\uFF88");
- sHalfWidthMap.put('\u30CE', "\uFF89");
- sHalfWidthMap.put('\u30CF', "\uFF8A");
- sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E");
- sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F");
- sHalfWidthMap.put('\u30D2', "\uFF8B");
- sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E");
- sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F");
- sHalfWidthMap.put('\u30D5', "\uFF8C");
- sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E");
- sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F");
- sHalfWidthMap.put('\u30D8', "\uFF8D");
- sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E");
- sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F");
- sHalfWidthMap.put('\u30DB', "\uFF8E");
- sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E");
- sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F");
- sHalfWidthMap.put('\u30DE', "\uFF8F");
- sHalfWidthMap.put('\u30DF', "\uFF90");
- sHalfWidthMap.put('\u30E0', "\uFF91");
- sHalfWidthMap.put('\u30E1', "\uFF92");
- sHalfWidthMap.put('\u30E2', "\uFF93");
- sHalfWidthMap.put('\u30E3', "\uFF6C");
- sHalfWidthMap.put('\u30E4', "\uFF94");
- sHalfWidthMap.put('\u30E5', "\uFF6D");
- sHalfWidthMap.put('\u30E6', "\uFF95");
- sHalfWidthMap.put('\u30E7', "\uFF6E");
- sHalfWidthMap.put('\u30E8', "\uFF96");
- sHalfWidthMap.put('\u30E9', "\uFF97");
- sHalfWidthMap.put('\u30EA', "\uFF98");
- sHalfWidthMap.put('\u30EB', "\uFF99");
- sHalfWidthMap.put('\u30EC', "\uFF9A");
- sHalfWidthMap.put('\u30ED', "\uFF9B");
- sHalfWidthMap.put('\u30EE', "\uFF9C");
- sHalfWidthMap.put('\u30EF', "\uFF9C");
- sHalfWidthMap.put('\u30F0', "\uFF72");
- sHalfWidthMap.put('\u30F1', "\uFF74");
- sHalfWidthMap.put('\u30F2', "\uFF66");
- sHalfWidthMap.put('\u30F3', "\uFF9D");
- sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E");
- sHalfWidthMap.put('\u30F5', "\uFF76");
- sHalfWidthMap.put('\u30F6', "\uFF79");
- sHalfWidthMap.put('\u30FB', "\uFF65");
- sHalfWidthMap.put('\u30FC', "\uFF70");
- sHalfWidthMap.put('\uFF01', "!");
- sHalfWidthMap.put('\uFF02', "\"");
- sHalfWidthMap.put('\uFF03', "#");
- sHalfWidthMap.put('\uFF04', "$");
- sHalfWidthMap.put('\uFF05', "%");
- sHalfWidthMap.put('\uFF06', "&");
- sHalfWidthMap.put('\uFF07', "'");
- sHalfWidthMap.put('\uFF08', "(");
- sHalfWidthMap.put('\uFF09', ")");
- sHalfWidthMap.put('\uFF0A', "*");
- sHalfWidthMap.put('\uFF0B', "+");
- sHalfWidthMap.put('\uFF0C', ",");
- sHalfWidthMap.put('\uFF0D', "-");
- sHalfWidthMap.put('\uFF0E', ".");
- sHalfWidthMap.put('\uFF0F', "/");
- sHalfWidthMap.put('\uFF10', "0");
- sHalfWidthMap.put('\uFF11', "1");
- sHalfWidthMap.put('\uFF12', "2");
- sHalfWidthMap.put('\uFF13', "3");
- sHalfWidthMap.put('\uFF14', "4");
- sHalfWidthMap.put('\uFF15', "5");
- sHalfWidthMap.put('\uFF16', "6");
- sHalfWidthMap.put('\uFF17', "7");
- sHalfWidthMap.put('\uFF18', "8");
- sHalfWidthMap.put('\uFF19', "9");
- sHalfWidthMap.put('\uFF1A', ":");
- sHalfWidthMap.put('\uFF1B', ";");
- sHalfWidthMap.put('\uFF1C', "<");
- sHalfWidthMap.put('\uFF1D', "=");
- sHalfWidthMap.put('\uFF1E', ">");
- sHalfWidthMap.put('\uFF1F', "?");
- sHalfWidthMap.put('\uFF20', "@");
- sHalfWidthMap.put('\uFF21', "A");
- sHalfWidthMap.put('\uFF22', "B");
- sHalfWidthMap.put('\uFF23', "C");
- sHalfWidthMap.put('\uFF24', "D");
- sHalfWidthMap.put('\uFF25', "E");
- sHalfWidthMap.put('\uFF26', "F");
- sHalfWidthMap.put('\uFF27', "G");
- sHalfWidthMap.put('\uFF28', "H");
- sHalfWidthMap.put('\uFF29', "I");
- sHalfWidthMap.put('\uFF2A', "J");
- sHalfWidthMap.put('\uFF2B', "K");
- sHalfWidthMap.put('\uFF2C', "L");
- sHalfWidthMap.put('\uFF2D', "M");
- sHalfWidthMap.put('\uFF2E', "N");
- sHalfWidthMap.put('\uFF2F', "O");
- sHalfWidthMap.put('\uFF30', "P");
- sHalfWidthMap.put('\uFF31', "Q");
- sHalfWidthMap.put('\uFF32', "R");
- sHalfWidthMap.put('\uFF33', "S");
- sHalfWidthMap.put('\uFF34', "T");
- sHalfWidthMap.put('\uFF35', "U");
- sHalfWidthMap.put('\uFF36', "V");
- sHalfWidthMap.put('\uFF37', "W");
- sHalfWidthMap.put('\uFF38', "X");
- sHalfWidthMap.put('\uFF39', "Y");
- sHalfWidthMap.put('\uFF3A', "Z");
- sHalfWidthMap.put('\uFF3B', "[");
- sHalfWidthMap.put('\uFF3C', "\\");
- sHalfWidthMap.put('\uFF3D', "]");
- sHalfWidthMap.put('\uFF3E', "^");
- sHalfWidthMap.put('\uFF3F', "_");
- sHalfWidthMap.put('\uFF41', "a");
- sHalfWidthMap.put('\uFF42', "b");
- sHalfWidthMap.put('\uFF43', "c");
- sHalfWidthMap.put('\uFF44', "d");
- sHalfWidthMap.put('\uFF45', "e");
- sHalfWidthMap.put('\uFF46', "f");
- sHalfWidthMap.put('\uFF47', "g");
- sHalfWidthMap.put('\uFF48', "h");
- sHalfWidthMap.put('\uFF49', "i");
- sHalfWidthMap.put('\uFF4A', "j");
- sHalfWidthMap.put('\uFF4B', "k");
- sHalfWidthMap.put('\uFF4C', "l");
- sHalfWidthMap.put('\uFF4D', "m");
- sHalfWidthMap.put('\uFF4E', "n");
- sHalfWidthMap.put('\uFF4F', "o");
- sHalfWidthMap.put('\uFF50', "p");
- sHalfWidthMap.put('\uFF51', "q");
- sHalfWidthMap.put('\uFF52', "r");
- sHalfWidthMap.put('\uFF53', "s");
- sHalfWidthMap.put('\uFF54', "t");
- sHalfWidthMap.put('\uFF55', "u");
- sHalfWidthMap.put('\uFF56', "v");
- sHalfWidthMap.put('\uFF57', "w");
- sHalfWidthMap.put('\uFF58', "x");
- sHalfWidthMap.put('\uFF59', "y");
- sHalfWidthMap.put('\uFF5A', "z");
- sHalfWidthMap.put('\uFF5B', "{");
- sHalfWidthMap.put('\uFF5C', "|");
- sHalfWidthMap.put('\uFF5D', "}");
- sHalfWidthMap.put('\uFF5E', "~");
- sHalfWidthMap.put('\uFF61', "\uFF61");
- sHalfWidthMap.put('\uFF62', "\uFF62");
- sHalfWidthMap.put('\uFF63', "\uFF63");
- sHalfWidthMap.put('\uFF64', "\uFF64");
- sHalfWidthMap.put('\uFF65', "\uFF65");
- sHalfWidthMap.put('\uFF66', "\uFF66");
- sHalfWidthMap.put('\uFF67', "\uFF67");
- sHalfWidthMap.put('\uFF68', "\uFF68");
- sHalfWidthMap.put('\uFF69', "\uFF69");
- sHalfWidthMap.put('\uFF6A', "\uFF6A");
- sHalfWidthMap.put('\uFF6B', "\uFF6B");
- sHalfWidthMap.put('\uFF6C', "\uFF6C");
- sHalfWidthMap.put('\uFF6D', "\uFF6D");
- sHalfWidthMap.put('\uFF6E', "\uFF6E");
- sHalfWidthMap.put('\uFF6F', "\uFF6F");
- sHalfWidthMap.put('\uFF70', "\uFF70");
- sHalfWidthMap.put('\uFF71', "\uFF71");
- sHalfWidthMap.put('\uFF72', "\uFF72");
- sHalfWidthMap.put('\uFF73', "\uFF73");
- sHalfWidthMap.put('\uFF74', "\uFF74");
- sHalfWidthMap.put('\uFF75', "\uFF75");
- sHalfWidthMap.put('\uFF76', "\uFF76");
- sHalfWidthMap.put('\uFF77', "\uFF77");
- sHalfWidthMap.put('\uFF78', "\uFF78");
- sHalfWidthMap.put('\uFF79', "\uFF79");
- sHalfWidthMap.put('\uFF7A', "\uFF7A");
- sHalfWidthMap.put('\uFF7B', "\uFF7B");
- sHalfWidthMap.put('\uFF7C', "\uFF7C");
- sHalfWidthMap.put('\uFF7D', "\uFF7D");
- sHalfWidthMap.put('\uFF7E', "\uFF7E");
- sHalfWidthMap.put('\uFF7F', "\uFF7F");
- sHalfWidthMap.put('\uFF80', "\uFF80");
- sHalfWidthMap.put('\uFF81', "\uFF81");
- sHalfWidthMap.put('\uFF82', "\uFF82");
- sHalfWidthMap.put('\uFF83', "\uFF83");
- sHalfWidthMap.put('\uFF84', "\uFF84");
- sHalfWidthMap.put('\uFF85', "\uFF85");
- sHalfWidthMap.put('\uFF86', "\uFF86");
- sHalfWidthMap.put('\uFF87', "\uFF87");
- sHalfWidthMap.put('\uFF88', "\uFF88");
- sHalfWidthMap.put('\uFF89', "\uFF89");
- sHalfWidthMap.put('\uFF8A', "\uFF8A");
- sHalfWidthMap.put('\uFF8B', "\uFF8B");
- sHalfWidthMap.put('\uFF8C', "\uFF8C");
- sHalfWidthMap.put('\uFF8D', "\uFF8D");
- sHalfWidthMap.put('\uFF8E', "\uFF8E");
- sHalfWidthMap.put('\uFF8F', "\uFF8F");
- sHalfWidthMap.put('\uFF90', "\uFF90");
- sHalfWidthMap.put('\uFF91', "\uFF91");
- sHalfWidthMap.put('\uFF92', "\uFF92");
- sHalfWidthMap.put('\uFF93', "\uFF93");
- sHalfWidthMap.put('\uFF94', "\uFF94");
- sHalfWidthMap.put('\uFF95', "\uFF95");
- sHalfWidthMap.put('\uFF96', "\uFF96");
- sHalfWidthMap.put('\uFF97', "\uFF97");
- sHalfWidthMap.put('\uFF98', "\uFF98");
- sHalfWidthMap.put('\uFF99', "\uFF99");
- sHalfWidthMap.put('\uFF9A', "\uFF9A");
- sHalfWidthMap.put('\uFF9B', "\uFF9B");
- sHalfWidthMap.put('\uFF9C', "\uFF9C");
- sHalfWidthMap.put('\uFF9D', "\uFF9D");
- sHalfWidthMap.put('\uFF9E', "\uFF9E");
- sHalfWidthMap.put('\uFF9F', "\uFF9F");
- sHalfWidthMap.put('\uFFE5', "\u005C\u005C");
- }
-
- /**
- * Return half-width version of that character if possible. Return null if not possible
- * @param ch input character
- * @return CharSequence object if the mapping for ch exists. Return null otherwise.
- */
- public static CharSequence tryGetHalfWidthText(char ch) {
- if (sHalfWidthMap.containsKey(ch)) {
- return sHalfWidthMap.get(ch);
- } else {
- return null;
- }
- }
-}
+}
\ No newline at end of file