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