• 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.app.AlertDialog;
21 import android.app.Dialog;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.SharedPreferences;
25 import android.content.res.TypedArray;
26 import android.graphics.drawable.Drawable;
27 import android.os.Bundle;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.text.TextUtils;
31 import android.util.AttributeSet;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.Window;
35 import android.view.WindowManager;
36 import android.widget.TextView;
37 
38 /**
39  * A base class for {@link Preference} objects that are
40  * dialog-based. These preferences will, when clicked, open a dialog showing the
41  * actual preference controls.
42  *
43  * @attr ref android.R.styleable#DialogPreference_dialogTitle
44  * @attr ref android.R.styleable#DialogPreference_dialogMessage
45  * @attr ref android.R.styleable#DialogPreference_dialogIcon
46  * @attr ref android.R.styleable#DialogPreference_dialogLayout
47  * @attr ref android.R.styleable#DialogPreference_positiveButtonText
48  * @attr ref android.R.styleable#DialogPreference_negativeButtonText
49  */
50 public abstract class DialogPreference extends Preference implements
51         DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
52         PreferenceManager.OnActivityDestroyListener {
53     private AlertDialog.Builder mBuilder;
54 
55     private CharSequence mDialogTitle;
56     private CharSequence mDialogMessage;
57     private Drawable mDialogIcon;
58     private CharSequence mPositiveButtonText;
59     private CharSequence mNegativeButtonText;
60     private int mDialogLayoutResId;
61 
62     /** The dialog, if it is showing. */
63     private Dialog mDialog;
64 
65     /** Which button was clicked. */
66     private int mWhichButtonClicked;
67 
DialogPreference(Context context, AttributeSet attrs, int defStyle)68     public DialogPreference(Context context, AttributeSet attrs, int defStyle) {
69         super(context, attrs, defStyle);
70 
71         TypedArray a = context.obtainStyledAttributes(attrs,
72                 com.android.internal.R.styleable.DialogPreference, defStyle, 0);
73         mDialogTitle = a.getString(com.android.internal.R.styleable.DialogPreference_dialogTitle);
74         if (mDialogTitle == null) {
75             // Fallback on the regular title of the preference
76             // (the one that is seen in the list)
77             mDialogTitle = getTitle();
78         }
79         mDialogMessage = a.getString(com.android.internal.R.styleable.DialogPreference_dialogMessage);
80         mDialogIcon = a.getDrawable(com.android.internal.R.styleable.DialogPreference_dialogIcon);
81         mPositiveButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_positiveButtonText);
82         mNegativeButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_negativeButtonText);
83         mDialogLayoutResId = a.getResourceId(com.android.internal.R.styleable.DialogPreference_dialogLayout,
84                 mDialogLayoutResId);
85         a.recycle();
86 
87     }
88 
DialogPreference(Context context, AttributeSet attrs)89     public DialogPreference(Context context, AttributeSet attrs) {
90         this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
91     }
92 
93     /**
94      * Sets the title of the dialog. This will be shown on subsequent dialogs.
95      *
96      * @param dialogTitle The title.
97      */
setDialogTitle(CharSequence dialogTitle)98     public void setDialogTitle(CharSequence dialogTitle) {
99         mDialogTitle = dialogTitle;
100     }
101 
102     /**
103      * @see #setDialogTitle(CharSequence)
104      * @param dialogTitleResId The dialog title as a resource.
105      */
setDialogTitle(int dialogTitleResId)106     public void setDialogTitle(int dialogTitleResId) {
107         setDialogTitle(getContext().getString(dialogTitleResId));
108     }
109 
110     /**
111      * Returns the title to be shown on subsequent dialogs.
112      * @return The title.
113      */
getDialogTitle()114     public CharSequence getDialogTitle() {
115         return mDialogTitle;
116     }
117 
118     /**
119      * Sets the message of the dialog. This will be shown on subsequent dialogs.
120      * <p>
121      * This message forms the content View of the dialog and conflicts with
122      * list-based dialogs, for example. If setting a custom View on a dialog via
123      * {@link #setDialogLayoutResource(int)}, include a text View with ID
124      * {@link android.R.id#message} and it will be populated with this message.
125      *
126      * @param dialogMessage The message.
127      */
setDialogMessage(CharSequence dialogMessage)128     public void setDialogMessage(CharSequence dialogMessage) {
129         mDialogMessage = dialogMessage;
130     }
131 
132     /**
133      * @see #setDialogMessage(CharSequence)
134      * @param dialogMessageResId The dialog message as a resource.
135      */
setDialogMessage(int dialogMessageResId)136     public void setDialogMessage(int dialogMessageResId) {
137         setDialogMessage(getContext().getString(dialogMessageResId));
138     }
139 
140     /**
141      * Returns the message to be shown on subsequent dialogs.
142      * @return The message.
143      */
getDialogMessage()144     public CharSequence getDialogMessage() {
145         return mDialogMessage;
146     }
147 
148     /**
149      * Sets the icon of the dialog. This will be shown on subsequent dialogs.
150      *
151      * @param dialogIcon The icon, as a {@link Drawable}.
152      */
setDialogIcon(Drawable dialogIcon)153     public void setDialogIcon(Drawable dialogIcon) {
154         mDialogIcon = dialogIcon;
155     }
156 
157     /**
158      * Sets the icon (resource ID) of the dialog. This will be shown on
159      * subsequent dialogs.
160      *
161      * @param dialogIconRes The icon, as a resource ID.
162      */
setDialogIcon(int dialogIconRes)163     public void setDialogIcon(int dialogIconRes) {
164         mDialogIcon = getContext().getResources().getDrawable(dialogIconRes);
165     }
166 
167     /**
168      * Returns the icon to be shown on subsequent dialogs.
169      * @return The icon, as a {@link Drawable}.
170      */
getDialogIcon()171     public Drawable getDialogIcon() {
172         return mDialogIcon;
173     }
174 
175     /**
176      * Sets the text of the positive button of the dialog. This will be shown on
177      * subsequent dialogs.
178      *
179      * @param positiveButtonText The text of the positive button.
180      */
setPositiveButtonText(CharSequence positiveButtonText)181     public void setPositiveButtonText(CharSequence positiveButtonText) {
182         mPositiveButtonText = positiveButtonText;
183     }
184 
185     /**
186      * @see #setPositiveButtonText(CharSequence)
187      * @param positiveButtonTextResId The positive button text as a resource.
188      */
setPositiveButtonText(int positiveButtonTextResId)189     public void setPositiveButtonText(int positiveButtonTextResId) {
190         setPositiveButtonText(getContext().getString(positiveButtonTextResId));
191     }
192 
193     /**
194      * Returns the text of the positive button to be shown on subsequent
195      * dialogs.
196      *
197      * @return The text of the positive button.
198      */
getPositiveButtonText()199     public CharSequence getPositiveButtonText() {
200         return mPositiveButtonText;
201     }
202 
203     /**
204      * Sets the text of the negative button of the dialog. This will be shown on
205      * subsequent dialogs.
206      *
207      * @param negativeButtonText The text of the negative button.
208      */
setNegativeButtonText(CharSequence negativeButtonText)209     public void setNegativeButtonText(CharSequence negativeButtonText) {
210         mNegativeButtonText = negativeButtonText;
211     }
212 
213     /**
214      * @see #setNegativeButtonText(CharSequence)
215      * @param negativeButtonTextResId The negative button text as a resource.
216      */
setNegativeButtonText(int negativeButtonTextResId)217     public void setNegativeButtonText(int negativeButtonTextResId) {
218         setNegativeButtonText(getContext().getString(negativeButtonTextResId));
219     }
220 
221     /**
222      * Returns the text of the negative button to be shown on subsequent
223      * dialogs.
224      *
225      * @return The text of the negative button.
226      */
getNegativeButtonText()227     public CharSequence getNegativeButtonText() {
228         return mNegativeButtonText;
229     }
230 
231     /**
232      * Sets the layout resource that is inflated as the {@link View} to be shown
233      * as the content View of subsequent dialogs.
234      *
235      * @param dialogLayoutResId The layout resource ID to be inflated.
236      * @see #setDialogMessage(CharSequence)
237      */
setDialogLayoutResource(int dialogLayoutResId)238     public void setDialogLayoutResource(int dialogLayoutResId) {
239         mDialogLayoutResId = dialogLayoutResId;
240     }
241 
242     /**
243      * Returns the layout resource that is used as the content View for
244      * subsequent dialogs.
245      *
246      * @return The layout resource.
247      */
getDialogLayoutResource()248     public int getDialogLayoutResource() {
249         return mDialogLayoutResId;
250     }
251 
252     /**
253      * Prepares the dialog builder to be shown when the preference is clicked.
254      * Use this to set custom properties on the dialog.
255      * <p>
256      * Do not {@link AlertDialog.Builder#create()} or
257      * {@link AlertDialog.Builder#show()}.
258      */
onPrepareDialogBuilder(AlertDialog.Builder builder)259     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
260     }
261 
262     @Override
onClick()263     protected void onClick() {
264         if (mDialog != null && mDialog.isShowing()) return;
265 
266         showDialog(null);
267     }
268 
269     /**
270      * Shows the dialog associated with this Preference. This is normally initiated
271      * automatically on clicking on the preference. Call this method if you need to
272      * show the dialog on some other event.
273      *
274      * @param state Optional instance state to restore on the dialog
275      */
showDialog(Bundle state)276     protected void showDialog(Bundle state) {
277         Context context = getContext();
278 
279         mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
280 
281         mBuilder = new AlertDialog.Builder(context)
282             .setTitle(mDialogTitle)
283             .setIcon(mDialogIcon)
284             .setPositiveButton(mPositiveButtonText, this)
285             .setNegativeButton(mNegativeButtonText, this);
286 
287         View contentView = onCreateDialogView();
288         if (contentView != null) {
289             onBindDialogView(contentView);
290             mBuilder.setView(contentView);
291         } else {
292             mBuilder.setMessage(mDialogMessage);
293         }
294 
295         onPrepareDialogBuilder(mBuilder);
296 
297         getPreferenceManager().registerOnActivityDestroyListener(this);
298 
299         // Create the dialog
300         final Dialog dialog = mDialog = mBuilder.create();
301         if (state != null) {
302             dialog.onRestoreInstanceState(state);
303         }
304         if (needInputMethod()) {
305             requestInputMethod(dialog);
306         }
307         dialog.setOnDismissListener(this);
308         dialog.show();
309     }
310 
311     /**
312      * Returns whether the preference needs to display a soft input method when the dialog
313      * is displayed. Default is false. Subclasses should override this method if they need
314      * the soft input method brought up automatically.
315      * @hide
316      */
needInputMethod()317     protected boolean needInputMethod() {
318         return false;
319     }
320 
321     /**
322      * Sets the required flags on the dialog window to enable input method window to show up.
323      */
requestInputMethod(Dialog dialog)324     private void requestInputMethod(Dialog dialog) {
325         Window window = dialog.getWindow();
326         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
327     }
328 
329     /**
330      * Creates the content view for the dialog (if a custom content view is
331      * required). By default, it inflates the dialog layout resource if it is
332      * set.
333      *
334      * @return The content View for the dialog.
335      * @see #setLayoutResource(int)
336      */
onCreateDialogView()337     protected View onCreateDialogView() {
338         if (mDialogLayoutResId == 0) {
339             return null;
340         }
341 
342         LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
343         return inflater.inflate(mDialogLayoutResId, null);
344     }
345 
346     /**
347      * Binds views in the content View of the dialog to data.
348      * <p>
349      * Make sure to call through to the superclass implementation.
350      *
351      * @param view The content View of the dialog, if it is custom.
352      */
onBindDialogView(View view)353     protected void onBindDialogView(View view) {
354         View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
355 
356         if (dialogMessageView != null) {
357             final CharSequence message = getDialogMessage();
358             int newVisibility = View.GONE;
359 
360             if (!TextUtils.isEmpty(message)) {
361                 if (dialogMessageView instanceof TextView) {
362                     ((TextView) dialogMessageView).setText(message);
363                 }
364 
365                 newVisibility = View.VISIBLE;
366             }
367 
368             if (dialogMessageView.getVisibility() != newVisibility) {
369                 dialogMessageView.setVisibility(newVisibility);
370             }
371         }
372     }
373 
onClick(DialogInterface dialog, int which)374     public void onClick(DialogInterface dialog, int which) {
375         mWhichButtonClicked = which;
376     }
377 
onDismiss(DialogInterface dialog)378     public void onDismiss(DialogInterface dialog) {
379 
380         getPreferenceManager().unregisterOnActivityDestroyListener(this);
381 
382         mDialog = null;
383         onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
384     }
385 
386     /**
387      * Called when the dialog is dismissed and should be used to save data to
388      * the {@link SharedPreferences}.
389      *
390      * @param positiveResult Whether the positive button was clicked (true), or
391      *            the negative button was clicked or the dialog was canceled (false).
392      */
onDialogClosed(boolean positiveResult)393     protected void onDialogClosed(boolean positiveResult) {
394     }
395 
396     /**
397      * Gets the dialog that is shown by this preference.
398      *
399      * @return The dialog, or null if a dialog is not being shown.
400      */
getDialog()401     public Dialog getDialog() {
402         return mDialog;
403     }
404 
405     /**
406      * {@inheritDoc}
407      */
onActivityDestroy()408     public void onActivityDestroy() {
409 
410         if (mDialog == null || !mDialog.isShowing()) {
411             return;
412         }
413 
414         mDialog.dismiss();
415     }
416 
417     @Override
onSaveInstanceState()418     protected Parcelable onSaveInstanceState() {
419         final Parcelable superState = super.onSaveInstanceState();
420         if (mDialog == null || !mDialog.isShowing()) {
421             return superState;
422         }
423 
424         final SavedState myState = new SavedState(superState);
425         myState.isDialogShowing = true;
426         myState.dialogBundle = mDialog.onSaveInstanceState();
427         return myState;
428     }
429 
430     @Override
onRestoreInstanceState(Parcelable state)431     protected void onRestoreInstanceState(Parcelable state) {
432         if (state == null || !state.getClass().equals(SavedState.class)) {
433             // Didn't save state for us in onSaveInstanceState
434             super.onRestoreInstanceState(state);
435             return;
436         }
437 
438         SavedState myState = (SavedState) state;
439         super.onRestoreInstanceState(myState.getSuperState());
440         if (myState.isDialogShowing) {
441             showDialog(myState.dialogBundle);
442         }
443     }
444 
445     private static class SavedState extends BaseSavedState {
446         boolean isDialogShowing;
447         Bundle dialogBundle;
448 
SavedState(Parcel source)449         public SavedState(Parcel source) {
450             super(source);
451             isDialogShowing = source.readInt() == 1;
452             dialogBundle = source.readBundle();
453         }
454 
455         @Override
writeToParcel(Parcel dest, int flags)456         public void writeToParcel(Parcel dest, int flags) {
457             super.writeToParcel(dest, flags);
458             dest.writeInt(isDialogShowing ? 1 : 0);
459             dest.writeBundle(dialogBundle);
460         }
461 
SavedState(Parcelable superState)462         public SavedState(Parcelable superState) {
463             super(superState);
464         }
465 
466         public static final Parcelable.Creator<SavedState> CREATOR =
467                 new Parcelable.Creator<SavedState>() {
468             public SavedState createFromParcel(Parcel in) {
469                 return new SavedState(in);
470             }
471 
472             public SavedState[] newArray(int size) {
473                 return new SavedState[size];
474             }
475         };
476     }
477 
478 }
479