Merge "Dismiss Dialog in post instead of immediately"
am: 9b2c026f10

Change-Id: Iceb48f0a6f0172a4de2d3da668a38c7977cf20dd
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index 534ef8d..4b5a7b4 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -43,7 +43,7 @@
  * A base class for {@link Preference} objects that are
  * dialog-based. These preferences will, when clicked, open a dialog showing the
  * actual preference controls.
- * 
+ *
  * @attr ref android.R.styleable#DialogPreference_dialogTitle
  * @attr ref android.R.styleable#DialogPreference_dialogMessage
  * @attr ref android.R.styleable#DialogPreference_dialogIcon
@@ -56,7 +56,7 @@
         PreferenceManager.OnActivityDestroyListener {
     @UnsupportedAppUsage
     private AlertDialog.Builder mBuilder;
-    
+
     @UnsupportedAppUsage
     private CharSequence mDialogTitle;
     @UnsupportedAppUsage
@@ -77,6 +77,14 @@
     @UnsupportedAppUsage
     private int mWhichButtonClicked;
 
+    /** Dismiss the dialog on the UI thread, but not inline with handlers */
+    private final Runnable mDismissRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mDialog.dismiss();
+        }
+    };
+
     public DialogPreference(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -112,7 +120,7 @@
 
     /**
      * Sets the title of the dialog. This will be shown on subsequent dialogs.
-     * 
+     *
      * @param dialogTitle The title.
      */
     public void setDialogTitle(CharSequence dialogTitle) {
@@ -126,7 +134,7 @@
     public void setDialogTitle(int dialogTitleResId) {
         setDialogTitle(getContext().getString(dialogTitleResId));
     }
-    
+
     /**
      * Returns the title to be shown on subsequent dialogs.
      * @return The title.
@@ -134,7 +142,7 @@
     public CharSequence getDialogTitle() {
         return mDialogTitle;
     }
-    
+
     /**
      * Sets the message of the dialog. This will be shown on subsequent dialogs.
      * <p>
@@ -142,7 +150,7 @@
      * list-based dialogs, for example. If setting a custom View on a dialog via
      * {@link #setDialogLayoutResource(int)}, include a text View with ID
      * {@link android.R.id#message} and it will be populated with this message.
-     * 
+     *
      * @param dialogMessage The message.
      */
     public void setDialogMessage(CharSequence dialogMessage) {
@@ -156,7 +164,7 @@
     public void setDialogMessage(int dialogMessageResId) {
         setDialogMessage(getContext().getString(dialogMessageResId));
     }
-    
+
     /**
      * Returns the message to be shown on subsequent dialogs.
      * @return The message.
@@ -164,26 +172,26 @@
     public CharSequence getDialogMessage() {
         return mDialogMessage;
     }
-    
+
     /**
      * Sets the icon of the dialog. This will be shown on subsequent dialogs.
-     * 
+     *
      * @param dialogIcon The icon, as a {@link Drawable}.
      */
     public void setDialogIcon(Drawable dialogIcon) {
         mDialogIcon = dialogIcon;
     }
-    
+
     /**
      * Sets the icon (resource ID) of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param dialogIconRes The icon, as a resource ID.
      */
     public void setDialogIcon(@DrawableRes int dialogIconRes) {
         mDialogIcon = getContext().getDrawable(dialogIconRes);
     }
-    
+
     /**
      * Returns the icon to be shown on subsequent dialogs.
      * @return The icon, as a {@link Drawable}.
@@ -191,11 +199,11 @@
     public Drawable getDialogIcon() {
         return mDialogIcon;
     }
-    
+
     /**
      * Sets the text of the positive button of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param positiveButtonText The text of the positive button.
      */
     public void setPositiveButtonText(CharSequence positiveButtonText) {
@@ -209,27 +217,27 @@
     public void setPositiveButtonText(@StringRes int positiveButtonTextResId) {
         setPositiveButtonText(getContext().getString(positiveButtonTextResId));
     }
-    
+
     /**
      * Returns the text of the positive button to be shown on subsequent
      * dialogs.
-     * 
+     *
      * @return The text of the positive button.
      */
     public CharSequence getPositiveButtonText() {
         return mPositiveButtonText;
     }
-    
+
     /**
      * Sets the text of the negative button of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param negativeButtonText The text of the negative button.
      */
     public void setNegativeButtonText(CharSequence negativeButtonText) {
         mNegativeButtonText = negativeButtonText;
     }
-    
+
     /**
      * @see #setNegativeButtonText(CharSequence)
      * @param negativeButtonTextResId The negative button text as a resource.
@@ -237,38 +245,38 @@
     public void setNegativeButtonText(@StringRes int negativeButtonTextResId) {
         setNegativeButtonText(getContext().getString(negativeButtonTextResId));
     }
-    
+
     /**
      * Returns the text of the negative button to be shown on subsequent
      * dialogs.
-     * 
+     *
      * @return The text of the negative button.
      */
     public CharSequence getNegativeButtonText() {
         return mNegativeButtonText;
     }
-    
+
     /**
      * Sets the layout resource that is inflated as the {@link View} to be shown
      * as the content View of subsequent dialogs.
-     * 
+     *
      * @param dialogLayoutResId The layout resource ID to be inflated.
      * @see #setDialogMessage(CharSequence)
      */
     public void setDialogLayoutResource(int dialogLayoutResId) {
         mDialogLayoutResId = dialogLayoutResId;
     }
-    
+
     /**
      * Returns the layout resource that is used as the content View for
      * subsequent dialogs.
-     * 
+     *
      * @return The layout resource.
      */
     public int getDialogLayoutResource() {
         return mDialogLayoutResId;
     }
-    
+
     /**
      * Prepares the dialog builder to be shown when the preference is clicked.
      * Use this to set custom properties on the dialog.
@@ -278,7 +286,7 @@
      */
     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
     }
-    
+
     @Override
     protected void onClick() {
         if (mDialog != null && mDialog.isShowing()) return;
@@ -290,14 +298,14 @@
      * Shows the dialog associated with this Preference. This is normally initiated
      * automatically on clicking on the preference. Call this method if you need to
      * show the dialog on some other event.
-     * 
+     *
      * @param state Optional instance state to restore on the dialog
      */
     protected void showDialog(Bundle state) {
         Context context = getContext();
 
         mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
-        
+
         mBuilder = new AlertDialog.Builder(context)
             .setTitle(mDialogTitle)
             .setIcon(mDialogIcon)
@@ -311,11 +319,11 @@
         } else {
             mBuilder.setMessage(mDialogMessage);
         }
-        
+
         onPrepareDialogBuilder(mBuilder);
-        
+
         getPreferenceManager().registerOnActivityDestroyListener(this);
-        
+
         // Create the dialog
         final Dialog dialog = mDialog = mBuilder.create();
         if (state != null) {
@@ -324,10 +332,29 @@
         if (needInputMethod()) {
             requestInputMethod(dialog);
         }
+        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
+            @Override
+            public void onShow(DialogInterface dialog) {
+                removeDismissCallbacks();
+            }
+        });
         dialog.setOnDismissListener(this);
         dialog.show();
     }
 
+    void postDismiss() {
+        removeDismissCallbacks();
+        View decorView = mDialog.getWindow().getDecorView();
+        decorView.post(mDismissRunnable);
+    }
+
+    private void removeDismissCallbacks() {
+        if (mDialog != null && mDialog.getWindow() != null
+                && mDialog.getWindow().getDecorView() != null) {
+            mDialog.getWindow().getDecorView().removeCallbacks(mDismissRunnable);
+        }
+    }
+
     /**
      * Returns whether the preference needs to display a soft input method when the dialog
      * is displayed. Default is false. Subclasses should override this method if they need
@@ -350,7 +377,7 @@
      * Creates the content view for the dialog (if a custom content view is
      * required). By default, it inflates the dialog layout resource if it is
      * set.
-     * 
+     *
      * @return The content View for the dialog.
      * @see #setLayoutResource(int)
      */
@@ -358,48 +385,49 @@
         if (mDialogLayoutResId == 0) {
             return null;
         }
-        
+
         LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
         return inflater.inflate(mDialogLayoutResId, null);
     }
-    
+
     /**
      * Binds views in the content View of the dialog to data.
      * <p>
      * Make sure to call through to the superclass implementation.
-     * 
+     *
      * @param view The content View of the dialog, if it is custom.
      */
     @CallSuper
     protected void onBindDialogView(View view) {
         View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
-        
+
         if (dialogMessageView != null) {
             final CharSequence message = getDialogMessage();
             int newVisibility = View.GONE;
-            
+
             if (!TextUtils.isEmpty(message)) {
                 if (dialogMessageView instanceof TextView) {
                     ((TextView) dialogMessageView).setText(message);
                 }
-                
+
                 newVisibility = View.VISIBLE;
             }
-            
+
             if (dialogMessageView.getVisibility() != newVisibility) {
                 dialogMessageView.setVisibility(newVisibility);
             }
         }
     }
-    
+
     public void onClick(DialogInterface dialog, int which) {
         mWhichButtonClicked = which;
     }
-    
+
+    @Override
     public void onDismiss(DialogInterface dialog) {
-        
+        removeDismissCallbacks();
         getPreferenceManager().unregisterOnActivityDestroyListener(this);
-        
+
         mDialog = null;
         onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
     }
@@ -407,7 +435,7 @@
     /**
      * Called when the dialog is dismissed and should be used to save data to
      * the {@link SharedPreferences}.
-     * 
+     *
      * @param positiveResult Whether the positive button was clicked (true), or
      *            the negative button was clicked or the dialog was canceled (false).
      */
@@ -416,7 +444,7 @@
 
     /**
      * Gets the dialog that is shown by this preference.
-     * 
+     *
      * @return The dialog, or null if a dialog is not being shown.
      */
     public Dialog getDialog() {
@@ -427,11 +455,11 @@
      * {@inheritDoc}
      */
     public void onActivityDestroy() {
-        
+
         if (mDialog == null || !mDialog.isShowing()) {
             return;
         }
-        
+
         mDialog.dismiss();
     }
 
@@ -466,7 +494,7 @@
     private static class SavedState extends BaseSavedState {
         boolean isDialogShowing;
         Bundle dialogBundle;
-        
+
         public SavedState(Parcel source) {
             super(source);
             isDialogShowing = source.readInt() == 1;
@@ -495,5 +523,5 @@
             }
         };
     }
-    
+
 }
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index e7dec0e..c0c71af 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -33,7 +33,7 @@
  * <p>
  * This preference will store a string into the SharedPreferences. This string will be the value
  * from the {@link #setEntryValues(CharSequence[])} array.
- * 
+ *
  * @attr ref android.R.styleable#ListPreference_entries
  * @attr ref android.R.styleable#ListPreference_entryValues
  */
@@ -193,7 +193,7 @@
 
     /**
      * Sets the value to the given index from the entry values.
-     * 
+     *
      * @param index The index of the value to set.
      */
     public void setValueIndex(int index) {
@@ -201,30 +201,30 @@
             setValue(mEntryValues[index].toString());
         }
     }
