am 8cd49472: Preventing a crash on rotation of the Display Options activity
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
index dcd0b74..f019f88 100644
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -220,13 +220,15 @@
                     boolean result;
                     try {
                         result = readOneVCardFile(targetUri,
-                                VCardConfig.DEFAULT_CHARSET, builderCollection, null, true, null);
+                                VCardConfig.DEFAULT_IMPORT_CHARSET,
+                                builderCollection, null, true, null);
                     } catch (VCardNestedException e) {
                         try {
                             // Assume that VCardSourceDetector was able to detect the source.
                             // Try again with the detector.
                             result = readOneVCardFile(targetUri,
-                                    VCardConfig.DEFAULT_CHARSET, counter, detector, false, null);
+                                    VCardConfig.DEFAULT_IMPORT_CHARSET,
+                                    counter, detector, false, null);
                         } catch (VCardNestedException e2) {
                             result = false;
                             Log.e(LOG_TAG, "Must not reach here. " + e2);
@@ -265,7 +267,8 @@
 
                         VCardSourceDetector detector = new VCardSourceDetector();
                         try {
-                            if (!readOneVCardFile(targetUri, VCardConfig.DEFAULT_CHARSET,
+                            if (!readOneVCardFile(targetUri,
+                                    VCardConfig.DEFAULT_IMPORT_CHARSET,
                                     detector, null, true, mErrorFileNameList)) {
                                 continue;
                             }
@@ -337,7 +340,7 @@
             if (charset != null) {
                 builder = new VCardEntryConstructor(charset, charset, false, vcardType, mAccount);
             } else {
-                charset = VCardConfig.DEFAULT_CHARSET;
+                charset = VCardConfig.DEFAULT_IMPORT_CHARSET;
                 builder = new VCardEntryConstructor(null, null, false, vcardType, mAccount);
             }
             VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
diff --git a/src/com/android/contacts/ui/DialogManager.java b/src/com/android/contacts/ui/DialogManager.java
new file mode 100644
index 0000000..469af06
--- /dev/null
+++ b/src/com/android/contacts/ui/DialogManager.java
@@ -0,0 +1,114 @@
+package com.android.contacts.ui;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Manages creation and destruction of Dialogs that are to be shown by Views. Unlike how Dialogs
+ * are regularly used, the Dialogs are not recycled but immediately destroyed after dismissal.
+ * To be able to do that, two IDs are required which are used consecutively.
+ * How to use:<ul>
+ * <li>The owning Activity creates on instance of this class, passing itself and two Ids that are
+ *    not used by other Dialogs of the Activity.</li>
+ * <li>Views owning Dialogs must implement {@link DialogManager.DialogShowingView}</li>
+ * <li>After creating the Views, configureManagingViews must be called to configure all views
+ *    that implement {@link DialogManager.DialogShowingView}</li>
+ * <li>In the implementation of {@link Activity#onCreateDialog}, calls for the
+ *    ViewId are forwarded to {@link DialogManager#onCreateDialog(int, Bundle)}</li>
+ * </ul>
+ * To actually show a Dialog, the View uses {@link DialogManager#showDialogInView(View, Bundle)},
+ * passing itself as a first parameter
+ */
+public class DialogManager {
+    private final Activity mActivity;
+    private final int mDialogId1;
+    private final int mDialogId2;
+    private boolean mUseDialogId2 = false;
+    public final static String VIEW_ID_KEY = "view_id";
+
+    /**
+     * Creates a new instance of this class for the given Activity.
+     * @param activity The activity this object is used for
+     * @param dialogId1 The first Id that is reserved for use by child-views
+     * @param dialogId2 The second Id that is reserved for use by child-views
+     */
+    public DialogManager(final Activity activity, final int dialogId1, final int dialogId2) {
+        if (activity == null) throw new IllegalArgumentException("activity must not be null");
+        if (dialogId1 == dialogId2) throw new IllegalArgumentException("Ids must be different");
+        mActivity = activity;
+        mDialogId1 = dialogId1;
+        mDialogId2 = dialogId2;
+    }
+
+    /**
+     * Called by a View to show a dialog. It has to pass itself and a Bundle with extra information.
+     * If the view can show several dialogs, it should distinguish them using an item in the Bundle.
+     * The View needs to have a valid and unique Id. This function modifies the bundle by adding a
+     * new item named {@link DialogManager#VIEW_ID_KEY}
+     */
+    public void showDialogInView(final View view, final Bundle bundle) {
+        final int viewId = view.getId();
+        if (bundle.containsKey(VIEW_ID_KEY)) {
+            throw new IllegalArgumentException("Bundle already contains a " + VIEW_ID_KEY);
+        }
+        if (viewId == View.NO_ID) {
+            throw new IllegalArgumentException("View does not have a proper ViewId");
+        }
+        bundle.putInt(VIEW_ID_KEY, viewId);
+        int dialogId = mUseDialogId2 ? mDialogId2 : mDialogId1;
+        mActivity.showDialog(dialogId, bundle);
+    }
+
+    /**
+     * Callback function called by the Activity to handle View-managed Dialogs.
+     * This function returns null if the id is not one of the two reserved Ids.
+     */
+    public Dialog onCreateDialog(final int id, final Bundle bundle) {
+        if (id == mDialogId1) {
+            mUseDialogId2 = true;
+        } else if (id == mDialogId2) {
+            mUseDialogId2 = false;
+        } else {
+            return null;
+        }
+        if (!bundle.containsKey(VIEW_ID_KEY)) {
+            throw new IllegalArgumentException("Bundle does not contain a ViewId");
+        }
+        final int viewId = bundle.getInt(VIEW_ID_KEY);
+        final View view = mActivity.findViewById(viewId);
+        if (view == null || !(view instanceof DialogShowingView)) {
+            return null;
+        }
+        final Dialog dialog = ((DialogShowingView)view).createDialog(bundle);
+
+        // As we will never re-use this dialog, we can completely kill it here
+        dialog.setOnDismissListener(new OnDismissListener() {
+            public void onDismiss(DialogInterface dialogInterface) {
+                mActivity.removeDialog(id);
+            }
+        });
+        return dialog;
+    }
+
+    /**
+     * Interface to implemented by Views that show Dialogs
+     */
+    public interface DialogShowingView {
+        /**
+         * Callback function to create a Dialog. Notice that the DialogManager overwrites the
+         * OnDismissListener on the returned Dialog, so the View should not use this Listener itself
+         */
+        Dialog createDialog(Bundle bundle);
+    }
+
+    /**
+     * Interface to implemented by Activities that host View-showing dialogs
+     */
+    public interface DialogShowingViewActivity {
+        DialogManager getDialogManager();
+    }
+}
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index c70cff6..9452eac 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -34,7 +34,6 @@
 import com.android.contacts.ui.widget.PhotoEditorView;
 import com.android.contacts.util.EmptyService;
 import com.android.contacts.util.WeakAsyncTask;
-import com.google.android.collect.Lists;
 
 import android.accounts.Account;
 import android.app.Activity;
@@ -53,6 +52,7 @@
 import android.content.Intent;
 import android.content.OperationApplicationException;
 import android.content.ContentProviderOperation.Builder;
+import android.content.DialogInterface.OnDismissListener;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.media.MediaScannerConnection;
@@ -94,7 +94,8 @@
  * Activity for editing or inserting a contact.
  */
 public final class EditContactActivity extends Activity
-        implements View.OnClickListener, Comparator<EntityDelta> {
+        implements View.OnClickListener, Comparator<EntityDelta>,
+        DialogManager.DialogShowingViewActivity {
 
     private static final String TAG = "EditContactActivity";
 
@@ -127,6 +128,13 @@
     private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
     private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
     private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
+    private static final int DIALOG_PICK_PHOTO = 5;
+    private static final int DIALOG_SPLIT = 6;
+    private static final int DIALOG_SELECT_ACCOUNT = 7;
+    private static final int DIALOG_VIEW_DIALOGS_ID1 = 8;
+    private static final int DIALOG_VIEW_DIALOGS_ID2 = 9;
+
+    private static final String BUNDLE_SELECT_ACCOUNT_LIST = "account_list";
 
     private static final int ICON_SIZE = 96;
 
@@ -144,14 +152,13 @@
     private static final int STATUS_SAVING = 2;
 
     private int mStatus;
+    private DialogManager mDialogManager;
 
     EntitySet mState;
 
     /** The linear layout holding the ContactEditorViews */
     LinearLayout mContent;
 
-    private ArrayList<Dialog> mManagedDialogs = Lists.newArrayList();
-
     private ViewIdGenerator mViewIdGenerator;
 
     @Override
@@ -163,6 +170,8 @@
 
         setContentView(R.layout.act_edit);
 
+        mDialogManager = new DialogManager(this, DIALOG_VIEW_DIALOGS_ID1, DIALOG_VIEW_DIALOGS_ID2);
+
         // Build editor and listen for photo requests
         mContent = (LinearLayout) findViewById(R.id.editors);
 
@@ -295,15 +304,6 @@
     }
 
     @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        for (Dialog dialog : mManagedDialogs) {
-            dismissDialog(dialog);
-        }
-    }
-
-    @Override
     protected Dialog onCreateDialog(int id, Bundle bundle) {
         switch (id) {
             case DIALOG_CONFIRM_DELETE:
@@ -341,25 +341,15 @@
                         .setPositiveButton(android.R.string.ok, new DeleteClickListener())
                         .setCancelable(false)
                         .create();
+            case DIALOG_PICK_PHOTO:
+                return createPickPhotoDialog();
+            case DIALOG_SPLIT:
+                return createSplitDialog();
+            case DIALOG_SELECT_ACCOUNT:
+                return createSelectAccountDialog(bundle);
+            default:
+                return mDialogManager.onCreateDialog(id, bundle);
         }
-        return null;
-    }
-
-    /**
-     * Start managing this {@link Dialog} along with the {@link Activity}.
-     */
-    private void startManagingDialog(Dialog dialog) {
-        synchronized (mManagedDialogs) {
-            mManagedDialogs.add(dialog);
-        }
-    }
-
-    /**
-     * Show this {@link Dialog} and manage with the {@link Activity}.
-     */
-    void showAndManageDialog(Dialog dialog) {
-        startManagingDialog(dialog);
-        dialog.show();
     }
 
     /**
@@ -1021,7 +1011,7 @@
 
         mRawContactIdRequestingPhoto = rawContactId;
 
-        showAndManageDialog(createPickPhotoDialog());
+        showDialog(DIALOG_PICK_PHOTO);
 
         return true;
     }
@@ -1036,8 +1026,7 @@
         final Context dialogContext = new ContextThemeWrapper(context,
                 android.R.style.Theme_Light);
 
-        String[] choices;
-        choices = new String[2];
+        String[] choices = new String[2];
         choices[0] = getString(R.string.take_photo);
         choices[1] = getString(R.string.pick_photo);
         final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
@@ -1167,7 +1156,7 @@
     private boolean doSplitContactAction() {
         if (!hasValidState()) return false;
 
-        showAndManageDialog(createSplitDialog());
+        showDialog(DIALOG_SPLIT);
         return true;
     }
 
@@ -1229,6 +1218,14 @@
             return;  // Don't show a dialog.
         }
 
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList(BUNDLE_SELECT_ACCOUNT_LIST, accounts);
+        showDialog(DIALOG_SELECT_ACCOUNT, bundle);
+    }
+
+    private Dialog createSelectAccountDialog(Bundle bundle) {
+        final ArrayList<Account> accounts = bundle.getParcelableArrayList(
+                BUNDLE_SELECT_ACCOUNT_LIST);
         // Wrap our context to inflate list items using correct theme
         final Context dialogContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
         final LayoutInflater dialogInflater =
@@ -1285,7 +1282,13 @@
         builder.setTitle(R.string.dialog_new_contact_account);
         builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
         builder.setOnCancelListener(cancelListener);
-        showAndManageDialog(builder.create());
+        final Dialog result = builder.create();
+        result.setOnDismissListener(new OnDismissListener() {
+            public void onDismiss(DialogInterface dialog) {
+                removeDialog(DIALOG_SELECT_ACCOUNT);
+            }
+        });
+        return result;
     }
 
     /**
@@ -1409,4 +1412,8 @@
             ContactsSearchManager.startSearch(this, initialQuery);
         }
     }
+
+    public DialogManager getDialogManager() {
+        return mDialogManager;
+    }
 }
diff --git a/src/com/android/contacts/ui/widget/GenericEditorView.java b/src/com/android/contacts/ui/widget/GenericEditorView.java
index 24262bb..cfee1c3 100644
--- a/src/com/android/contacts/ui/widget/GenericEditorView.java
+++ b/src/com/android/contacts/ui/widget/GenericEditorView.java
@@ -25,13 +25,16 @@
 import com.android.contacts.model.ContactsSource.EditField;
 import com.android.contacts.model.ContactsSource.EditType;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.ui.DialogManager;
 import com.android.contacts.ui.ViewIdGenerator;
+import com.android.contacts.ui.DialogManager.DialogShowingView;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Entity;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.PhoneNumberFormattingTextWatcher;
@@ -58,10 +61,15 @@
  * the entry. Uses {@link ValuesDelta} to read any existing
  * {@link Entity} values, and to correctly write any changes values.
  */
-public class GenericEditorView extends RelativeLayout implements Editor, View.OnClickListener {
+public class GenericEditorView extends RelativeLayout implements Editor, View.OnClickListener,
+        DialogShowingView {
     protected static final int RES_FIELD = R.layout.item_editor_field;
     protected static final int RES_LABEL_ITEM = android.R.layout.simple_list_item_1;
 
+    private static final String DIALOG_ID_KEY = "dialog_id";
+    private static final int DIALOG_ID_LABEL = 1;
+    private static final int DIALOG_ID_CUSTOM = 2;
+
     protected LayoutInflater mInflater;
 
     protected static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
@@ -85,6 +93,7 @@
     private EditType mPendingType;
 
     private ViewIdGenerator mViewIdGenerator;
+    private DialogManager mDialogManager = null;
 
     public GenericEditorView(Context context) {
         super(context);
@@ -354,7 +363,7 @@
                     // Only when the custum value input in the next step is correct one.
                     // this method also set the type value to what the user requested here.
                     mPendingType = selected;
-                    createCustomDialog().show();
+                    showDialog(DIALOG_ID_CUSTOM);
                 } else {
                     // User picked type, and we're sure it's ok to actually write the entry.
                     mType = selected;
@@ -376,7 +385,7 @@
     public void onClick(View v) {
         switch (v.getId()) {
             case R.id.edit_label: {
-                createLabelDialog().show();
+                showDialog(DIALOG_ID_LABEL);
                 break;
             }
             case R.id.edit_delete: {
@@ -402,6 +411,26 @@
         }
     }
 
+    /* package */
+    void showDialog(int bundleDialogId) {
+        Bundle bundle = new Bundle();
+        bundle.putInt(DIALOG_ID_KEY, bundleDialogId);
+        getDialogManager().showDialogInView(this, bundle);
+    }
+
+    private DialogManager getDialogManager() {
+        if (mDialogManager == null) {
+            Context context = getContext();
+            if (!(context instanceof DialogManager.DialogShowingViewActivity)) {
+                throw new IllegalStateException(
+                        "View must be hosted in an Activity that implements " +
+                        "DialogManager.DialogShowingViewActivity");
+            }
+            mDialogManager = ((DialogManager.DialogShowingViewActivity)context).getDialogManager();
+        }
+        return mDialogManager;
+    }
+
     private static class SavedState extends BaseSavedState {
         public boolean mHideOptional;
         public int[] mVisibilities;
@@ -469,4 +498,17 @@
             mFields.getChildAt(i).setVisibility(ss.mVisibilities[i]);
         }
     }
+
+    public Dialog createDialog(Bundle bundle) {
+        if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
+        int dialogId = bundle.getInt(DIALOG_ID_KEY);
+        switch (dialogId) {
+            case DIALOG_ID_CUSTOM:
+                return createCustomDialog();
+            case DIALOG_ID_LABEL:
+                return createLabelDialog();
+            default:
+                throw new IllegalArgumentException("Invalid dialogId: " + dialogId);
+        }
+    }
 }