blob: a643c8a578e9d69bd987118448c792b98d05f51f [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;
67
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
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
259 protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
260 }
261
262 @Override
263 protected void onClick() {
Amith Yamasaniade026f2012-05-16 17:02:32 -0700264 if (mDialog != null && mDialog.isShowing()) return;
265
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 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 */
276 protected void showDialog(Bundle state) {
277 Context context = getContext();
278
Christian Mehlmauer746a95a2010-05-17 21:16:20 +0200279 mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280
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 }
Amith Yamasani1d458572009-09-15 10:25:51 -0700304 if (needInputMethod()) {
305 requestInputMethod(dialog);
306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 dialog.setOnDismissListener(this);
308 dialog.show();
309 }
Amith Yamasani1d458572009-09-15 10:25:51 -0700310
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 */
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 */
324 private void requestInputMethod(Dialog dialog) {
325 Window window = dialog.getWindow();
Adam Powellc82c7a52011-08-28 14:36:05 -0700326 window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
Amith Yamasani1d458572009-09-15 10:25:51 -0700327 }
328
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 /**
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 */
337 protected View onCreateDialogView() {
338 if (mDialogLayoutResId == 0) {
339 return null;
340 }
341
Adam Powell7e06ea82010-12-05 18:22:52 -0800342 LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 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 */
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
374 public void onClick(DialogInterface dialog, int which) {
375 mWhichButtonClicked = which;
376 }
377
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 */
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 */
401 public Dialog getDialog() {
402 return mDialog;
403 }
404
405 /**
406 * {@inheritDoc}
407 */
408 public void onActivityDestroy() {
409
410 if (mDialog == null || !mDialog.isShowing()) {
411 return;
412 }
413
414 mDialog.dismiss();
415 }
416
417 @Override
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
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
449 public SavedState(Parcel source) {
450 super(source);
451 isDialogShowing = source.readInt() == 1;
452 dialogBundle = source.readBundle();
453 }
454
455 @Override
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
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}