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