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