Several changes included...
- Suppress RuntimeException reported in the issue 2145361
-- Ask the parent Activity to show messages.
- Make vCard exporter a separated Activity.
-- There's no need to do this any more, but looks cleaner and easier to
understand the behavior.
- Make error messages from vCard composer translatable.
- Stop using showDialog() when it is not appropriate.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 68751db..546238a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -419,5 +419,7 @@
<activity android:name=".ImportVCardActivity"
android:theme="@style/BackgroundOnly" />
+ <activity android:name=".ExportVCardActivity"
+ android:theme="@style/BackgroundOnly" />
</application>
</manifest>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 1af6162..cf1d419 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -42,8 +42,7 @@
<item type="id" name="dialog_error_with_message" />
<!-- For ExportVCard -->
- <item type="id" name="dialog_confirm_export_vcard" />
- <item type="id" name="dialog_exporting_vcard" />
+ <item type="id" name="dialog_export_confirmation" />
<item type="id" name="dialog_fail_to_export_with_reason" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2cbfb02..5bf5bcf 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -810,6 +810,12 @@
<!-- 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>
+ <!-- The message for exporting all contacts in the phone, without caring its Account -->
+ <string name="export_all_contacts">All contacts</string>
+
+ <!-- The message for exporting only contacts is the phone locally, which are not associated with any account -->
+ <string name="export_phone_local_only">Locally stored contacts</string>
+
<!-- The menu item that launches VCard export activity -->
<string name="export_contact_list">Export contacts</string>
@@ -825,6 +831,9 @@
<!-- Dialog message shown when exporting Contact data failed -->
<string name="exporting_contact_failed_message">Failed to export contact data.\nReason for failure: \"<xliff:g id="fail_reason">%s</xliff:g>\"</string>
+ <!-- The failed reason: "There is no exportable contact" -->
+ <string name="fail_reason_no_exportable_contact">There is no exportable contact</string>
+
<!-- The failed reason: "Too many vcard files on the SD Card" -->
<string name="fail_reason_too_many_vcard">Too many vCard files on the SD card</string>
@@ -846,6 +855,15 @@
<!-- 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 error reason the vCard composer emits: "Failed to get database information" -->
+ <string name="composer_failed_to_get_database_infomation">Failed to get database information</string>
+
+ <!-- The error reason the vCard composer emits: "There is no exportable contact. You might choose un-exportable data" -->
+ <string name="composer_has_no_exportable_contact">There is no exportable contact. You might choose un-exportable data</string>
+
+ <!-- The error reason the vCard composer emits: "The vCard composer object is not correctly initialized" -->
+ <string name="composer_not_initialized">The vCard composer is not correctly initialized</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>
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 072725a..d446efe 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -17,11 +17,11 @@
package com.android.contacts;
import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.GoogleSource;
import com.android.contacts.model.Sources;
import com.android.contacts.ui.DisplayGroupsActivity;
import com.android.contacts.ui.DisplayGroupsActivity.Prefs;
import com.android.contacts.util.Constants;
+import com.android.contacts.util.AccountSelectionUtil;
import android.accounts.Account;
import android.app.Activity;
@@ -301,15 +301,6 @@
static final String KEY_PICKER_MODE = "picker_mode";
- /*
- * TODO: Will be commented in the really near future. Similar commented out codes will be done.
- * These are for make vCard exporter code understard two additional options:
- * "export contacts in the phone only" and
- * "export all contacts without caring Account information".
- * static final Account sExportPhoneLocalAccount;
- * static final Account sExportAllAccount;
- */
-
private ContactItemListAdapter mAdapter;
int mMode = MODE_DEFAULT;
@@ -351,8 +342,6 @@
*/
private String mQueryData;
- private VCardExporter mVCardExporter;
-
private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER + "=1";
@@ -362,8 +351,6 @@
static {
sContactsIdMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sContactsIdMatcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
- /*sExportPhoneLocalAccount = new Account("Phone-local Account", "phone-local");
- sExportAllAccount = new Account("All account", "all");*/
}
private class DeleteClickListener implements DialogInterface.OnClickListener {
@@ -755,21 +742,6 @@
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchManager.stopSearch();
}
-
- // When the orientation is changed or Home button is pressed, onStop() is called.
- // Then, we stop exporting just for safety.
- //
- // Technically, it is because the dialog displaying the current status of export is
- // closed on this method call and we cannot reliably restore the dialog in the current
- // implementation, while the thread for exporting vCard is working at that time, without
- // showing its status to users :(
- // Also, it is probably not a strong requirment for us to both
- // - enable users to press Home, slide hardware keyboard during the export
- // - and also let vCard export to keep working.
- if (mVCardExporter != null) {
- mVCardExporter.cancelExport();
- mVCardExporter = null;
- }
}
@Override
@@ -812,7 +784,7 @@
return true;
}
case R.id.menu_import_export: {
- showDialog(R.id.dialog_import_export);
+ displayImportExportDialog();
return true;
}
case R.id.menu_accounts: {
@@ -830,31 +802,6 @@
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
- case R.id.dialog_import_export: {
- return createImportExportDialog();
- }
- case R.string.import_from_sim:
- case R.string.import_from_sdcard:
- case R.string.export_to_sdcard: {
- return createSelectAccountDialog(id);
- }
- case R.string.fail_reason_too_many_vcard: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.exporting_contact_failed_title)
- .setMessage(getString(R.string.exporting_contact_failed_message,
- getString(R.string.fail_reason_too_many_vcard)))
- .setPositiveButton(android.R.string.ok, null)
- .create();
- }
- case R.id.dialog_confirm_export_vcard: {
- return mVCardExporter.getExportConfirmationDialog();
- }
- case R.id.dialog_exporting_vcard: {
- return mVCardExporter.getExportingVCardDialog();
- }
- case R.id.dialog_fail_to_export_with_reason: {
- return mVCardExporter.getErrorDialogWithReason();
- }
case R.id.dialog_sdcard_not_found: {
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(R.string.no_sdcard_title)
@@ -866,104 +813,11 @@
return super.onCreateDialog(id);
}
- private class AccountSelectedListener
- implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
-
- final private List<Account> mAccountList;
- final private int mResId;
-
- public AccountSelectedListener(List<Account> accountList, int resId) {
- if (accountList == null || accountList.size() == 0) {
- Log.e(TAG, "The size of Account list is 0.");
- }
- mAccountList = accountList;
- mResId = resId;
- }
-
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- switch (mResId) {
- case R.string.import_from_sim: {
- doImportFromSim(mAccountList.get(which));
- break;
- }
- case R.string.import_from_sdcard: {
- doImportFromSdCard(mAccountList.get(which));
- break;
- }
- /*case R.string.export_to_sdcard: {
- }*/
- }
- }
-
- public void onCancel(DialogInterface dialog) {
- dialog.dismiss();
- }
- }
-
- private Dialog createSelectAccountDialog(int resId) {
- final boolean isExport = (resId == R.string.export_to_sdcard);
- final boolean displayWritableOnly = !isExport;
-
- final Sources sources = Sources.getInstance(this);
- final List<Account> accountList = sources.getAccounts(displayWritableOnly);
-
- /*if (isExport) {
- accountList.add(sExportPhoneLocalAccount);
- accountList.add(sExportAllAccount);
- }*/
-
- // Assume accountList.size() > 1
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(
- this, android.R.style.Theme_Light);
- final LayoutInflater dialogInflater = (LayoutInflater)dialogContext
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- final ArrayAdapter<Account> accountAdapter =
- new ArrayAdapter<Account>(this, android.R.layout.simple_list_item_2, accountList) {
-
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- if (convertView == null) {
- convertView = dialogInflater.inflate(
- android.R.layout.simple_list_item_2,
- parent, false);
- }
-
- // TODO: show icon along with title
- final TextView text1 =
- (TextView)convertView.findViewById(android.R.id.text1);
- final TextView text2 =
- (TextView)convertView.findViewById(android.R.id.text2);
-
- final Account account = this.getItem(position);
- final ContactsSource source =
- sources.getInflatedSource(account.type,
- ContactsSource.LEVEL_SUMMARY);
-
- text1.setText(account.name);
- text2.setText(source.getDisplayLabel(ContactsListActivity.this));
-
- return convertView;
- }
- };
-
- AccountSelectedListener listener = new AccountSelectedListener(accountList, resId);
- final AlertDialog.Builder builder =
- new AlertDialog.Builder(this)
- .setTitle(R.string.dialog_new_contact_account)
- .setSingleChoiceItems(accountAdapter, 0, listener)
- .setOnCancelListener(listener);
- return builder.create();
- }
-
/**
* Create a {@link Dialog} that allows the user to pick from a bulk import
* or bulk export task across all contacts.
*/
- private Dialog createImportExportDialog() {
+ private void displayImportExportDialog() {
// Wrap our context to inflate list items using correct theme
final Context dialogContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
final Resources res = dialogContext.getResources();
@@ -996,7 +850,8 @@
adapter.add(R.string.export_to_sdcard);
}
- final DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
@@ -1004,12 +859,13 @@
switch (resId) {
case R.string.import_from_sim:
case R.string.import_from_sdcard: {
- handleImportExportRequest(resId);
+ handleImportRequest(resId);
break;
}
case R.string.export_to_sdcard: {
- // TODO: use handleImportExportRequest()
- doExportToSdCard();
+ Context context = ContactsListActivity.this;
+ Intent exportIntent = new Intent(context, ExportVCardActivity.class);
+ context.startActivity(exportIntent);
break;
}
default: {
@@ -1020,93 +876,27 @@
}
};
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.dialog_import_export);
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setSingleChoiceItems(adapter, -1, clickListener);
- return builder.create();
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.dialog_import_export)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setSingleChoiceItems(adapter, -1, clickListener)
+ .show();
}
- private void handleImportExportRequest(int resId) {
- /*final boolean isExport = (resId == R.string.export_to_sdcard);
- if (isExport) {
- // We're sure there are at least two Account ("phone-local" and "all").
- // Go to account selection every time.
- showDialog(R.string.export_to_sdcard);
- return;
- }*/
-
- // As for import, there's three possibilities
+ private void handleImportRequest(int resId) {
+ // There's three possibilities:
// - more than one accounts -> ask the user
// - just one account -> use the account without asking the user
// - no account -> use phone-local storage without asking the user
-
final Sources sources = Sources.getInstance(this);
final List<Account> accountList = sources.getAccounts(true);
final int size = accountList.size();
- Account account;
if (size > 1) {
showDialog(resId);
return;
- } else if (size == 1) {
- account = accountList.get(0);
- } else {
- account = null;
- }
- switch (resId) {
- case R.string.import_from_sim: {
- doImportFromSim(account);
- break;
- }
- case R.string.import_from_sdcard: {
- doImportFromSdCard(account);
- break;
- }
- /*case R.string.export_to_sdcard: {
- doExportToSdCard(account);
- break;
- }*/
- }
- }
-
- private void doImportFromSim(Account account) {
- if (account != null) {
- GoogleSource.createMyContactsIfNotExist(account, this);
}
- Intent importIntent = new Intent(Intent.ACTION_VIEW);
- importIntent.setType("vnd.android.cursor.item/sim-contact");
- if (account != null) {
- importIntent.putExtra("account_name", account.name);
- importIntent.putExtra("account_type", account.type);
- }
- importIntent.setClassName("com.android.phone", "com.android.phone.SimContacts");
- startActivity(importIntent);
- }
-
- private void doImportFromSdCard(Account account) {
- if (account != null) {
- GoogleSource.createMyContactsIfNotExist(account, this);
- }
-
- Intent importIntent = new Intent(this, ImportVCardActivity.class);
- if (account != null) {
- importIntent.putExtra("account_name", account.name);
- importIntent.putExtra("account_type", account.type);
- }
- startActivity(importIntent);
- }
-
- private void doExportToSdCard() {
- mVCardExporter = new VCardExporter(this);
- mVCardExporter.startExportVCardToSdCard();
- }
-
- /**
- * Used when VCardExporter finishes its exporting.
- */
- /* package */ void removeReferenceToVCardExporter() {
- mVCardExporter = null;
+ AccountSelectionUtil.doImport(this, resId, (size == 1 ? accountList.get(0) : null));
}
@Override
diff --git a/src/com/android/contacts/ExportVCardActivity.java b/src/com/android/contacts/ExportVCardActivity.java
new file mode 100644
index 0000000..08f43c1
--- /dev/null
+++ b/src/com/android/contacts/ExportVCardActivity.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.pim.vcard.VCardComposer;
+import android.provider.ContactsContract.Contacts;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ExportVCardActivity extends Activity {
+ private static final String LOG_TAG = "ExportVCardActivity";
+
+ // If true, VCardExporter is able to emits files longer than 8.3 format.
+ private static final boolean ALLOW_LONG_FILE_NAME = false;
+ private String mTargetDirectory;
+ private String mFileNamePrefix;
+ private String mFileNameSuffix;
+ private int mFileIndexMinimum;
+ private int mFileIndexMaximum;
+ private String mFileNameExtension;
+ private String mVCardTypeStr;
+ private Set<String> mExtensionsToConsider;
+
+ private ProgressDialog mProgressDialog;
+
+ private Handler mHandler = new Handler();
+
+ // Used temporaly when asking users to confirm the file name
+ private String mTargetFileName;
+
+ // String for storing error reason temporaly.
+ private String mErrorReason;
+
+ private ActualExportThread mActualExportThread;
+
+ 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 ErrorReasonDisplayer implements Runnable {
+ private final int mResId;
+ public ErrorReasonDisplayer(int resId) {
+ mResId = resId;
+ }
+ public ErrorReasonDisplayer(String errorReason) {
+ mResId = R.id.dialog_fail_to_export_with_reason;
+ mErrorReason = errorReason;
+ }
+ public void run() {
+ // Show the Dialog only when the parent Activity is still alive.
+ if (!ExportVCardActivity.this.isFinishing()) {
+ showDialog(mResId);
+ }
+ }
+ }
+
+ private class ExportConfirmationListener implements DialogInterface.OnClickListener {
+ private final String mFileName;
+
+ public ExportConfirmationListener(String fileName) {
+ mFileName = fileName;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mActualExportThread = new ActualExportThread(mFileName);
+ String title = getString(R.string.exporting_contact_list_title);
+ String message = getString(R.string.exporting_contact_list_message, mFileName);
+ mProgressDialog = new ProgressDialog(ExportVCardActivity.this);
+ mProgressDialog.setTitle(title);
+ mProgressDialog.setMessage(message);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ mProgressDialog.setOnCancelListener(mActualExportThread);
+ mProgressDialog.show();
+ mActualExportThread.start();
+ }
+ }
+ }
+
+ private class ActualExportThread extends Thread
+ implements DialogInterface.OnCancelListener {
+ private PowerManager.WakeLock mWakeLock;
+ private String mFileName;
+ private boolean mCanceled = false;
+
+ public ActualExportThread(String fileName) {
+ mFileName = fileName;
+ PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(
+ PowerManager.SCREEN_DIM_WAKE_LOCK |
+ PowerManager.ON_AFTER_RELEASE, LOG_TAG);
+ }
+
+ @Override
+ public void run() {
+ boolean shouldCallFinish = true;
+ mWakeLock.acquire();
+ VCardComposer composer = null;
+ try {
+ OutputStream outputStream = null;
+ try {
+ outputStream = new FileOutputStream(mFileName);
+ } catch (FileNotFoundException e) {
+ final String errorReason =
+ getString(R.string.fail_reason_could_not_open_file,
+ mFileName, e.getMessage());
+ shouldCallFinish = false;
+ mHandler.post(new ErrorReasonDisplayer(errorReason));
+ return;
+ }
+
+ composer = new VCardComposer(ExportVCardActivity.this, mVCardTypeStr, true);
+ // composer = new VCardComposer(ExportVCardActivity,
+ // VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8, true);
+ composer.addHandler(composer.new HandlerForOutputStream(outputStream));
+
+ if (!composer.init()) {
+ final String errorReason = composer.getErrorReason();
+ Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
+ final String translatedErrorReason =
+ translateComposerError(errorReason);
+ mHandler.post(new ErrorReasonDisplayer(
+ getString(R.string.fail_reason_could_not_initialize_exporter,
+ translatedErrorReason)));
+ shouldCallFinish = false;
+ return;
+ }
+
+ int size = composer.getCount();
+
+ if (size == 0) {
+ mHandler.post(new ErrorReasonDisplayer(
+ getString(R.string.fail_reason_no_exportable_contact)));
+ shouldCallFinish = false;
+ return;
+ }
+
+ mProgressDialog.setProgressNumberFormat(
+ getString(R.string.exporting_contact_list_progress));
+ mProgressDialog.setMax(size);
+ mProgressDialog.setProgress(0);
+
+ while (!composer.isAfterLast()) {
+ if (mCanceled) {
+ return;
+ }
+ if (!composer.createOneEntry()) {
+ final String errorReason = composer.getErrorReason();
+ Log.e(LOG_TAG, "Failed to read a contact: " + errorReason);
+ final String translatedErrorReason =
+ translateComposerError(errorReason);
+ mHandler.post(new ErrorReasonDisplayer(
+ getString(R.string.fail_reason_error_occurred_during_export,
+ translatedErrorReason)));
+ shouldCallFinish = false;
+ return;
+ }
+ mProgressDialog.incrementProgressBy(1);
+ }
+ } finally {
+ if (composer != null) {
+ composer.terminate();
+ }
+ mWakeLock.release();
+ mProgressDialog.dismiss();
+ if (shouldCallFinish && !isFinishing()) {
+ finish();
+ }
+ }
+ }
+
+ @Override
+ public void finalize() {
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
+ public void cancel() {
+ mCanceled = true;
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ cancel();
+ }
+ }
+
+ private String translateComposerError(String errorMessage) {
+ Resources resources = getResources();
+ if (VCardComposer.FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO.equals(errorMessage)) {
+ return resources.getString(R.string.composer_failed_to_get_database_infomation);
+ } else if (VCardComposer.FAILURE_REASON_NO_ENTRY.equals(errorMessage)) {
+ return resources.getString(R.string.composer_has_no_exportable_contact);
+ } else if (VCardComposer.FAILURE_REASON_NOT_INITIALIZED.equals(errorMessage)) {
+ return resources.getString(R.string.composer_not_initialized);
+ } else {
+ return errorMessage;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ mTargetDirectory = getString(R.string.config_export_dir);
+ mFileNamePrefix = getString(R.string.config_export_file_prefix);
+ mFileNameSuffix = getString(R.string.config_export_file_suffix);
+ mFileNameExtension = getString(R.string.config_export_file_extension);
+ mVCardTypeStr = getString(R.string.config_export_vcard_type);
+
+ mExtensionsToConsider = new HashSet<String>();
+ mExtensionsToConsider.add(mFileNameExtension);
+
+ final String additionalExtensions =
+ getString(R.string.config_export_extensions_to_consider);
+ if (!TextUtils.isEmpty(additionalExtensions)) {
+ for (String extension : additionalExtensions.split(",")) {
+ String trimed = extension.trim();
+ if (trimed.length() > 0) {
+ mExtensionsToConsider.add(trimed);
+ }
+ }
+ }
+
+ final Resources resources = getResources();
+ mFileIndexMinimum = resources.getInteger(R.integer.config_export_file_min_index);
+ mFileIndexMaximum = resources.getInteger(R.integer.config_export_file_max_index);
+
+ startExportVCardToSdCard();
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case R.id.dialog_export_confirmation: {
+ return getExportConfirmationDialog();
+ }
+ case R.string.fail_reason_too_many_vcard: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.exporting_contact_failed_title)
+ .setMessage(getString(R.string.exporting_contact_failed_message,
+ getString(R.string.fail_reason_too_many_vcard)))
+ .setPositiveButton(android.R.string.ok, mCancelListener)
+ .create();
+ }
+ case R.id.dialog_fail_to_export_with_reason: {
+ return getErrorDialogWithReason();
+ }
+ case R.id.dialog_sdcard_not_found: {
+ AlertDialog.Builder builder = 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, mCancelListener);
+ }
+ }
+ return super.onCreateDialog(id);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog) {
+ if (id == R.id.dialog_fail_to_export_with_reason) {
+ ((AlertDialog)dialog).setMessage(getErrorReason());
+ } else if (id == R.id.dialog_export_confirmation) {
+ ((AlertDialog)dialog).setMessage(
+ getString(R.string.confirm_export_message, mTargetFileName));
+ } else {
+ super.onPrepareDialog(id, dialog);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mActualExportThread != null) {
+ // The Activity is no longer visible. Stop the thread.
+ mActualExportThread.cancel();
+ mActualExportThread = null;
+ }
+
+ if (!isFinishing()) {
+ finish();
+ }
+ }
+
+ /**
+ * Tries to start exporting VCard. If there's no SDCard available,
+ * an error dialog is shown.
+ */
+ public void startExportVCardToSdCard() {
+ File targetDirectory = new File(mTargetDirectory);
+
+ if (!(targetDirectory.exists() &&
+ targetDirectory.isDirectory() &&
+ targetDirectory.canRead()) &&
+ !targetDirectory.mkdirs()) {
+ showDialog(R.id.dialog_sdcard_not_found);
+ } else {
+ mTargetFileName = getAppropriateFileName(mTargetDirectory);
+ if (TextUtils.isEmpty(mTargetFileName)) {
+ mTargetFileName = null;
+ // finish() is called via the error dialog. Do not call the method here.
+ return;
+ }
+
+ showDialog(R.id.dialog_export_confirmation);
+ }
+ }
+
+ /**
+ * Tries to get an appropriate filename. Returns null if it fails.
+ */
+ private String getAppropriateFileName(final String destDirectory) {
+ int fileNumberStringLength = 0;
+ {
+ // Calling Math.Log10() is costly.
+ int tmp;
+ for (fileNumberStringLength = 0, tmp = mFileIndexMaximum; tmp > 0;
+ fileNumberStringLength++, tmp /= 10) {
+ }
+ }
+ String bodyFormat = "%s%0" + fileNumberStringLength + "d%s";
+
+ if (!ALLOW_LONG_FILE_NAME) {
+ String possibleBody = String.format(bodyFormat,mFileNamePrefix, 1, mFileNameSuffix);
+ if (possibleBody.length() > 8 || mFileNameExtension.length() > 3) {
+ Log.e(LOG_TAG, "This code does not allow any long file name.");
+ mErrorReason = getString(R.string.fail_reason_too_long_filename,
+ String.format("%s.%s", possibleBody, mFileNameExtension));
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ // finish() is called via the error dialog. Do not call the method here.
+ return null;
+ }
+ }
+
+ // Note that this logic assumes that the target directory is case insensitive.
+ // As of 2009-07-16, it is true since the external storage is only sdcard, and
+ // it is formated as FAT/VFAT.
+ // TODO: fix this.
+ for (int i = mFileIndexMinimum; i <= mFileIndexMaximum; i++) {
+ boolean numberIsAvailable = true;
+ // SD Association's specification seems to require this feature, though we cannot
+ // have the specification since it is proprietary...
+ String body = null;
+ for (String possibleExtension : mExtensionsToConsider) {
+ body = String.format(bodyFormat, mFileNamePrefix, i, mFileNameSuffix);
+ File file = new File(String.format("%s/%s.%s",
+ destDirectory, body, possibleExtension));
+ if (file.exists()) {
+ numberIsAvailable = false;
+ break;
+ }
+ }
+ if (numberIsAvailable) {
+ return String.format("%s/%s.%s", destDirectory, body, mFileNameExtension);
+ }
+ }
+ showDialog(R.string.fail_reason_too_many_vcard);
+ return null;
+ }
+
+ public Dialog getExportConfirmationDialog() {
+ if (TextUtils.isEmpty(mTargetFileName)) {
+ Log.e(LOG_TAG, "Target file name is empty, which must not be!");
+ // This situation is not acceptable (probably a bug!), but we don't have no reason to
+ // show...
+ mErrorReason = null;
+ return getErrorDialogWithReason();
+ }
+
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.confirm_export_title)
+ .setMessage(getString(R.string.confirm_export_message, mTargetFileName))
+ .setPositiveButton(android.R.string.ok,
+ new ExportConfirmationListener(mTargetFileName))
+ .setNegativeButton(android.R.string.cancel, mCancelListener)
+ .setOnCancelListener(mCancelListener)
+ .create();
+ }
+
+ public Dialog getErrorDialogWithReason() {
+ if (mErrorReason == null) {
+ Log.e(LOG_TAG, "Error reason must have been set.");
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ }
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.exporting_contact_failed_title)
+ .setMessage(getString(R.string.exporting_contact_failed_message, mErrorReason))
+ .setPositiveButton(android.R.string.ok, mCancelListener)
+ .setOnCancelListener(mCancelListener)
+ .create();
+ }
+
+ public void cancelExport() {
+ if (mActualExportThread != null) {
+ mActualExportThread.cancel();
+ mActualExportThread = null;
+ }
+ }
+
+ public String getErrorReason() {
+ return mErrorReason;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
index e77a3e8..64b5727 100644
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -531,22 +531,14 @@
mProgressDialogForScanVCard = null;
if (mGotIOException) {
- mHandler.post(new Runnable() {
- public void run() {
- showDialog(R.id.dialog_io_exception);
- }
- });
+ mHandler.post(new DialogDisplayer(R.id.dialog_io_exception));
} else if (mCanceled) {
finish();
} else {
int size = mAllVCardFileList.size();
final Context context = ImportVCardActivity.this;
if (size == 0) {
- mHandler.post(new Runnable() {
- public void run() {
- showDialog(R.id.dialog_vcard_not_found);
- }
- });
+ mHandler.post(new DialogDisplayer(R.id.dialog_vcard_not_found));
} else {
startVCardSelectAndImport();
}
diff --git a/src/com/android/contacts/VCardExporter.java b/src/com/android/contacts/VCardExporter.java
deleted file mode 100644
index 5f13e99..0000000
--- a/src/com/android/contacts/VCardExporter.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.contacts;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.pim.vcard.VCardComposer;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.util.HashSet;
-import java.util.Set;
-
-public class VCardExporter {
- private static final String LOG_TAG = "VCardExporter";
-
- // If true, VCardExporter is able to emits files longer than 8.3 format.
- private static final boolean ALLOW_LONG_FILE_NAME = false;
- private final String mTargetDirectory;
- private final String mFileNamePrefix;
- private final String mFileNameSuffix;
- private final int mFileIndexMinimum;
- private final int mFileIndexMaximum;
- private final String mFileNameExtension;
- private final String mVCardTypeStr;
- private final Set<String> mExtensionsToConsider;
-
- private ContactsListActivity mParentActivity;
- private ProgressDialog mProgressDialog;
-
- // Used temporaly when asking users to confirm the file name
- private String mTargetFileName;
-
- // String for storing error reason temporaly.
- private String mErrorReason;
-
- private ActualExportThread mActualExportThread;
-
- private class ConfirmListener implements DialogInterface.OnClickListener {
- private String mFileName;
-
- public ConfirmListener(String fileName) {
- mFileName = fileName;
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- mActualExportThread = new ActualExportThread(mFileName);
- String title = getString(R.string.exporting_contact_list_title);
- String message = getString(R.string.exporting_contact_list_message, mFileName);
- mProgressDialog = new ProgressDialog(mParentActivity);
- mProgressDialog.setTitle(title);
- mProgressDialog.setMessage(message);
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setOnCancelListener(mActualExportThread);
- mParentActivity.showDialog(R.id.dialog_exporting_vcard);
- mActualExportThread.start();
- }
- }
- }
-
- private class ActualExportThread extends Thread
- implements DialogInterface.OnCancelListener {
- private PowerManager.WakeLock mWakeLock;
- private String mFileName;
- private boolean mCanceled = false;
-
- public ActualExportThread(String fileName) {
- mFileName = fileName;
- PowerManager powerManager = (PowerManager)mParentActivity.getSystemService(
- Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(
- PowerManager.SCREEN_DIM_WAKE_LOCK |
- PowerManager.ON_AFTER_RELEASE, LOG_TAG);
- }
-
- @Override
- public void run() {
- mWakeLock.acquire();
- VCardComposer composer = null;
- try {
- OutputStream outputStream = null;
- try {
- outputStream = new FileOutputStream(mFileName);
- } catch (FileNotFoundException e) {
- mErrorReason = getString(R.string.fail_reason_could_not_open_file,
- mFileName, e.getMessage());
- mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
- return;
- }
-
- composer = new VCardComposer(mParentActivity, mVCardTypeStr, true);
- // composer = new VCardComposer(mParentContext,
- // VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8, true);
- composer.addHandler(composer.new HandlerForOutputStream(outputStream));
-
- if (!composer.init()) {
- mErrorReason = getString(R.string.fail_reason_could_not_initialize_exporter,
- composer.getErrorReason());
- mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
- return;
- }
-
- int size = composer.getCount();
-
- mProgressDialog.setProgressNumberFormat(
- getString(R.string.exporting_contact_list_progress));
- mProgressDialog.setMax(size);
- mProgressDialog.setProgress(0);
-
- while (!composer.isAfterLast()) {
- if (mCanceled) {
- return;
- }
- if (!composer.createOneEntry()) {
- Log.e(LOG_TAG, "Failed to read a contact.");
- mErrorReason = getString(R.string.fail_reason_error_occurred_during_export,
- composer.getErrorReason());
- mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
- return;
- }
- mProgressDialog.incrementProgressBy(1);
- }
- } finally {
- if (composer != null) {
- composer.terminate();
- }
- mWakeLock.release();
- mProgressDialog.dismiss();
- mParentActivity.removeReferenceToVCardExporter();
- }
- }
-
- @Override
- public void finalize() {
- if (mWakeLock != null && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
-
- public void cancel() {
- mCanceled = true;
- }
-
- public void onCancel(DialogInterface dialog) {
- cancel();
- }
- }
-
- /**
- * @param parentActivity must not be null
- */
- public VCardExporter(ContactsListActivity parentActivity) {
- mParentActivity = parentActivity;
- mTargetDirectory = getString(R.string.config_export_dir);
- mFileNamePrefix = getString(R.string.config_export_file_prefix);
- mFileNameSuffix = getString(R.string.config_export_file_suffix);
- mFileNameExtension = getString(R.string.config_export_file_extension);
- mVCardTypeStr = getString(R.string.config_export_vcard_type);
-
- mExtensionsToConsider = new HashSet<String>();
- mExtensionsToConsider.add(mFileNameExtension);
-
- final String additionalExtensions =
- getString(R.string.config_export_extensions_to_consider);
- if (!TextUtils.isEmpty(additionalExtensions)) {
- for (String extension : additionalExtensions.split(",")) {
- String trimed = extension.trim();
- if (trimed.length() > 0) {
- mExtensionsToConsider.add(trimed);
- }
- }
- }
-
- Resources resources = parentActivity.getResources();
- mFileIndexMinimum = resources.getInteger(R.integer.config_export_file_min_index);
- mFileIndexMaximum = resources.getInteger(R.integer.config_export_file_max_index);
- }
-
- /**
- * Tries to start exporting VCard. If there's no SDCard available,
- * an error dialog is shown.
- */
- public void startExportVCardToSdCard() {
- File targetDirectory = new File(mTargetDirectory);
-
- if (!(targetDirectory.exists() &&
- targetDirectory.isDirectory() &&
- targetDirectory.canRead()) &&
- !targetDirectory.mkdirs()) {
- mParentActivity.showDialog(R.id.dialog_sdcard_not_found);
- } else {
- mTargetFileName = getAppropriateFileName(mTargetDirectory);
- if (TextUtils.isEmpty(mTargetFileName)) {
- mTargetFileName = null;
- return;
- }
-
- mParentActivity.showDialog(R.id.dialog_confirm_export_vcard);
- }
- }
-
- /**
- * Tries to get an appropriate filename. Returns null if it fails.
- */
- private String getAppropriateFileName(final String destDirectory) {
- int fileNumberStringLength = 0;
- {
- // Calling Math.Log10() is costly.
- int tmp;
- for (fileNumberStringLength = 0, tmp = mFileIndexMaximum; tmp > 0;
- fileNumberStringLength++, tmp /= 10) {
- }
- }
- String bodyFormat = "%s%0" + fileNumberStringLength + "d%s";
-
- if (!ALLOW_LONG_FILE_NAME) {
- String possibleBody = String.format(bodyFormat,mFileNamePrefix, 1, mFileNameSuffix);
- if (possibleBody.length() > 8 || mFileNameExtension.length() > 3) {
- Log.e(LOG_TAG, "This code does not allow any long file name.");
- mErrorReason = getString(R.string.fail_reason_too_long_filename,
- String.format("%s.%s", possibleBody, mFileNameExtension));
- mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
- return null;
- }
- }
-
- // Note that this logic assumes that the target directory is case insensitive.
- // As of 2009-07-16, it is true since the external storage is only sdcard, and
- // it is formated as FAT/VFAT.
- // TODO: fix this.
- for (int i = mFileIndexMinimum; i <= mFileIndexMaximum; i++) {
- boolean numberIsAvailable = true;
- // SD Association's specification seems to require this feature, though we cannot
- // have the specification since it is proprietary...
- String body = null;
- for (String possibleExtension : mExtensionsToConsider) {
- body = String.format(bodyFormat, mFileNamePrefix, i, mFileNameSuffix);
- File file = new File(String.format("%s/%s.%s",
- destDirectory, body, possibleExtension));
- if (file.exists()) {
- numberIsAvailable = false;
- break;
- }
- }
- if (numberIsAvailable) {
- return String.format("%s/%s.%s", destDirectory, body, mFileNameExtension);
- }
- }
- mParentActivity.showDialog(R.string.fail_reason_too_many_vcard);
- return null;
- }
-
- public Dialog getExportConfirmationDialog() {
- if (TextUtils.isEmpty(mTargetFileName)) {
- Log.e(LOG_TAG, "Target file name is empty, which must not be!");
- // This situation is not acceptable (probably a bug!), but we don't have no reason to
- // show...
- mErrorReason = null;
- return getErrorDialogWithReason();
- }
-
- return new AlertDialog.Builder(mParentActivity)
- .setTitle(R.string.confirm_export_title)
- .setMessage(getString(R.string.confirm_export_message, mTargetFileName))
- .setPositiveButton(android.R.string.ok, new ConfirmListener(mTargetFileName))
- .setNegativeButton(android.R.string.cancel, null)
- .create();
- }
-
- public Dialog getExportingVCardDialog() {
- return mProgressDialog;
- }
-
- public Dialog getErrorDialogWithReason() {
- if (mErrorReason == null) {
- Log.e(LOG_TAG, "Error reason must have been set.");
- mErrorReason = getString(R.string.fail_reason_unknown);
- }
- return new AlertDialog.Builder(mParentActivity)
- .setTitle(R.string.exporting_contact_failed_title)
- .setMessage(getString(R.string.exporting_contact_failed_message, mErrorReason))
- .setPositiveButton(android.R.string.ok, null)
- .create();
- }
-
- public void cancelExport() {
- if (mActualExportThread != null) {
- mActualExportThread.cancel();
- mActualExportThread = null;
- }
- }
-
- private String getString(int resId, Object... formatArgs) {
- return mParentActivity.getString(resId, formatArgs);
- }
-
- private String getString(int resId) {
- return mParentActivity.getString(resId);
- }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/util/AccountSelectionUtil.java b/src/com/android/contacts/util/AccountSelectionUtil.java
new file mode 100644
index 0000000..86c04eb
--- /dev/null
+++ b/src/com/android/contacts/util/AccountSelectionUtil.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.util;
+
+import com.android.contacts.ImportVCardActivity;
+import com.android.contacts.R;
+import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.GoogleSource;
+import com.android.contacts.model.Sources;
+
+import android.accounts.Account;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Utility class for selectiong an Account for importing contact(s)
+ */
+public class AccountSelectionUtil {
+ // TODO: maybe useful for EditContactActivity.java...
+ private static final String LOG_TAG = "AccountSelectionUtil";
+
+ private static class AccountSelectedListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+
+ final private Context mContext;
+ final private List<Account> mAccountList;
+ final private int mResId;
+
+ public AccountSelectedListener(Context context, List<Account> accountList, int resId) {
+ if (accountList == null || accountList.size() == 0) {
+ Log.e(LOG_TAG, "The size of Account list is 0.");
+ }
+ mContext = context;
+ mAccountList = accountList;
+ mResId = resId;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ doImport(mContext, mResId, mAccountList.get(which));
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ dialog.dismiss();
+ }
+ }
+
+ public static void displaySelectAccountDialog(Context context, int resId) {
+ displaySelectAccountDialog(context, resId, null);
+ }
+
+ public static void displaySelectAccountDialog(Context context, int resId,
+ DialogInterface.OnCancelListener onCancelListener) {
+ final Sources sources = Sources.getInstance(context);
+ final List<Account> writableAccountList = sources.getAccounts(true);
+
+ // Assume accountList.size() > 1
+
+ // Wrap our context to inflate list items using correct theme
+ final Context dialogContext = new ContextThemeWrapper(
+ context, android.R.style.Theme_Light);
+ final LayoutInflater dialogInflater = (LayoutInflater)dialogContext
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final ArrayAdapter<Account> accountAdapter =
+ new ArrayAdapter<Account>(context, android.R.layout.simple_list_item_2,
+ writableAccountList) {
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = dialogInflater.inflate(
+ android.R.layout.simple_list_item_2,
+ parent, false);
+ }
+
+ // TODO: show icon along with title
+ final TextView text1 =
+ (TextView)convertView.findViewById(android.R.id.text1);
+ final TextView text2 =
+ (TextView)convertView.findViewById(android.R.id.text2);
+
+ final Account account = this.getItem(position);
+ final ContactsSource source =
+ sources.getInflatedSource(account.type,
+ ContactsSource.LEVEL_SUMMARY);
+ final Context context = getContext();
+
+ text1.setText(account.name);
+ text2.setText(source.getDisplayLabel(context));
+
+ return convertView;
+ }
+ };
+
+ AccountSelectedListener accountSelectedListener =
+ new AccountSelectedListener(context, writableAccountList, resId);
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.dialog_new_contact_account)
+ .setSingleChoiceItems(accountAdapter, 0, accountSelectedListener)
+ .setOnCancelListener(accountSelectedListener)
+ .show();
+ }
+
+ public static void doImport(Context context, int resId, Account account) {
+ switch (resId) {
+ case R.string.import_from_sim: {
+ doImportFromSim(context, account);
+ break;
+ }
+ case R.string.import_from_sdcard: {
+ doImportFromSdCard(context, account);
+ break;
+ }
+ }
+ }
+
+ public static void doImportFromSim(Context context, Account account) {
+ if (account != null) {
+ GoogleSource.createMyContactsIfNotExist(account, context);
+ }
+
+ Intent importIntent = new Intent(Intent.ACTION_VIEW);
+ importIntent.setType("vnd.android.cursor.item/sim-contact");
+ if (account != null) {
+ importIntent.putExtra("account_name", account.name);
+ importIntent.putExtra("account_type", account.type);
+ }
+ importIntent.setClassName("com.android.phone", "com.android.phone.SimContacts");
+ context.startActivity(importIntent);
+ }
+
+ public static void doImportFromSdCard(Context context, Account account) {
+ if (account != null) {
+ GoogleSource.createMyContactsIfNotExist(account, context);
+ }
+
+ Intent importIntent = new Intent(context, ImportVCardActivity.class);
+ if (account != null) {
+ importIntent.putExtra("account_name", account.name);
+ importIntent.putExtra("account_type", account.type);
+ }
+ context.startActivity(importIntent);
+ }
+}