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);
+    }
+}