Merge change 26392 into eclair

* changes:
  Fix long names in recent calls screen.
diff --git a/res/values/ids.xml b/res/values/ids.xml
index dbed28a..5746232 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -40,4 +40,9 @@
     <item type="id" name="dialog_reading_vcard" />
     <item type="id" name="dialog_io_exception" />
 
+    <!-- For ExportVCard -->
+    <item type="id" name="dialog_confirm_export_vcard" />
+    <item type="id" name="dialog_exporting_vcard" />
+    <item type="id" name="dialog_fail_to_export_with_reason" />
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f358592..c033f8c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -764,6 +764,10 @@
     <!-- The failed reason: "One or more files failed to be imported. (&lt;a list of file names&gt;)" -->
     <string name="fail_reason_failed_to_read_files">One or more files failed to be imported (%s).</string>
 
+    <!-- The failed reason: "Unknown error". This message should not be shown
+         but it may in some buggy condition. -->
+    <string name="fail_reason_unknown">Unknown error</string>
+
     <!-- Dialog title shown when a user is asked to select VCard file -->
     <string name="select_vcard_title">Select vCard file</string>
 
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index c897317..7661f35 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -346,7 +346,7 @@
      */
     private String mQueryData;
 
-    private Handler mHandler = new Handler();
+    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";
@@ -748,6 +748,21 @@
             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
@@ -815,6 +830,30 @@
             case R.string.import_from_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)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(R.string.no_sdcard_message)
+                .setPositiveButton(android.R.string.ok, null);
+            }
         }
         return super.onCreateDialog(id);
     }
@@ -983,8 +1022,15 @@
     }
 
     private void doExportToSdCard() {
-        VCardExporter exporter = new VCardExporter(ContactsListActivity.this, mHandler);
-        exporter.startExportVCardToSdCard();
+        mVCardExporter = new VCardExporter(this);
+        mVCardExporter.startExportVCardToSdCard();
+    }
+
+    /**
+     * Used when VCardExporter finishes its exporting.
+     */
+    /* package */ void removeReferenceToVCardExporter() {
+        mVCardExporter = null;
     }
 
     @Override
diff --git a/src/com/android/contacts/VCardExporter.java b/src/com/android/contacts/VCardExporter.java
index 2e9ab2c..5f13e99 100644
--- a/src/com/android/contacts/VCardExporter.java
+++ b/src/com/android/contacts/VCardExporter.java
@@ -15,7 +15,9 @@
  */
 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;
@@ -47,24 +49,16 @@
     private final String mVCardTypeStr;
     private final Set<String> mExtensionsToConsider;
 
-    private Context mParentContext;
-    private Handler mParentHandler;
+    private ContactsListActivity mParentActivity;
     private ProgressDialog mProgressDialog;
 
-    private class ErrorMessageDisplayRunnable implements Runnable {
-        private String mReason;
-        public ErrorMessageDisplayRunnable(String reason) {
-            mReason = reason;
-        }
+    // Used temporaly when asking users to confirm the file name
+    private String mTargetFileName;
 
-        public void run() {
-            new AlertDialog.Builder(mParentContext)
-                .setTitle(getString(R.string.exporting_contact_failed_title))
-                .setMessage(getString(R.string.exporting_contact_failed_message, mReason))
-                .setPositiveButton(android.R.string.ok, null)
-                .show();
-        }
-    }
+    // String for storing error reason temporaly.
+    private String mErrorReason;
+
+    private ActualExportThread mActualExportThread;
 
     private class ConfirmListener implements DialogInterface.OnClickListener {
         private String mFileName;
@@ -75,8 +69,16 @@
 
         public void onClick(DialogInterface dialog, int which) {
             if (which == DialogInterface.BUTTON_POSITIVE) {
-                startExport(mFileName);
-            } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+                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();
             }
         }
     }
@@ -89,7 +91,7 @@
 
         public ActualExportThread(String fileName) {
             mFileName = fileName;
-            PowerManager powerManager = (PowerManager)mParentContext.getSystemService(
+            PowerManager powerManager = (PowerManager)mParentActivity.getSystemService(
                     Context.POWER_SERVICE);
             mWakeLock = powerManager.newWakeLock(
                     PowerManager.SCREEN_DIM_WAKE_LOCK |
@@ -105,21 +107,21 @@
                 try {
                     outputStream = new FileOutputStream(mFileName);
                 } catch (FileNotFoundException e) {
-                    String reason = getString(R.string.fail_reason_could_not_open_file,
+                    mErrorReason = getString(R.string.fail_reason_could_not_open_file,
                             mFileName, e.getMessage());
-                    mParentHandler.post(new ErrorMessageDisplayRunnable(reason));
+                    mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
                     return;
                 }
 
-                composer = new VCardComposer(mParentContext, mVCardTypeStr, true);
+                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()) {
-                    String reason = getString(R.string.fail_reason_could_not_initialize_exporter,
+                    mErrorReason = getString(R.string.fail_reason_could_not_initialize_exporter,
                             composer.getErrorReason());
-                    mParentHandler.post(new ErrorMessageDisplayRunnable(reason));
+                    mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
                     return;
                 }
 
@@ -136,9 +138,9 @@
                     }
                     if (!composer.createOneEntry()) {
                         Log.e(LOG_TAG, "Failed to read a contact.");
-                        String reason = getString(R.string.fail_reason_error_occurred_during_export,
+                        mErrorReason = getString(R.string.fail_reason_error_occurred_during_export,
                                 composer.getErrorReason());
-                        mParentHandler.post(new ErrorMessageDisplayRunnable(reason));
+                        mParentActivity.showDialog(R.id.dialog_fail_to_export_with_reason);
                         return;
                     }
                     mProgressDialog.incrementProgressBy(1);
@@ -149,6 +151,7 @@
                 }
                 mWakeLock.release();
                 mProgressDialog.dismiss();
+                mParentActivity.removeReferenceToVCardExporter();
             }
         }
 
@@ -159,18 +162,20 @@
             }
         }
 
