VCardImporter, VCardExporter
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 33d8168..9ff4b4b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.mail" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:label="@string/contactsList"
@@ -367,6 +368,9 @@
</intent-filter>
</activity>
+ <activity android:name=".ImportVCardActivity"
+ android:theme="@style/BackgroundOnly" />
+
</application>
</manifest>
diff --git a/res/drawable-finger/ic_menu_export_contact.png b/res/drawable-finger/ic_menu_export_contact.png
new file mode 100644
index 0000000..d5b7554
--- /dev/null
+++ b/res/drawable-finger/ic_menu_export_contact.png
Binary files differ
diff --git a/res/values/config.xml b/res/values/config.xml
index 9747fd1..369759c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -19,7 +19,7 @@
<resources>
<!-- Flag indicating whether Contacts app is allowed to import contacts from SDCard -->
- <bool name="config_allow_import_from_sd_card">false</bool>
+ <bool name="config_allow_import_from_sdcard">false</bool>
<!-- If true, all vcard files are imported from SDCard without asking a user.
If not, dialog shows to let the user to select whether all vcard files are imported or not.
If the user selects "not", then the application ask the user to select a file.-->
@@ -30,4 +30,7 @@
If config_import_all_vcard_from_sdcard_automatically is set true, this configuration
is ignored. -->
<bool name="config_allow_users_select_all_vcard_import">false</bool>
+
+ <!-- Flag indicating whether Contacts app is allowed to export contacts to SDCard -->
+ <bool name="config_allow_export_to_sdcard">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fe866f7..86722f0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -670,4 +670,43 @@
<!-- Message while reading multiple vCard files "(current number) of (total number) files" The order of "current number" and "total number" cannot be changed (like "total: (total number), current: (current number)")-->
<string name="reading_vcard_files"><xliff:g id="current_number">%s</xliff:g> of <xliff:g id="total_number">%s</xliff:g> files</string>
-</resources>
+
+ <!-- The menu item that launches VCard export activity -->
+ <string name="export_contact_list">Export contacts</string>
+
+ <!-- Dialog title shown when a user confirms whether he/she export Contact data -->
+ <string name="confirm_export_title">Confirmation for export</string>
+
+ <!-- Dialog message shown when a user confirms whether he/she export Contact data -->
+ <string name="confirm_export_message">Is it ok to export your contact list to \"<xliff:g id="vcard_filename">%s</xliff:g>\"?</string>
+
+ <!-- Dialog title shown when exporting Contact data failed -->
+ <string name="exporting_contact_failed_title">Exporting contact data has failed</string>
+
+ <!-- Dialog message shown when exporting Contact data failed -->
+ <string name="exporting_contact_failed_message">Exporting contact data has failed\nReason for failure: \"<xliff:g id="fail_reason">%s</xliff:g>\"</string>
+
+ <!-- The failed reason: "Too many vcard files on the SD Card" -->
+ <string name="fail_reason_too_many_vcard">Too many VCard data on the SD Card</string>
+
+ <!-- The failed reason: "Cannot open or create the destination directory" -->
+ <string name="fail_reason_cannot_open_destination_dir">Cannot open or create the destination directory\"<xliff:g id="dir_name">%s</xliff:g>\"</string>
+
+ <!-- Dialog title shown when the application is exporting contact data outside -->
+ <string name="exporting_contact_list_title">Exporting contact data</string>
+
+ <!-- Message shown when the application is exporting contact data outside -->
+ <string name="exporting_contact_list_message">Exporting contact data to \"<xliff:g id="file_name">%s</xliff:g>\"</string>
+
+ <!-- The failed reason: "Could not initialize the exporter" -->
+ <string name="fail_reason_could_not_initialize_exporter">Could not initialize the exporter: \"<xliff:g id="exact_reason">%s</xliff:g>\"</string>
+
+ <!-- The failed reason: "Error occured during export" -->
+ <string name="fail_reason_error_occurred_during_export">Error occured during export: \"<xliff:g id="exact_reason">%s</xliff:g>\"</string>
+
+ <!-- The failed reason: "Could not open a specific file" -->
+ <string name="fail_reason_could_not_open_file">Could not open \"<xliff:g id="file_name">%s</xliff:g>\": <xliff:g id="exact_reason">%s</xliff:g></string>
+
+ <!-- Message in progress bar while exporting contact list to a file "(current number) of (total number) contacts" The order of "current number" and "total number" cannot be changed (like "total: (total number), current: (current number)")-->
+ <string name="exporting_contact_list_progress"><xliff:g id="current_number">%s</xliff:g> of <xliff:g id="total_number">%s</xliff:g> contacts</string>
+</resources>
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c202eda..998da21 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,4 +35,13 @@
<item name="android:windowContentOverlay">@null</item>
</style>
+ <style name="BackgroundOnly">
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowNoDisplay">true</item>
+ <item name="android:windowIsFloating">true</item>
+ </style>
+
</resources>
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 968252e..51363be 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -109,6 +109,7 @@
public static final int MENU_NEW_CONTACT = 10;
public static final int MENU_DISPLAY_GROUP = 11;
public static final int MENU_IMPORT_CONTACTS = 12;
+ public static final int MENU_EXPORT_CONTACTS = 13;
private static final int SUBACTIVITY_NEW_CONTACT = 1;
@@ -335,8 +336,7 @@
if (mIndex == IMPORT_FROM_SIM) {
doImportFromSim();
} else {
- VCardImporter importer = new VCardImporter(ContactsListActivity.this, mHandler);
- importer.startImportVCardFromSdCard();
+ doImportFromSDCard();
}
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
@@ -797,6 +797,12 @@
menu.add(0, MENU_IMPORT_CONTACTS, 0, R.string.importFromSim)
.setIcon(R.drawable.ic_menu_import_contact);
+ /* Temporarily commented out
+ if (getResources().getBoolean(R.bool.config_allow_export_to_sdcard)) {
+ menu.add(0, MENU_EXPORT_CONTACTS, 0, R.string.export_contact_list)
+ .setIcon(R.drawable.ic_menu_export_contact);
+ }*/
+
return super.onCreateOptionsMenu(menu);
}
@@ -861,7 +867,7 @@
return true;
case MENU_IMPORT_CONTACTS:
- if (getResources().getBoolean(R.bool.config_allow_import_from_sd_card)) {
+ if (getResources().getBoolean(R.bool.config_allow_import_from_sdcard)) {
ImportTypeSelectedListener listener =
new ImportTypeSelectedListener();
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this)
@@ -878,6 +884,8 @@
}
return true;
+ /*case MENU_EXPORT_CONTACTS:
+ handleExportContacts();*/
}
return false;
}
@@ -889,6 +897,17 @@
startActivity(importIntent);
}
+ private void doImportFromSDCard() {
+ Intent intent = new Intent(this, ImportVCardActivity.class);
+ startActivity(intent);
+ }
+
+ /*
+ private void handleExportContacts() {
+ VCardExporter exporter = new VCardExporter(ContactsListActivity.this, mHandler);
+ exporter.startExportVCardToSdCard();
+ }*/
+
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
diff --git a/src/com/android/contacts/VCardImporter.java b/src/com/android/contacts/ImportVCardActivity.java
similarity index 87%
rename from src/com/android/contacts/VCardImporter.java
rename to src/com/android/contacts/ImportVCardActivity.java
index e987e85..07eb821 100644
--- a/src/com/android/contacts/VCardImporter.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
@@ -23,6 +24,7 @@
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.syncml.pim.VBuilder;
@@ -81,15 +83,27 @@
* Class for importing vCard. Several user interaction will be required while reading
* (selecting a file, waiting a moment, etc.)
*/
-public class VCardImporter {
- private static final String LOG_TAG = "VCardImporter";
+public class ImportVCardActivity extends Activity {
+ private static final String LOG_TAG = "ImportVCardActivity";
private static final boolean DO_PERFORMANCE_PROFILE = false;
private ProgressDialog mProgressDialog;
- private Context mParentContext;
- private Handler mParentHandler;
+ private Handler mHandler = new Handler();
private boolean mLastNameComesBeforeFirstName;
+ private class CancelListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ }
+
+ private CancelListener mCancelListener = new CancelListener();
+
private class ErrorDisplayer implements Runnable {
private String mErrorMessage;
@@ -101,11 +115,12 @@
String message =
getString(R.string.reading_vcard_failed_message, mErrorMessage);
AlertDialog.Builder builder =
- new AlertDialog.Builder(mParentContext)
+ new AlertDialog.Builder(ImportVCardActivity.this)
.setTitle(getString(R.string.reading_vcard_failed_title))
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(message)
- .setPositiveButton(android.R.string.ok, null);
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
builder.show();
}
}
@@ -132,8 +147,9 @@
}
private void init() {
- mResolver = mParentContext.getContentResolver();
- PowerManager powerManager = (PowerManager)mParentContext.getSystemService(
+ Context context = ImportVCardActivity.this;
+ mResolver = context.getContentResolver();
+ PowerManager powerManager = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK |
@@ -226,17 +242,19 @@
} finally {
mWakeLock.release();
mProgressDialog.dismiss();
+ finish();
}
}
private void doActuallyReadOneVCard(String charset, boolean doIncrementProgress,
VCardSourceDetector detector) {
VCardDataBuilder builder;
+ final Context context = ImportVCardActivity.this;
if (charset != null) {
builder = new VCardDataBuilder(mResolver,
mProgressDialog,
- mParentContext.getString(R.string.reading_vcard_message),
- mParentHandler,
+ context.getString(R.string.reading_vcard_message),
+ mHandler,
charset,
charset,
false,
@@ -244,8 +262,8 @@
} else {
builder = new VCardDataBuilder(mResolver,
mProgressDialog,
- mParentContext.getString(R.string.reading_vcard_message),
- mParentHandler,
+ context.getString(R.string.reading_vcard_message),
+ mHandler,
null,
null,
false,
@@ -304,7 +322,7 @@
mProgressDialog.dismiss();
- mParentHandler.post(new ErrorDisplayer(
+ mHandler.post(new ErrorDisplayer(
getString(R.string.fail_reason_io_error) +
" (" + e.getMessage() + ")"));
return false;
@@ -313,7 +331,7 @@
throw e;
} else {
Log.e(LOG_TAG, "VCardNestedException was emitted: " + e);
- mParentHandler.post(new ErrorDisplayer(
+ mHandler.post(new ErrorDisplayer(
getString(R.string.fail_reason_vcard_parse_error) +
" (" + e.getMessage() + ")"));
return false;
@@ -321,7 +339,7 @@
} catch (VCardException e) {
Log.e(LOG_TAG, "VCardException was emitted: " + e);
- mParentHandler.post(new ErrorDisplayer(
+ mHandler.post(new ErrorDisplayer(
getString(R.string.fail_reason_vcard_parse_error) +
" (" + e.getMessage() + ")"));
return false;
@@ -357,7 +375,7 @@
showVCardFileSelectDialog(mVCardFileList);
}
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
-
+ finish();
} else {
mCurrentIndex = which;
}
@@ -377,7 +395,7 @@
if (which == DialogInterface.BUTTON_POSITIVE) {
importOneVCardFromSDCard(mVCardFileList.get(mCurrentIndex).getCanonicalPath());
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
-
+ finish();
} else {
// Some file is selected.
mCurrentIndex = which;
@@ -410,7 +428,7 @@
mRootDirectory = sdcardDirectory;
mCheckedPaths = new HashSet<String>();
mVCardFiles = new Vector<VCardFile>();
- PowerManager powerManager = (PowerManager)mParentContext.getSystemService(
+ PowerManager powerManager = (PowerManager)ImportVCardActivity.this.getSystemService(
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK |
@@ -437,43 +455,46 @@
mProgressDialog.dismiss();
if (mGotIOException) {
- mParentHandler.post(new Runnable() {
+ mHandler.post(new Runnable() {
public void run() {
String message = (getString(R.string.scanning_sdcard_failed_message,
getString(R.string.fail_reason_io_error)));
AlertDialog.Builder builder =
- new AlertDialog.Builder(mParentContext)
+ new AlertDialog.Builder(ImportVCardActivity.this)
.setTitle(R.string.scanning_sdcard_failed_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(message)
- .setPositiveButton(android.R.string.ok, null);
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
builder.show();
}
});
} else if (mCanceled) {
- return;
+ finish();
} else {
- mParentHandler.post(new Runnable() {
+ mHandler.post(new Runnable() {
public void run() {
int size = mVCardFiles.size();
+ final Context context = ImportVCardActivity.this;
if (size == 0) {
String message = (getString(R.string.scanning_sdcard_failed_message,
getString(R.string.fail_reason_no_vcard_file)));
AlertDialog.Builder builder =
- new AlertDialog.Builder(mParentContext)
+ new AlertDialog.Builder(context)
.setTitle(R.string.scanning_sdcard_failed_title)
.setMessage(message)
- .setPositiveButton(android.R.string.ok, null);
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
builder.show();
return;
- } else if (mParentContext.getResources().getBoolean(
+ } else if (context.getResources().getBoolean(
R.bool.config_import_all_vcard_from_sdcard_automatically)) {
importAllVCardFromSDCard(mVCardFiles);
} else if (size == 1) {
importOneVCardFromSDCard(mVCardFiles.get(0).getCanonicalPath());
- } else if (mParentContext.getResources().getBoolean(
+ } else if (context.getResources().getBoolean(
R.bool.config_allow_users_select_all_vcard_import)) {
showSelectImportTypeDialog(mVCardFiles);
} else {
@@ -541,10 +562,11 @@
DialogInterface.OnClickListener listener =
new ImportTypeSelectedListener(vcardFileList);
AlertDialog.Builder builder =
- new AlertDialog.Builder(mParentContext)
+ new AlertDialog.Builder(ImportVCardActivity.this)
.setTitle(R.string.select_vcard_title)
.setPositiveButton(android.R.string.ok, listener)
- .setNegativeButton(android.R.string.cancel, null);
+ .setOnCancelListener(mCancelListener)
+ .setNegativeButton(android.R.string.cancel, mCancelListener);
String[] items = new String[2];
items[ImportTypeSelectedListener.IMPORT_ALL] =
@@ -561,10 +583,11 @@
DialogInterface.OnClickListener listener =
new VCardSelectedListener(vcardFileList);
AlertDialog.Builder builder =
- new AlertDialog.Builder(mParentContext)
+ new AlertDialog.Builder(this)
.setTitle(R.string.select_vcard_title)
.setPositiveButton(android.R.string.ok, listener)
- .setNegativeButton(android.R.string.cancel, null);
+ .setOnCancelListener(mCancelListener)
+ .setNegativeButton(android.R.string.cancel, mCancelListener);
CharSequence[] items = new CharSequence[size];
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -591,7 +614,7 @@
private void showReadingVCardDialog(DialogInterface.OnCancelListener listener) {
String title = getString(R.string.reading_vcard_title);
String message = getString(R.string.reading_vcard_message);
- mProgressDialog = new ProgressDialog(mParentContext);
+ mProgressDialog = new ProgressDialog(this);
mProgressDialog.setTitle(title);
mProgressDialog.setMessage(message);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
@@ -599,23 +622,14 @@
mProgressDialog.show();
}
- private String getString(int resId, Object... formatArgs) {
- return mParentContext.getString(resId, formatArgs);
- }
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
- private String getString(int resId) {
- return mParentContext.getString(resId);
- }
-
- /**
- * @param parentContext must not be null
- * @param parentHandler must not be null
- */
- public VCardImporter(Context parentContext, Handler parentHandler) {
- mParentContext = parentContext;
- mParentHandler = parentHandler;
- mLastNameComesBeforeFirstName = parentContext.getResources().getBoolean(
+ mLastNameComesBeforeFirstName = getResources().getBoolean(
com.android.internal.R.bool.config_lastname_comes_before_firstname);
+
+ startImportVCardFromSdCard();
}
/**
@@ -627,18 +641,18 @@
public void startImportVCardFromSdCard() {
File file = new File("/sdcard");
if (!file.exists() || !file.isDirectory() || !file.canRead()) {
- new AlertDialog.Builder(mParentContext)
+ new AlertDialog.Builder(this)
.setTitle(R.string.no_sdcard_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_sdcard_message)
- .setPositiveButton(android.R.string.ok, null)
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener)
.show();
} else {
String title = getString(R.string.searching_vcard_title);
String message = getString(R.string.searching_vcard_message);
- mProgressDialog = ProgressDialog.show(mParentContext,
- title, message, true, false);
+ mProgressDialog = ProgressDialog.show(this, title, message, true, false);
VCardScanThread thread = new VCardScanThread(file);
mProgressDialog.setOnCancelListener(thread);
thread.start();