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