-        public void onCancel(DialogInterface dialog) {
+        public void cancel() {
             mCanceled = true;
         }
+
+        public void onCancel(DialogInterface dialog) {
+            cancel();
+        }
     }
 
     /**
-     * @param parentContext must not be null
-     * @param parentHandler must not be null
+     * @param parentActivity must not be null
      */
-    public VCardExporter(Context parentContext, Handler parentHandler) {
-        mParentContext = parentContext;
-        mParentHandler = parentHandler;
+    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);
@@ -191,7 +196,7 @@
             }
         }
 
-        Resources resources = parentContext.getResources();
+        Resources resources = parentActivity.getResources();
         mFileIndexMinimum = resources.getInteger(R.integer.config_export_file_min_index);
         mFileIndexMaximum = resources.getInteger(R.integer.config_export_file_max_index);
     }
@@ -207,24 +212,15 @@
                 targetDirectory.isDirectory() &&
                 targetDirectory.canRead()) &&
                 !targetDirectory.mkdirs()) {
-            new AlertDialog.Builder(mParentContext)
-                    .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)
-                    .show();
+            mParentActivity.showDialog(R.id.dialog_sdcard_not_found);
         } else {
-            String fileName = getAppropriateFileName(mTargetDirectory);
-            if (TextUtils.isEmpty(fileName)) {
+            mTargetFileName = getAppropriateFileName(mTargetDirectory);
+            if (TextUtils.isEmpty(mTargetFileName)) {
+                mTargetFileName = null;
                 return;
             }
 
-            new AlertDialog.Builder(mParentContext)
-                .setTitle(R.string.confirm_export_title)
-                .setMessage(getString(R.string.confirm_export_message, fileName))
-                .setPositiveButton(android.R.string.ok, new ConfirmListener(fileName))
-                .setNegativeButton(android.R.string.cancel, null)
-                .show();
+            mParentActivity.showDialog(R.id.dialog_confirm_export_vcard);
         }
     }
 
@@ -246,8 +242,9 @@
             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.");
-                displayErrorMessage(getString(R.string.fail_reason_too_long_filename,
-                        String.format("%s.%s", possibleBody, mFileNameExtension)));
+                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;
             }
         }
@@ -274,42 +271,55 @@
                 return String.format("%s/%s.%s", destDirectory, body, mFileNameExtension);
             }
         }
-        displayErrorMessage(getString(R.string.fail_reason_too_many_vcard));
+        mParentActivity.showDialog(R.string.fail_reason_too_many_vcard);
         return null;
     }
 
-    private void startExport(String fileName) {
-        ActualExportThread thread = new ActualExportThread(fileName);
-        displayReadingVCardDialog(thread, fileName);
-        thread.start();
+    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();
     }
 
-    private void displayReadingVCardDialog(DialogInterface.OnCancelListener listener,
-            String fileName) {
-        String title = getString(R.string.exporting_contact_list_title);
-        String message = getString(R.string.exporting_contact_list_message, fileName);
-        mProgressDialog = new ProgressDialog(mParentContext);
-        mProgressDialog.setTitle(title);
-        mProgressDialog.setMessage(message);
-        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
-        mProgressDialog.setOnCancelListener(listener);
-        mProgressDialog.show();
+    public Dialog getExportingVCardDialog() {
+        return mProgressDialog;
     }
 
-    private void displayErrorMessage(String failureReason) {
-        new AlertDialog.Builder(mParentContext)
+    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,
-                    failureReason))
+                .setMessage(getString(R.string.exporting_contact_failed_message, mErrorReason))
             .setPositiveButton(android.R.string.ok, null)
-            .show();
+            .create();
+    }
+
+    public void cancelExport() {
+        if (mActualExportThread != null) {
+            mActualExportThread.cancel();
+            mActualExportThread = null;
+        }
     }
 
     private String getString(int resId, Object... formatArgs) {
-        return mParentContext.getString(resId, formatArgs);
+        return mParentActivity.getString(resId, formatArgs);
     }
 
     private String getString(int resId) {
-        return mParentContext.getString(resId);
+        return mParentActivity.getString(resId);
     }
 }
\ No newline at end of file