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