• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.preference;
18 
19 
20 import android.annotation.CallSuper;
21 import android.annotation.DrawableRes;
22 import android.annotation.Nullable;
23 import android.annotation.StringRes;
24 import android.annotation.UnsupportedAppUsage;
25 import android.app.AlertDialog;
26 import android.app.Dialog;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.SharedPreferences;
30 import android.content.res.TypedArray;
31 import android.graphics.drawable.Drawable;
32 import android.os.Bundle;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 import android.text.TextUtils;
36 import android.util.AttributeSet;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.Window;
40 import android.view.WindowManager;
41 import android.widget.TextView;
42 
43 /**
44  * A base class for {@link Preference} objects that are
45  * dialog-based. These preferences will, when clicked, open a dialog showing the
46  * actual preference controls.
47  *
48  * @attr ref android.R.styleable#DialogPreference_dialogTitle
49  * @attr ref android.R.styleable#DialogPreference_dialogMessage
50  * @attr ref android.R.styleable#DialogPreference_dialogIcon
51  * @attr ref android.R.styleable#DialogPreference_dialogLayout
52  * @attr ref android.R.styleable#DialogPreference_positiveButtonText
53  * @attr ref android.R.styleable#DialogPreference_negativeButtonText
54  *
55  * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
56  *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
57  *      Preference Library</a> for consistent behavior across all devices. For more information on
58  *      using the AndroidX Preference Library see
59  *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
60  */
61 @Deprecated
62 public abstract class DialogPreference extends Preference implements
63         DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
64         PreferenceManager.OnActivityDestroyListener {
65     @UnsupportedAppUsage
66     private AlertDialog.Builder mBuilder;
67 
68     @UnsupportedAppUsage
69     private CharSequence mDialogTitle;
70     @UnsupportedAppUsage
71     private CharSequence mDialogMessage;
72     @UnsupportedAppUsage
73     private Drawable mDialogIcon;
74     @UnsupportedAppUsage
75     private CharSequence mPositiveButtonText;
76     @UnsupportedAppUsage
77     private CharSequence mNegativeButtonText;
78     private int mDialogLayoutResId;
79 
80     /** The dialog, if it is showing. */
81     @UnsupportedAppUsage
82     private Dialog mDialog;
83 
84     /** Which button was clicked. */
85     @UnsupportedAppUsage
86     private int mWhichButtonClicked;
87 
88     /** Dismiss the dialog on the UI thread, but not inline with handlers */
89     private final Runnable mDismissRunnable = new Runnable() {
90         @Override
91         public void run() {
92             mDialog.dismiss();
93         }
94     };
95 
DialogPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)96     public DialogPreference(
97             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
98         super(context, attrs, defStyleAttr, defStyleRes);
99 
100         final TypedArray a = context.obtainStyledAttributes(attrs,
101                 com.android.internal.R.styleable.DialogPreference, defStyleAttr, defStyleRes);
102         mDialogTitle = a.getString(com.android.internal.R.styleable.DialogPreference_dialogTitle);
103         if (mDialogTitle == null) {
104             // Fallback on the regular title of the preference
105             // (the one that is seen in the list)
106             mDialogTitle = getTitle();
107         }
108         mDialogMessage = a.getString(com.android.internal.R.styleable.DialogPreference_dialogMessage);
109         mDialogIcon = a.getDrawable(com.android.internal.R.styleable.DialogPreference_dialogIcon);
110         mPositiveButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_positiveButtonText);
111         mNegativeButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_negativeButtonText);
112         mDialogLayoutResId = a.getResourceId(com.android.internal.R.styleable.DialogPreference_dialogLayout,
113                 mDialogLayoutResId);
114         a.recycle();
115     }
116 
DialogPreference(Context context, AttributeSet attrs, int defStyleAttr)117     public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
118         this(context, attrs, defStyleAttr, 0);
119     }
120 
DialogPreference(Context context, AttributeSet attrs)121     public DialogPreference(Context context, AttributeSet attrs) {
122         this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
123     }
124 
DialogPreference(Context context)125     public DialogPreference(Context context) {
126         this(context, null);
127     }
128 
129     /**
130      * Sets the title of the dialog. This will be shown on subsequent dialogs.
131      *
132      * @param dialogTitle The title.
133      */
setDialogTitle(CharSequence dialogTitle)134     public void setDialogTitle(CharSequence dialogTitle) {
135         mDialogTitle = dialogTitle;
136     }
137 
138     /**
139      * @see #setDialogTitle(CharSequence)
140      * @param dialogTitleResId The dialog title as a resource.
141      */
setDialogTitle(int dialogTitleResId)142     public void setDialogTitle(int dialogTitleResId) {
143         setDialogTitle(getContext().getString(dialogTitleResId));
144     }
145 
146     /**
147      * Returns the title to be shown on subsequent dialogs.
148      * @return The title.
149      */
getDialogTitle()150     public CharSequence getDialogTitle() {
151         return mDialogTitle;
152     }
153 
154     /**
155      * Sets the message of the dialog. This will be shown on subsequent dialogs.
156      * <p>
157      * This message forms the content View of the dialog and conflicts with
158      * list-based dialogs, for example. If setting a custom View on a dialog via
159      * {@link #setDialogLayoutResource(int)}, include a text View with ID
160      * {@link android.R.id#message} and it will be populated with this message.
161      *
162      * @param dialogMessage The message.
163      */
setDialogMessage(CharSequence dialogMessage)164     public void setDialogMessage(CharSequence dialogMessage) {
165         mDialogMessage = dialogMessage;
166     }
167 
168     /**
169      * @see #setDialogMessage(CharSequence)
170      * @param dialogMessageResId The dialog message as a resource.
171      */
setDialogMessage(int dialogMessageResId)172     public void setDialogMessage(int dialogMessageResId) {
173         setDialogMessage(getContext().getString(dialogMessageResId));
174     }
175 
176     /**
177      * Returns the message to be shown on subsequent dialogs.
178      * @return The message.
179      */
getDialogMessage()180     public CharSequence getDialogMessage() {
181         return mDialogMessage;
182     }
183 
184     /**
185      * Sets the icon of the dialog. This will be shown on subsequent dialogs.
186      *
187      * @param dialogIcon The icon, as a {@link Drawable}.
188      */
setDialogIcon(Drawable dialogIcon)189     public void setDialogIcon(Drawable dialogIcon) {
190         mDialogIcon = dialogIcon;
191     }
192 
193     /**
194      * Sets the icon (resource ID) of the dialog. This will be shown on
195      * subsequent dialogs.
196      *
197      * @param dialogIconRes The icon, as a resource ID.
198      */
setDialogIcon(@rawableRes int dialogIconRes)199     public void setDialogIcon(@DrawableRes int dialogIconRes) {
200         mDialogIcon = getContext().getDrawable(dialogIconRes);
201     }
202 
203     /**
204      * Returns the icon to be shown on subsequent dialogs.
205      * @return The icon, as a {@link Drawable}.
206      */
getDialogIcon()207     public Drawable getDialogIcon() {
208         return mDialogIcon;
209     }
210 
211     /**
212      * Sets the text of the positive button of the dialog. This will be shown on
213      * subsequent dialogs.
214      *
215      * @param positiveButtonText The text of the positive button.
216      */
setPositiveButtonText(CharSequence positiveButtonText)217     public void setPositiveButtonText(CharSequence positiveButtonText) {
218         mPositiveButtonText = positiveButtonText;
219     }
220 
221     /**
222      * @see #setPositiveButtonText(CharSequence)
223      * @param positiveButtonTextResId The positive button text as a resource.
224      */
setPositiveButtonText(@tringRes int positiveButtonTextResId)225     public void setPositiveButtonText(@StringRes int positiveButtonTextResId) {
226         setPositiveButtonText(getContext().getString(positiveButtonTextResId));
227     }
228 
229     /**
230      * Returns the text of the positive button to be shown on subsequent
231      * dialogs.
232      *
233      * @return The text of the positive button.
234      */
getPositiveButtonText()235     public CharSequence getPositiveButtonText() {
236         return mPositiveButtonText;
237     }
238 
239     /**
240      * Sets the text of the negative button of the dialog. This will be shown on
241      * subsequent dialogs.
242      *
243      * @param negativeButtonText The text of the negative button.
244      */
setNegativeButtonText(CharSequence negativeButtonText)245     public void setNegativeButtonText(CharSequence negativeButtonText) {
246         mNegativeButtonText = negativeButtonText;
247     }
248 
249     /**
250      * @see #setNegativeButtonText(CharSequence)
251      * @param negativeButtonTextResId The negative button text as a resource.
252      */
setNegativeButtonText(@tringRes int negativeButtonTextResId)253     public void setNegativeButtonText(@StringRes int negativeButtonTextResId) {
254         setNegativeButtonText(getContext().getString(negativeButtonTextResId));
255     }
256 
257     /**
258      * Returns the text of the negative button to be shown on subsequent
259      * dialogs.
260      *
261      * @return The text of the negative button.
262      */
getNegativeButtonText()263     public CharSequence getNegativeButtonText() {
264         return mNegativeButtonText;
265     }
266 
267     /**
268      * Sets the layout resource that is inflated as the {@link View} to be shown
269      * as the content View of subsequent dialogs.
270      *
271      * @param dialogLayoutResId The layout resource ID to be inflated.
272      * @see #setDialogMessage(CharSequence)
273      */
setDialogLayoutResource(int dialogLayoutResId)274     public void setDialogLayoutResource(int dialogLayoutResId) {
275         mDialogLayoutResId = dialogLayoutResId;
276     }
277 
278     /**
279      * Returns the layout resource that is used as the content View for
280      * subsequent dialogs.
281      *
282      * @return The layout resource.
283      */
getDialogLayoutResource()284     public int getDialogLayoutResource() {
285         return mDialogLayoutResId;
286     }
287 
288     /**
289      * Prepares the dialog builder to be shown when the preference is clicked.
290      * Use this to set custom properties on the dialog.
291      * <p>
292      * Do not {@link AlertDialog.Builder#create()} or
293      * {@link AlertDialog.Builder#show()}.
294      */
onPrepareDialogBuilder(AlertDialog.Builder builder)295     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
296     }
297 
298     @Override
onClick()299     protected void onClick() {
300         if (mDialog != null && mDialog.isShowing()) return;
301 
302         showDialog(null);
303     }
304 
305     /**
306      * Shows the dialog associated with this Preference. This is normally initiated
307      * automatically on clicking on the preference. Call this method if you need to
308      * show the dialog on some other event.
309      *
310      * @param state Optional instance state to restore on the dialog
311      */
showDialog(Bundle state)312     protected void showDialog(Bundle state) {
313         Context context = getContext();
314 
315         mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
316 
317         mBuilder = new AlertDialog.Builder(context)
318             .setTitle(mDialogTitle)
319             .setIcon(mDialogIcon)
320             .setPositiveButton(mPositiveButtonText, this)
321             .setNegativeButton(mNegativeButtonText, this);
322 
323         View contentView = onCreateDialogView();
324         if (contentView != null) {
325             onBindDialogView(contentView);
326             mBuilder.setView(contentView);
327         } else {
328             mBuilder.setMessage(mDialogMessage);
329         }
330 
331         onPrepareDialogBuilder(mBuilder);
332 
333         getPreferenceManager().registerOnActivityDestroyListener(this);
334 
335         // Create the dialog
336         final Dialog dialog = mDialog = mBuilder.create();
337         if (state != null) {
338             dialog.onRestoreInstanceState(state);
339         }
340         if (needInputMethod()) {
341             requestInputMethod(dialog);
342         }
343         dialog.setOnShowListener(new DialogInterface.OnShowListener() {
344             @Override
345             public void onShow(DialogInterface dialog) {
346                 removeDismissCallbacks();
347             }
348         });
349         dialog.setOnDismissListener(this);
350         dialog.show();
351     }
352 
353     /**
354      * Get the DecorView.
355      * @return the DecorView for the current dialog window, if it exists.
356      * If the window does not exist, null is returned.
357      */
358     @Nullable
getDecorView()359     private View getDecorView() {
360         if (mDialog != null && mDialog.getWindow() != null) {
361             return mDialog.getWindow().getDecorView();
362         }
363         return null;
364     }
365 
postDismiss()366     void postDismiss() {
367         removeDismissCallbacks();
368         View decorView = getDecorView();
369         if (decorView != null) {
370             // If decorView is null, dialog was already dismissed
371             decorView.post(mDismissRunnable);
372         }
373     }
374 
removeDismissCallbacks()375     private void removeDismissCallbacks() {
376         View decorView = getDecorView();
377         if (decorView != null) {
378             decorView.removeCallbacks(mDismissRunnable);
379         }
380     }
381 
382     /**
383      * Returns whether the preference needs to display a soft input method when the dialog
384      * is displayed. Default is false. Subclasses should override this method if they need
385      * the soft input method brought up automatically.
386      * @hide
387      */
needInputMethod()388     protected boolean needInputMethod() {
389         return false;
390     }
391 
392     /**
393      * Sets the required flags on the dialog window to enable input method window to show up.
394      */
requestInputMethod(Dialog dialog)395     private void requestInputMethod(Dialog dialog) {
396         Window window = dialog.getWindow();
397         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
398     }
399 
400     /**
401      * Creates the content view for the dialog (if a custom content view is
402      * required). By default, it inflates the dialog layout resource if it is
403      * set.
404      *
405      * @return The content View for the dialog.
406      * @see #setLayoutResource(int)
407      */
onCreateDialogView()408     protected View onCreateDialogView() {
409         if (mDialogLayoutResId == 0) {
410             return null;
411         }
412 
413         LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
414         return inflater.inflate(mDialogLayoutResId, null);
415     }
416 
417     /**
418      * Binds views in the content View of the dialog to data.
419      * <p>
420      * Make sure to call through to the superclass implementation.
421      *
422      * @param view The content View of the dialog, if it is custom.
423      */
424     @CallSuper
onBindDialogView(View view)425     protected void onBindDialogView(View view) {
426         View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
427 
428         if (dialogMessageView != null) {
429             final CharSequence message = getDialogMessage();
430             int newVisibility = View.GONE;
431 
432             if (!TextUtils.isEmpty(message)) {
433                 if (dialogMessageView instanceof TextView) {
434                     ((TextView) dialogMessageView).setText(message);
435                 }
436 
437                 newVisibility = View.VISIBLE;
438             }
439 
440             if (dialogMessageView.getVisibility() != newVisibility) {
441                 dialogMessageView.setVisibility(newVisibility);
442             }
443         }
444     }
445 
onClick(DialogInterface dialog, int which)446     public void onClick(DialogInterface dialog, int which) {
447         mWhichButtonClicked = which;
448     }
449 
450     @Override
onDismiss(DialogInterface dialog)451     public void onDismiss(DialogInterface dialog) {
452         removeDismissCallbacks();
453         getPreferenceManager().unregisterOnActivityDestroyListener(this);
454 
455         mDialog = null;
456         onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
457     }
458 
459     /**
460      * Called when the dialog is dismissed and should be used to save data to
461      * the {@link SharedPreferences}.
462      *
463      * @param positiveResult Whether the positive button was clicked (true), or
464      *            the negative button was clicked or the dialog was canceled (false).
465      */
onDialogClosed(boolean positiveResult)466     protected void onDialogClosed(boolean positiveResult) {
467     }
468 
469     /**
470      * Gets the dialog that is shown by this preference.
471      *
472      * @return The dialog, or null if a dialog is not being shown.
473      */
getDialog()474     public Dialog getDialog() {
475         return mDialog;
476     }
477 
478     /**
479      * {@inheritDoc}
480      */
onActivityDestroy()481     public void onActivityDestroy() {
482 
483         if (mDialog == null || !mDialog.isShowing()) {
484             return;
485         }
486 
487         mDialog.dismiss();
488     }
489 
490     @Override
onSaveInstanceState()491     protected Parcelable onSaveInstanceState() {
492         final Parcelable superState = super.onSaveInstanceState();
493         if (mDialog == null || !mDialog.isShowing()) {
494             return superState;
495         }
496 
497         final SavedState myState = new SavedState(superState);
498         myState.isDialogShowing = true;
499         myState.dialogBundle = mDialog.onSaveInstanceState();
500         return myState;
501     }
502 
503     @Override
onRestoreInstanceState(Parcelable state)504     protected void onRestoreInstanceState(Parcelable state) {
505         if (state == null || !state.getClass().equals(SavedState.class)) {
506             // Didn't save state for us in onSaveInstanceState
507             super.onRestoreInstanceState(state);
508             return;
509         }
510 
511         SavedState myState = (SavedState) state;
512         super.onRestoreInstanceState(myState.getSuperState());
513         if (myState.isDialogShowing) {
514             showDialog(myState.dialogBundle);
515         }
516     }
517 
518     private static class SavedState extends BaseSavedState {
519         boolean isDialogShowing;
520         Bundle dialogBundle;
521 
SavedState(Parcel source)522         public SavedState(Parcel source) {
523             super(source);
524             isDialogShowing = source.readInt() == 1;
525             dialogBundle = source.readBundle();
526         }
527 
528         @Override
writeToParcel(Parcel dest, int flags)529         public void writeToParcel(Parcel dest, int flags) {
530             super.writeToParcel(dest, flags);
531             dest.writeInt(isDialogShowing ? 1 : 0);
532             dest.writeBundle(dialogBundle);
533         }
534 
SavedState(Parcelable superState)535         public SavedState(Parcelable superState) {
536             super(superState);
537         }
538 
539         public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR =
540                 new Parcelable.Creator<SavedState>() {
541             public SavedState createFromParcel(Parcel in) {
542                 return new SavedState(in);
543             }
544 
545             public SavedState[] newArray(int size) {
546                 return new SavedState[size];
547             }
548         };
549     }
550 
551 }
552