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