-    
+
     /**
      * Returns the value of the key. This should be one of the entries in
      * {@link #getEntryValues()}.
-     * 
+     *
      * @return The value of the key.
      */
     public String getValue() {
-        return mValue; 
+        return mValue;
     }
-    
+
     /**
      * Returns the entry corresponding to the current value.
-     * 
+     *
      * @return The entry corresponding to the current value, or null.
      */
     public CharSequence getEntry() {
         int index = getValueIndex();
         return index >= 0 && mEntries != null ? mEntries[index] : null;
     }
-    
+
     /**
      * Returns the index of the given value (in the entry values array).
-     * 
+     *
      * @param value The value whose index should be returned.
      * @return The index of the value, or -1 if not found.
      */
@@ -238,22 +238,22 @@
         }
         return -1;
     }
-    
+
     private int getValueIndex() {
         return findIndexOfValue(mValue);
     }
-    
+
     @Override
     protected void onPrepareDialogBuilder(Builder builder) {
         super.onPrepareDialogBuilder(builder);
-        
+
         if (mEntries == null || mEntryValues == null) {
             throw new IllegalStateException(
                     "ListPreference requires an entries array and an entryValues array.");
         }
 
         mClickedDialogEntryIndex = getValueIndex();
-        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, 
+        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
                 new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         mClickedDialogEntryIndex = which;
@@ -263,10 +263,10 @@
                          * click, and dismisses the dialog.
                          */
                         ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
-                        dialog.dismiss();
+                        postDismiss();
                     }
         });
-        
+
         /*
          * The typical interaction for list-based dialogs is to have
          * click-on-an-item dismiss the dialog instead of the user having to
@@ -278,7 +278,7 @@
     @Override
     protected void onDialogClosed(boolean positiveResult) {
         super.onDialogClosed(positiveResult);
-        
+
         if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
             String value = mEntryValues[mClickedDialogEntryIndex].toString();
             if (callChangeListener(value)) {
@@ -296,7 +296,7 @@
     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
         setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
     }
-    
+
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
@@ -304,7 +304,7 @@
             // No need to save instance state since it's persistent
             return superState;
         }
-        
+
         final SavedState myState = new SavedState(superState);
         myState.value = getValue();
         return myState;
@@ -317,15 +317,15 @@
             super.onRestoreInstanceState(state);
             return;
         }
-         
+
         SavedState myState = (SavedState) state;
         super.onRestoreInstanceState(myState.getSuperState());
         setValue(myState.value);
     }
-    
+
     private static class SavedState extends BaseSavedState {
         String value;
-        
+
         public SavedState(Parcel source) {
             super(source);
             value = source.readString();
@@ -352,5 +352,5 @@
             }
         };
     }
-    
+
 }