| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.preference; |
| |
| |
| import android.annotation.CallSuper; |
| import android.annotation.DrawableRes; |
| import android.annotation.StringRes; |
| import android.annotation.UnsupportedAppUsage; |
| import android.app.AlertDialog; |
| import android.app.Dialog; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.SharedPreferences; |
| import android.content.res.TypedArray; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.text.TextUtils; |
| import android.util.AttributeSet; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.Window; |
| import android.view.WindowManager; |
| import android.widget.TextView; |
| |
| /** |
| * 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 |
| * @attr ref android.R.styleable#DialogPreference_dialogLayout |
| * @attr ref android.R.styleable#DialogPreference_positiveButtonText |
| * @attr ref android.R.styleable#DialogPreference_negativeButtonText |
| */ |
| public abstract class DialogPreference extends Preference implements |
| DialogInterface.OnClickListener, DialogInterface.OnDismissListener, |
| PreferenceManager.OnActivityDestroyListener { |
| @UnsupportedAppUsage |
| private AlertDialog.Builder mBuilder; |
| |
| @UnsupportedAppUsage |
| private CharSequence mDialogTitle; |
| @UnsupportedAppUsage |
| private CharSequence mDialogMessage; |
| @UnsupportedAppUsage |
| private Drawable mDialogIcon; |
| @UnsupportedAppUsage |
| private CharSequence mPositiveButtonText; |
| @UnsupportedAppUsage |
| private CharSequence mNegativeButtonText; |
| private int mDialogLayoutResId; |
| |
| /** The dialog, if it is showing. */ |
| @UnsupportedAppUsage |
| private Dialog mDialog; |
| |
| /** Which button was clicked. */ |
| @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); |
| |
| final TypedArray a = context.obtainStyledAttributes(attrs, |
| com.android.internal.R.styleable.DialogPreference, defStyleAttr, defStyleRes); |
| mDialogTitle = a.getString(com.android.internal.R.styleable.DialogPreference_dialogTitle); |
| if (mDialogTitle == null) { |
| // Fallback on the regular title of the preference |
| // (the one that is seen in the list) |
| mDialogTitle = getTitle(); |
| } |
| mDialogMessage = a.getString(com.android.internal.R.styleable.DialogPreference_dialogMessage); |
| mDialogIcon = a.getDrawable(com.android.internal.R.styleable.DialogPreference_dialogIcon); |
| mPositiveButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_positiveButtonText); |
| mNegativeButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_negativeButtonText); |
| mDialogLayoutResId = a.getResourceId(com.android.internal.R.styleable.DialogPreference_dialogLayout, |
| mDialogLayoutResId); |
| a.recycle(); |
| } |
| |
| public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) { |
| this(context, attrs, defStyleAttr, 0); |
| } |
| |
| public DialogPreference(Context context, AttributeSet attrs) { |
| this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle); |
| } |
| |
| public DialogPreference(Context context) { |
| this(context, null); |
| } |
| |
| /** |
| * Sets the title of the dialog. This will be shown on subsequent dialogs. |
| * |
| * @param dialogTitle The title. |
| */ |
| public void setDialogTitle(CharSequence dialogTitle) { |
| mDialogTitle = dialogTitle; |
| } |
| |
| /** |
| * @see #setDialogTitle(CharSequence) |
| * @param dialogTitleResId The dialog title as a resource. |
| */ |
| public void setDialogTitle(int dialogTitleResId) { |
| setDialogTitle(getContext().getString(dialogTitleResId)); |
| } |
| |
| /** |
| * Returns the title to be shown on subsequent dialogs. |
| * @return The title. |
| */ |
| public CharSequence getDialogTitle() { |
| return mDialogTitle; |
| } |
| |
| /** |
| * Sets the message of the dialog. This will be shown on subsequent dialogs. |
| * <p> |
| * This message forms the content View of the dialog and conflicts with |
| * 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) { |
| mDialogMessage = dialogMessage; |
| } |
| |
| /** |
| * @see #setDialogMessage(CharSequence) |
| * @param dialogMessageResId The dialog message as a resource. |
| */ |
| public void setDialogMessage(int dialogMessageResId) { |
| setDialogMessage(getContext().getString(dialogMessageResId)); |
| } |
| |
| /** |
| * Returns the message to be shown on subsequent dialogs. |
| * @return The message. |
| */ |
| 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}. |
| */ |
| 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) { |
| mPositiveButtonText = positiveButtonText; |
| } |
| |
| /** |
| * @see #setPositiveButtonText(CharSequence) |
| * @param positiveButtonTextResId The positive button text as a resource. |
| */ |
| 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. |
| */ |
| 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. |
| * <p> |
| * Do not {@link AlertDialog.Builder#create()} or |
| * {@link AlertDialog.Builder#show()}. |
| */ |
| protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { |
| } |
| |
| @Override |
| protected void onClick() { |
| if (mDialog != null && mDialog.isShowing()) return; |
| |
| showDialog(null); |
| } |
| |
| /** |
| * 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) |
| .setPositiveButton(mPositiveButtonText, this) |
| .setNegativeButton(mNegativeButtonText, this); |
| |
| View contentView = onCreateDialogView(); |
| if (contentView != null) { |
| onBindDialogView(contentView); |
| mBuilder.setView(contentView); |
| } else { |
| mBuilder.setMessage(mDialogMessage); |
| } |
| |
| onPrepareDialogBuilder(mBuilder); |
| |
| getPreferenceManager().registerOnActivityDestroyListener(this); |
| |
| // Create the dialog |
| final Dialog dialog = mDialog = mBuilder.create(); |
| if (state != null) { |
| dialog.onRestoreInstanceState(state); |
| } |
| 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 |
| * the soft input method brought up automatically. |
| * @hide |
| */ |
| protected boolean needInputMethod() { |
| return false; |
| } |
| |
| /** |
| * Sets the required flags on the dialog window to enable input method window to show up. |
| */ |
| private void requestInputMethod(Dialog dialog) { |
| Window window = dialog.getWindow(); |
| window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); |
| } |
| |
| /** |
| * 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) |
| */ |
| protected View onCreateDialogView() { |
| 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); |
| } |
| |
| /** |
| * 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). |
| */ |
| protected void onDialogClosed(boolean positiveResult) { |
| } |
| |
| /** |
| * Gets the dialog that is shown by this preference. |
| * |
| * @return The dialog, or null if a dialog is not being shown. |
| */ |
| public Dialog getDialog() { |
| return mDialog; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void onActivityDestroy() { |
| |
| if (mDialog == null || !mDialog.isShowing()) { |
| return; |
| } |
| |
| mDialog.dismiss(); |
| } |
| |
| @Override |
| protected Parcelable onSaveInstanceState() { |
| final Parcelable superState = super.onSaveInstanceState(); |
| if (mDialog == null || !mDialog.isShowing()) { |
| return superState; |
| } |
| |
| final SavedState myState = new SavedState(superState); |
| myState.isDialogShowing = true; |
| myState.dialogBundle = mDialog.onSaveInstanceState(); |
| return myState; |
| } |
| |
| @Override |
| protected void onRestoreInstanceState(Parcelable state) { |
| if (state == null || !state.getClass().equals(SavedState.class)) { |
| // Didn't save state for us in onSaveInstanceState |
| super.onRestoreInstanceState(state); |
| return; |
| } |
| |
| SavedState myState = (SavedState) state; |
| super.onRestoreInstanceState(myState.getSuperState()); |
| if (myState.isDialogShowing) { |
| showDialog(myState.dialogBundle); |
| } |
| } |
| |
| private static class SavedState extends BaseSavedState { |
| boolean isDialogShowing; |
| Bundle dialogBundle; |
| |
| public SavedState(Parcel source) { |
| super(source); |
| isDialogShowing = source.readInt() == 1; |
| dialogBundle = source.readBundle(); |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| super.writeToParcel(dest, flags); |
| dest.writeInt(isDialogShowing ? 1 : 0); |
| dest.writeBundle(dialogBundle); |
| } |
| |
| public SavedState(Parcelable superState) { |
| super(superState); |
| } |
| |
| public static final Parcelable.Creator<SavedState> CREATOR = |
| new Parcelable.Creator<SavedState>() { |
| public SavedState createFromParcel(Parcel in) { |
| return new SavedState(in); |
| } |
| |
| public SavedState[] newArray(int size) { |
| return new SavedState[size]; |
| } |
| }; |
| } |
| |
| } |