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 19import java.util.ArrayList; 20import java.util.List; 21 22import android.content.Context; 23import android.content.Intent; 24import android.content.SharedPreferences; 25import android.content.res.TypedArray; 26import android.os.Bundle; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.text.TextUtils; 30import android.util.AttributeSet; 31import com.android.internal.util.CharSequences; 32import android.view.AbsSavedState; 33import android.view.LayoutInflater; 34import android.view.View; 35import android.view.ViewGroup; 36import android.widget.ListView; 37import android.widget.TextView; 38 39/** 40 * Represents the basic Preference UI building 41 * block displayed by a {@link PreferenceActivity} in the form of a 42 * {@link ListView}. This class provides the {@link View} to be displayed in 43 * the activity and associates with a {@link SharedPreferences} to 44 * store/retrieve the preference data. 45 * <p> 46 * When specifying a preference hierarchy in XML, each element can point to a 47 * subclass of {@link Preference}, similar to the view hierarchy and layouts. 48 * <p> 49 * This class contains a {@code key} that will be used as the key into the 50 * {@link SharedPreferences}. It is up to the subclass to decide how to store 51 * the value. 52 * 53 * @attr ref android.R.styleable#Preference_key 54 * @attr ref android.R.styleable#Preference_title 55 * @attr ref android.R.styleable#Preference_summary 56 * @attr ref android.R.styleable#Preference_order 57 * @attr ref android.R.styleable#Preference_layout 58 * @attr ref android.R.styleable#Preference_widgetLayout 59 * @attr ref android.R.styleable#Preference_enabled 60 * @attr ref android.R.styleable#Preference_selectable 61 * @attr ref android.R.styleable#Preference_dependency 62 * @attr ref android.R.styleable#Preference_persistent 63 * @attr ref android.R.styleable#Preference_defaultValue 64 * @attr ref android.R.styleable#Preference_shouldDisableView 65 */ 66public class Preference implements Comparable<Preference>, OnDependencyChangeListener { 67 /** 68 * Specify for {@link #setOrder(int)} if a specific order is not required. 69 */ 70 public static final int DEFAULT_ORDER = Integer.MAX_VALUE; 71 72 private Context mContext; 73 private PreferenceManager mPreferenceManager; 74 75 /** 76 * Set when added to hierarchy since we need a unique ID within that 77 * hierarchy. 78 */ 79 private long mId; 80 81 private OnPreferenceChangeListener mOnChangeListener; 82 private OnPreferenceClickListener mOnClickListener; 83 84 private int mOrder = DEFAULT_ORDER; 85 private CharSequence mTitle; 86 private CharSequence mSummary; 87 private String mKey; 88 private Intent mIntent; 89 private boolean mEnabled = true; 90 private boolean mSelectable = true; 91 private boolean mRequiresKey; 92 private boolean mPersistent = true; 93 private String mDependencyKey; 94 private Object mDefaultValue; 95 private boolean mDependencyMet = true; 96 97 /** 98 * @see #setShouldDisableView(boolean) 99 */ 100 private boolean mShouldDisableView = true; 101 102 private int mLayoutResId = com.android.internal.R.layout.preference; 103 private int mWidgetLayoutResId; 104 private boolean mHasSpecifiedLayout = false; 105 106 private OnPreferenceChangeInternalListener mListener; 107 108 private List<Preference> mDependents; 109 110 private boolean mBaseMethodCalled; 111 112 /** 113 * Interface definition for a callback to be invoked when the value of this 114 * {@link Preference} has been changed by the user and is 115 * about to be set and/or persisted. This gives the client a chance 116 * to prevent setting and/or persisting the value. 117 */ 118 public interface OnPreferenceChangeListener { 119 /** 120 * Called when a Preference has been changed by the user. This is 121 * called before the state of the Preference is about to be updated and 122 * before the state is persisted. 123 * 124 * @param preference The changed Preference. 125 * @param newValue The new value of the Preference. 126 * @return True to update the state of the Preference with the new value. 127 */ 128 boolean onPreferenceChange(Preference preference, Object newValue); 129 } 130 131 /** 132 * Interface definition for a callback to be invoked when a {@link Preference} is 133 * clicked. 134 */ 135 public interface OnPreferenceClickListener { 136 /** 137 * Called when a Preference has been clicked. 138 * 139 * @param preference The Preference that was clicked. 140 * @return True if the click was handled. 141 */ 142 boolean onPreferenceClick(Preference preference); 143 } 144 145 /** 146 * Interface definition for a callback to be invoked when this 147 * {@link Preference} is changed or, if this is a group, there is an 148 * addition/removal of {@link Preference}(s). This is used internally. 149 */ 150 interface OnPreferenceChangeInternalListener { 151 /** 152 * Called when this Preference has changed. 153 * 154 * @param preference This preference. 155 */ 156 void onPreferenceChange(Preference preference); 157 158 /** 159 * Called when this group has added/removed {@link Preference}(s). 160 * 161 * @param preference This Preference. 162 */ 163 void onPreferenceHierarchyChange(Preference preference); 164 } 165 166 /** 167 * Perform inflation from XML and apply a class-specific base style. This 168 * constructor of Preference allows subclasses to use their own base 169 * style when they are inflating. For example, a {@link CheckBoxPreference} 170 * constructor calls this version of the super class constructor and 171 * supplies {@code android.R.attr.checkBoxPreferenceStyle} for <var>defStyle</var>. 172 * This allows the theme's checkbox preference style to modify all of the base 173 * preference attributes as well as the {@link CheckBoxPreference} class's 174 * attributes. 175 * 176 * @param context The Context this is associated with, through which it can 177 * access the current theme, resources, {@link SharedPreferences}, 178 * etc. 179 * @param attrs The attributes of the XML tag that is inflating the preference. 180 * @param defStyle The default style to apply to this preference. If 0, no style 181 * will be applied (beyond what is included in the theme). This 182 * may either be an attribute resource, whose value will be 183 * retrieved from the current theme, or an explicit style 184 * resource. 185 * @see #Preference(Context, AttributeSet) 186 */ 187 public Preference(Context context, AttributeSet attrs, int defStyle) { 188 mContext = context; 189 190 TypedArray a = context.obtainStyledAttributes(attrs, 191 com.android.internal.R.styleable.Preference); 192 if (a.hasValue(com.android.internal.R.styleable.Preference_layout) || 193 a.hasValue(com.android.internal.R.styleable.Preference_widgetLayout)) { 194 // This preference has a custom layout defined (not one taken from 195 // the default style) 196 mHasSpecifiedLayout = true; 197 } 198 a.recycle(); 199 200 a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Preference, 201 defStyle, 0); 202 for (int i = a.getIndexCount(); i >= 0; i--) { 203 int attr = a.getIndex(i); 204 switch (attr) { 205 case com.android.internal.R.styleable.Preference_key: 206 mKey = a.getString(attr); 207 break; 208 209 case com.android.internal.R.styleable.Preference_title: 210 mTitle = a.getString(attr); 211 break; 212 213 case com.android.internal.R.styleable.Preference_summary: 214 mSummary = a.getString(attr); 215 break; 216 217 case com.android.internal.R.styleable.Preference_order: 218 mOrder = a.getInt(attr, mOrder); 219 break; 220 221 case com.android.internal.R.styleable.Preference_layout: 222 mLayoutResId = a.getResourceId(attr, mLayoutResId); 223 break; 224 225 case com.android.internal.R.styleable.Preference_widgetLayout: 226 mWidgetLayoutResId = a.getResourceId(attr, mWidgetLayoutResId); 227 break; 228 229 case com.android.internal.R.styleable.Preference_enabled: 230 mEnabled = a.getBoolean(attr, true); 231 break; 232 233 case com.android.internal.R.styleable.Preference_selectable: 234 mSelectable = a.getBoolean(attr, true); 235 break; 236 237 case com.android.internal.R.styleable.Preference_persistent: 238 mPersistent = a.getBoolean(attr, mPersistent); 239 break; 240 241 case com.android.internal.R.styleable.Preference_dependency: 242 mDependencyKey = a.getString(attr); 243 break; 244 245 case com.android.internal.R.styleable.Preference_defaultValue: 246 mDefaultValue = onGetDefaultValue(a, attr); 247 break; 248 249 case com.android.internal.R.styleable.Preference_shouldDisableView: 250 mShouldDisableView = a.getBoolean(attr, mShouldDisableView); 251 break; 252 } 253 } 254 a.recycle(); 255 } 256 257 /** 258 * Constructor that is called when inflating a Preference from XML. This is 259 * called when a Preference is being constructed from an XML file, supplying 260 * attributes that were specified in the XML file. This version uses a 261 * default style of 0, so the only attribute values applied are those in the 262 * Context's Theme and the given AttributeSet. 263 * 264 * @param context The Context this is associated with, through which it can 265 * access the current theme, resources, {@link SharedPreferences}, 266 * etc. 267 * @param attrs The attributes of the XML tag that is inflating the 268 * preference. 269 * @see #Preference(Context, AttributeSet, int) 270 */ 271 public Preference(Context context, AttributeSet attrs) { 272 this(context, attrs, 0); 273 } 274 275 /** 276 * Constructor to create a Preference. 277 * 278 * @param context The Context in which to store Preference values. 279 */ 280 public Preference(Context context) { 281 this(context, null); 282 } 283 284 /** 285 * Called when a Preference is being inflated and the default value 286 * attribute needs to be read. Since different Preference types have 287 * different value types, the subclass should get and return the default 288 * value which will be its value type. 289 * <p> 290 * For example, if the value type is String, the body of the method would 291 * proxy to {@link TypedArray#getString(int)}. 292 * 293 * @param a The set of attributes. 294 * @param index The index of the default value attribute. 295 * @return The default value of this preference type. 296 */ 297 protected Object onGetDefaultValue(TypedArray a, int index) { 298 return null; 299 } 300 301 /** 302 * Sets an {@link Intent} to be used for 303 * {@link Context#startActivity(Intent)} when this Preference is clicked. 304 * 305 * @param intent The intent associated with this Preference. 306 */ 307 public void setIntent(Intent intent) { 308 mIntent = intent; 309 } 310 311 /** 312 * Return the {@link Intent} associated with this Preference. 313 * 314 * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML. 315 */ 316 public Intent getIntent() { 317 return mIntent; 318 } 319 320 /** 321 * Sets the layout resource that is inflated as the {@link View} to be shown 322 * for this Preference. In most cases, the default layout is sufficient for 323 * custom Preference objects and only the widget layout needs to be changed. 324 * <p> 325 * This layout should contain a {@link ViewGroup} with ID 326 * {@link android.R.id#widget_frame} to be the parent of the specific widget 327 * for this Preference. It should similarly contain 328 * {@link android.R.id#title} and {@link android.R.id#summary}. 329 * 330 * @param layoutResId The layout resource ID to be inflated and returned as 331 * a {@link View}. 332 * @see #setWidgetLayoutResource(int) 333 */ 334 public void setLayoutResource(int layoutResId) { 335 336 if (!mHasSpecifiedLayout) { 337 mHasSpecifiedLayout = true; 338 } 339 340 mLayoutResId = layoutResId; 341 } 342 343 /** 344 * Gets the layout resource that will be shown as the {@link View} for this Preference. 345 * 346 * @return The layout resource ID. 347 */ 348 public int getLayoutResource() { 349 return mLayoutResId; 350 } 351 352 /** 353 * Sets The layout for the controllable widget portion of this Preference. This 354 * is inflated into the main layout. For example, a {@link CheckBoxPreference} 355 * would specify a custom layout (consisting of just the CheckBox) here, 356 * instead of creating its own main layout. 357 * 358 * @param widgetLayoutResId The layout resource ID to be inflated into the 359 * main layout. 360 * @see #setLayoutResource(int) 361 */ 362 public void setWidgetLayoutResource(int widgetLayoutResId) { 363 mWidgetLayoutResId = widgetLayoutResId; 364 } 365 366 /** 367 * Gets the layout resource for the controllable widget portion of this Preference. 368 * 369 * @return The layout resource ID. 370 */ 371 public int getWidgetLayoutResource() { 372 return mWidgetLayoutResId; 373 } 374 375 /** 376 * Gets the View that will be shown in the {@link PreferenceActivity}. 377 * 378 * @param convertView The old View to reuse, if possible. Note: You should 379 * check that this View is non-null and of an appropriate type 380 * before using. If it is not possible to convert this View to 381 * display the correct data, this method can create a new View. 382 * @param parent The parent that this View will eventually be attached to. 383 * @return Returns the same Preference object, for chaining multiple calls 384 * into a single statement. 385 * @see #onCreateView(ViewGroup) 386 * @see #onBindView(View) 387 */ 388 public View getView(View convertView, ViewGroup parent) { 389 if (convertView == null) { 390 convertView = onCreateView(parent); 391 } 392 onBindView(convertView); 393 return convertView; 394 } 395 396 /** 397 * Creates the View to be shown for this Preference in the 398 * {@link PreferenceActivity}. The default behavior is to inflate the main 399 * layout of this Preference (see {@link #setLayoutResource(int)}. If 400 * changing this behavior, please specify a {@link ViewGroup} with ID 401 * {@link android.R.id#widget_frame}. 402 * <p> 403 * Make sure to call through to the superclass's implementation. 404 * 405 * @param parent The parent that this View will eventually be attached to. 406 * @return The View that displays this Preference. 407 * @see #onBindView(View) 408 */ 409 protected View onCreateView(ViewGroup parent) { 410 final LayoutInflater layoutInflater = 411 (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 412 413 final View layout = layoutInflater.inflate(mLayoutResId, parent, false); 414 415 if (mWidgetLayoutResId != 0) { 416 final ViewGroup widgetFrame = (ViewGroup)layout.findViewById(com.android.internal.R.id.widget_frame); 417 layoutInflater.inflate(mWidgetLayoutResId, widgetFrame); 418 } 419 420 return layout; 421 } 422 423 /** 424 * Binds the created View to the data for this Preference. 425 * <p> 426 * This is a good place to grab references to custom Views in the layout and 427 * set properties on them. 428 * <p> 429 * Make sure to call through to the superclass's implementation. 430 * 431 * @param view The View that shows this Preference. 432 * @see #onCreateView(ViewGroup) 433 */ 434 protected void onBindView(View view) { 435 TextView textView = (TextView) view.findViewById(com.android.internal.R.id.title); 436 if (textView != null) { 437 textView.setText(getTitle()); 438 } 439 440 textView = (TextView) view.findViewById(com.android.internal.R.id.summary); 441 if (textView != null) { 442 final CharSequence summary = getSummary(); 443 if (!TextUtils.isEmpty(summary)) { 444 if (textView.getVisibility() != View.VISIBLE) { 445 textView.setVisibility(View.VISIBLE); 446 } 447 448 textView.setText(getSummary()); 449 } else { 450 if (textView.getVisibility() != View.GONE) { 451 textView.setVisibility(View.GONE); 452 } 453 } 454 } 455 456 if (mShouldDisableView) { 457 setEnabledStateOnViews(view, isEnabled()); 458 } 459 } 460 461 /** 462 * Makes sure the view (and any children) get the enabled state changed. 463 */ 464 private void setEnabledStateOnViews(View v, boolean enabled) { 465 v.setEnabled(enabled); 466 467 if (v instanceof ViewGroup) { 468 final ViewGroup vg = (ViewGroup) v; 469 for (int i = vg.getChildCount() - 1; i >= 0; i--) { 470 setEnabledStateOnViews(vg.getChildAt(i), enabled); 471 } 472 } 473 } 474 475 /** 476 * Sets the order of this Preference with respect to other 477 * Preference objects on the same level. If this is not specified, the 478 * default behavior is to sort alphabetically. The 479 * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order 480 * Preference objects based on the order they appear in the XML. 481 * 482 * @param order The order for this Preference. A lower value will be shown 483 * first. Use {@link #DEFAULT_ORDER} to sort alphabetically or 484 * allow ordering from XML. 485 * @see PreferenceGroup#setOrderingAsAdded(boolean) 486 * @see #DEFAULT_ORDER 487 */ 488 public void setOrder(int order) { 489 if (order != mOrder) { 490 mOrder = order; 491 492 // Reorder the list 493 notifyHierarchyChanged(); 494 } 495 } 496 497 /** 498 * Gets the order of this Preference with respect to other Preference objects 499 * on the same level. 500 * 501 * @return The order of this Preference. 502 * @see #setOrder(int) 503 */ 504 public int getOrder() { 505 return mOrder; 506 } 507 508 /** 509 * Sets the title for this Preference with a CharSequence. 510 * This title will be placed into the ID 511 * {@link android.R.id#title} within the View created by 512 * {@link #onCreateView(ViewGroup)}. 513 * 514 * @param title The title for this Preference. 515 */ 516 public void setTitle(CharSequence title) { 517 if (title == null && mTitle != null || title != null && !title.equals(mTitle)) { 518 mTitle = title; 519 notifyChanged(); 520 } 521 } 522 523 /** 524 * Sets the title for this Preference with a resource ID. 525 * 526 * @see #setTitle(CharSequence) 527 * @param titleResId The title as a resource ID. 528 */ 529 public void setTitle(int titleResId) { 530 setTitle(mContext.getString(titleResId)); 531 } 532 533 /** 534 * Returns the title of this Preference. 535 * 536 * @return The title. 537 * @see #setTitle(CharSequence) 538 */ 539 public CharSequence getTitle() { 540 return mTitle; 541 } 542 543 /** 544 * Returns the summary of this Preference. 545 * 546 * @return The summary. 547 * @see #setSummary(CharSequence) 548 */ 549 public CharSequence getSummary() { 550 return mSummary; 551 } 552 553 /** 554 * Sets the summary for this Preference with a CharSequence. 555 * 556 * @param summary The summary for the preference. 557 */ 558 public void setSummary(CharSequence summary) { 559 if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) { 560 mSummary = summary; 561 notifyChanged(); 562 } 563 } 564 565 /** 566 * Sets the summary for this Preference with a resource ID. 567 * 568 * @see #setSummary(CharSequence) 569 * @param summaryResId The summary as a resource. 570 */ 571 public void setSummary(int summaryResId) { 572 setSummary(mContext.getString(summaryResId)); 573 } 574 575 /** 576 * Sets whether this Preference is enabled. If disabled, it will 577 * not handle clicks. 578 * 579 * @param enabled Set true to enable it. 580 */ 581 public void setEnabled(boolean enabled) { 582 if (mEnabled != enabled) { 583 mEnabled = enabled; 584 585 // Enabled state can change dependent preferences' states, so notify 586 notifyDependencyChange(shouldDisableDependents()); 587 588 notifyChanged(); 589 } 590 } 591 592 /** 593 * Checks whether this Preference should be enabled in the list. 594 * 595 * @return True if this Preference is enabled, false otherwise. 596 */ 597 public boolean isEnabled() { 598 return mEnabled && mDependencyMet; 599 } 600 601 /** 602 * Sets whether this Preference is selectable. 603 * 604 * @param selectable Set true to make it selectable. 605 */ 606 public void setSelectable(boolean selectable) { 607 if (mSelectable != selectable) { 608 mSelectable = selectable; 609 notifyChanged(); 610 } 611 } 612 613 /** 614 * Checks whether this Preference should be selectable in the list. 615 * 616 * @return True if it is selectable, false otherwise. 617 */ 618 public boolean isSelectable() { 619 return mSelectable; 620 } 621 622 /** 623 * Sets whether this Preference should disable its view when it gets 624 * disabled. 625 * <p> 626 * For example, set this and {@link #setEnabled(boolean)} to false for 627 * preferences that are only displaying information and 1) should not be 628 * clickable 2) should not have the view set to the disabled state. 629 * 630 * @param shouldDisableView Set true if this preference should disable its view 631 * when the preference is disabled. 632 */ 633 public void setShouldDisableView(boolean shouldDisableView) { 634 mShouldDisableView = shouldDisableView; 635 notifyChanged(); 636 } 637 638 /** 639 * Checks whether this Preference should disable its view when it's action is disabled. 640 * @see #setShouldDisableView(boolean) 641 * @return True if it should disable the view. 642 */ 643 public boolean getShouldDisableView() { 644 return mShouldDisableView; 645 } 646 647 /** 648 * Returns a unique ID for this Preference. This ID should be unique across all 649 * Preference objects in a hierarchy. 650 * 651 * @return A unique ID for this Preference. 652 */ 653 long getId() { 654 return mId; 655 } 656 657 /** 658 * Processes a click on the preference. This includes saving the value to 659 * the {@link SharedPreferences}. However, the overridden method should 660 * call {@link #callChangeListener(Object)} to make sure the client wants to 661 * update the preference's state with the new value. 662 */ 663 protected void onClick() { 664 } 665 666 /** 667 * Sets the key for this Preference, which is used as a key to the 668 * {@link SharedPreferences}. This should be unique for the package. 669 * 670 * @param key The key for the preference. 671 */ 672 public void setKey(String key) { 673 mKey = key; 674 675 if (mRequiresKey && !hasKey()) { 676 requireKey(); 677 } 678 } 679 680 /** 681 * Gets the key for this Preference, which is also the key used for storing 682 * values into SharedPreferences. 683 * 684 * @return The key. 685 */ 686 public String getKey() { 687 return mKey; 688 } 689 690 /** 691 * Checks whether the key is present, and if it isn't throws an 692 * exception. This should be called by subclasses that store preferences in 693 * the {@link SharedPreferences}. 694 * 695 * @throws IllegalStateException If there is no key assigned. 696 */ 697 void requireKey() { 698 if (mKey == null) { 699 throw new IllegalStateException("Preference does not have a key assigned."); 700 } 701 702 mRequiresKey = true; 703 } 704 705 /** 706 * Checks whether this Preference has a valid key. 707 * 708 * @return True if the key exists and is not a blank string, false otherwise. 709 */ 710 public boolean hasKey() { 711 return !TextUtils.isEmpty(mKey); 712 } 713 714 /** 715 * Checks whether this Preference is persistent. If it is, it stores its value(s) into 716 * the persistent {@link SharedPreferences} storage. 717 * 718 * @return True if it is persistent. 719 */ 720 public boolean isPersistent() { 721 return mPersistent; 722 } 723 724 /** 725 * Checks whether, at the given time this method is called, 726 * this Preference should store/restore its value(s) into the 727 * {@link SharedPreferences}. This, at minimum, checks whether this 728 * Preference is persistent and it currently has a key. Before you 729 * save/restore from the {@link SharedPreferences}, check this first. 730 * 731 * @return True if it should persist the value. 732 */ 733 protected boolean shouldPersist() { 734 return mPreferenceManager != null && isPersistent() && hasKey(); 735 } 736 737 /** 738 * Sets whether this Preference is persistent. When persistent, 739 * it stores its value(s) into the persistent {@link SharedPreferences} 740 * storage. 741 * 742 * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}. 743 */ 744 public void setPersistent(boolean persistent) { 745 mPersistent = persistent; 746 } 747 748 /** 749 * Call this method after the user changes the preference, but before the 750 * internal state is set. This allows the client to ignore the user value. 751 * 752 * @param newValue The new value of this Preference. 753 * @return True if the user value should be set as the preference 754 * value (and persisted). 755 */ 756 protected boolean callChangeListener(Object newValue) { 757 return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue); 758 } 759 760 /** 761 * Sets the callback to be invoked when this Preference is changed by the 762 * user (but before the internal state has been updated). 763 * 764 * @param onPreferenceChangeListener The callback to be invoked. 765 */ 766 public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) { 767 mOnChangeListener = onPreferenceChangeListener; 768 } 769 770 /** 771 * Returns the callback to be invoked when this Preference is changed by the 772 * user (but before the internal state has been updated). 773 * 774 * @return The callback to be invoked. 775 */ 776 public OnPreferenceChangeListener getOnPreferenceChangeListener() { 777 return mOnChangeListener; 778 } 779 780 /** 781 * Sets the callback to be invoked when this Preference is clicked. 782 * 783 * @param onPreferenceClickListener The callback to be invoked. 784 */ 785 public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) { 786 mOnClickListener = onPreferenceClickListener; 787 } 788 789 /** 790 * Returns the callback to be invoked when this Preference is clicked. 791 * 792 * @return The callback to be invoked. 793 */ 794 public OnPreferenceClickListener getOnPreferenceClickListener() { 795 return mOnClickListener; 796 } 797 798 /** 799 * Called when a click should be performed. 800 * 801 * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click 802 * listener should be called in the proper order (between other 803 * processing). May be null. 804 */ 805 void performClick(PreferenceScreen preferenceScreen) { 806 807 if (!isEnabled()) { 808 return; 809 } 810 811 onClick(); 812 813 if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) { 814 return; 815 } 816 817 PreferenceManager preferenceManager = getPreferenceManager(); 818 if (preferenceManager != null) { 819 PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager 820 .getOnPreferenceTreeClickListener(); 821 if (preferenceScreen != null && listener != null 822 && listener.onPreferenceTreeClick(preferenceScreen, this)) { 823 return; 824 } 825 } 826 827 if (mIntent != null) { 828 Context context = getContext(); 829 context.startActivity(mIntent); 830 } 831 } 832 833 /** 834 * Returns the {@link android.content.Context} of this Preference. 835 * Each Preference in a Preference hierarchy can be 836 * from different Context (for example, if multiple activities provide preferences into a single 837 * {@link PreferenceActivity}). This Context will be used to save the Preference values. 838 * 839 * @return The Context of this Preference. 840 */ 841 public Context getContext() { 842 return mContext; 843 } 844 845 /** 846 * Returns the {@link SharedPreferences} where this Preference can read its 847 * value(s). Usually, it's easier to use one of the helper read methods: 848 * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)}, 849 * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)}, 850 * {@link #getPersistedString(String)}. To save values, see 851 * {@link #getEditor()}. 852 * <p> 853 * In some cases, writes to the {@link #getEditor()} will not be committed 854 * right away and hence not show up in the returned 855 * {@link SharedPreferences}, this is intended behavior to improve 856 * performance. 857 * 858 * @return The {@link SharedPreferences} where this Preference reads its 859 * value(s), or null if it isn't attached to a Preference hierarchy. 860 * @see #getEditor() 861 */ 862 public SharedPreferences getSharedPreferences() { 863 if (mPreferenceManager == null) { 864 return null; 865 } 866 867 return mPreferenceManager.getSharedPreferences(); 868 } 869 870 /** 871 * Returns an {@link SharedPreferences.Editor} where this Preference can 872 * save its value(s). Usually it's easier to use one of the helper save 873 * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)}, 874 * {@link #persistInt(int)}, {@link #persistLong(long)}, 875 * {@link #persistString(String)}. To read values, see 876 * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns 877 * true, it is this Preference's responsibility to commit. 878 * <p> 879 * In some cases, writes to this will not be committed right away and hence 880 * not show up in the SharedPreferences, this is intended behavior to 881 * improve performance. 882 * 883 * @return A {@link SharedPreferences.Editor} where this preference saves 884 * its value(s), or null if it isn't attached to a Preference 885 * hierarchy. 886 * @see #shouldCommit() 887 * @see #getSharedPreferences() 888 */ 889 public SharedPreferences.Editor getEditor() { 890 if (mPreferenceManager == null) { 891 return null; 892 } 893 894 return mPreferenceManager.getEditor(); 895 } 896 897 /** 898 * Returns whether the {@link Preference} should commit its saved value(s) in 899 * {@link #getEditor()}. This may return false in situations where batch 900 * committing is being done (by the manager) to improve performance. 901 * 902 * @return Whether the Preference should commit its saved value(s). 903 * @see #getEditor() 904 */ 905 public boolean shouldCommit() { 906 if (mPreferenceManager == null) { 907 return false; 908 } 909 910 return mPreferenceManager.shouldCommit(); 911 } 912 913 /** 914 * Compares Preference objects based on order (if set), otherwise alphabetically on the titles. 915 * 916 * @param another The Preference to compare to this one. 917 * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>; 918 * greater than 0 if this Preference sorts after <var>another</var>. 919 */ 920 public int compareTo(Preference another) { 921 if (mOrder != DEFAULT_ORDER 922 || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) { 923 // Do order comparison 924 return mOrder - another.mOrder; 925 } else if (mTitle == null) { 926 return 1; 927 } else if (another.mTitle == null) { 928 return -1; 929 } else { 930 // Do name comparison 931 return CharSequences.compareToIgnoreCase(mTitle, another.mTitle); 932 } 933 } 934 935 /** 936 * Sets the internal change listener. 937 * 938 * @param listener The listener. 939 * @see #notifyChanged() 940 */ 941 final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) { 942 mListener = listener; 943 } 944 945 /** 946 * Should be called when the data of this {@link Preference} has changed. 947 */ 948 protected void notifyChanged() { 949 if (mListener != null) { 950 mListener.onPreferenceChange(this); 951 } 952 } 953 954 /** 955 * Should be called when a Preference has been 956 * added/removed from this group, or the ordering should be 957 * re-evaluated. 958 */ 959 protected void notifyHierarchyChanged() { 960 if (mListener != null) { 961 mListener.onPreferenceHierarchyChange(this); 962 } 963 } 964 965 /** 966 * Gets the {@link PreferenceManager} that manages this Preference object's tree. 967 * 968 * @return The {@link PreferenceManager}. 969 */ 970 public PreferenceManager getPreferenceManager() { 971 return mPreferenceManager; 972 } 973 974 /** 975 * Called when this Preference has been attached to a Preference hierarchy. 976 * Make sure to call the super implementation. 977 * 978 * @param preferenceManager The PreferenceManager of the hierarchy. 979 */ 980 protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { 981 mPreferenceManager = preferenceManager; 982 983 mId = preferenceManager.getNextId(); 984 985 dispatchSetInitialValue(); 986 } 987 988 /** 989 * Called when the Preference hierarchy has been attached to the 990 * {@link PreferenceActivity}. This can also be called when this 991 * Preference has been attached to a group that was already attached 992 * to the {@link PreferenceActivity}. 993 */ 994 protected void onAttachedToActivity() { 995 // At this point, the hierarchy that this preference is in is connected 996 // with all other preferences. 997 registerDependency(); 998 } 999 1000 private void registerDependency() { 1001 1002 if (TextUtils.isEmpty(mDependencyKey)) return; 1003 1004 Preference preference = findPreferenceInHierarchy(mDependencyKey); 1005 if (preference != null) { 1006 preference.registerDependent(this); 1007 } else { 1008 throw new IllegalStateException("Dependency \"" + mDependencyKey 1009 + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\""); 1010 } 1011 } 1012 1013 private void unregisterDependency() { 1014 if (mDependencyKey != null) { 1015 final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey); 1016 if (oldDependency != null) { 1017 oldDependency.unregisterDependent(this); 1018 } 1019 } 1020 } 1021 1022 /** 1023 * Finds a Preference in this hierarchy (the whole thing, 1024 * even above/below your {@link PreferenceScreen} screen break) with the given 1025 * key. 1026 * <p> 1027 * This only functions after we have been attached to a hierarchy. 1028 * 1029 * @param key The key of the Preference to find. 1030 * @return The Preference that uses the given key. 1031 */ 1032 protected Preference findPreferenceInHierarchy(String key) { 1033 if (TextUtils.isEmpty(key) || mPreferenceManager == null) { 1034 return null; 1035 } 1036 1037 return mPreferenceManager.findPreference(key); 1038 } 1039 1040 /** 1041 * Adds a dependent Preference on this Preference so we can notify it. 1042 * Usually, the dependent Preference registers itself (it's good for it to 1043 * know it depends on something), so please use 1044 * {@link Preference#setDependency(String)} on the dependent Preference. 1045 * 1046 * @param dependent The dependent Preference that will be enabled/disabled 1047 * according to the state of this Preference. 1048 */ 1049 private void registerDependent(Preference dependent) { 1050 if (mDependents == null) { 1051 mDependents = new ArrayList<Preference>(); 1052 } 1053 1054 mDependents.add(dependent); 1055 1056 dependent.onDependencyChanged(this, shouldDisableDependents()); 1057 } 1058 1059 /** 1060 * Removes a dependent Preference on this Preference. 1061 * 1062 * @param dependent The dependent Preference that will be enabled/disabled 1063 * according to the state of this Preference. 1064 * @return Returns the same Preference object, for chaining multiple calls 1065 * into a single statement. 1066 */ 1067 private void unregisterDependent(Preference dependent) { 1068 if (mDependents != null) { 1069 mDependents.remove(dependent); 1070 } 1071 } 1072 1073 /** 1074 * Notifies any listening dependents of a change that affects the 1075 * dependency. 1076 * 1077 * @param disableDependents Whether this Preference should disable 1078 * its dependents. 1079 */ 1080 public void notifyDependencyChange(boolean disableDependents) { 1081 final List<Preference> dependents = mDependents; 1082 1083 if (dependents == null) { 1084 return; 1085 } 1086 1087 final int dependentsCount = dependents.size(); 1088 for (int i = 0; i < dependentsCount; i++) { 1089 dependents.get(i).onDependencyChanged(this, disableDependents); 1090 } 1091 } 1092 1093 /** 1094 * Called when the dependency changes. 1095 * 1096 * @param dependency The Preference that this Preference depends on. 1097 * @param disableDependent Set true to disable this Preference. 1098 */ 1099 public void onDependencyChanged(Preference dependency, boolean disableDependent) { 1100 if (mDependencyMet == disableDependent) { 1101 mDependencyMet = !disableDependent; 1102 1103 // Enabled state can change dependent preferences' states, so notify 1104 notifyDependencyChange(shouldDisableDependents()); 1105 1106 notifyChanged(); 1107 } 1108 } 1109 1110 /** 1111 * Checks whether this preference's dependents should currently be 1112 * disabled. 1113 * 1114 * @return True if the dependents should be disabled, otherwise false. 1115 */ 1116 public boolean shouldDisableDependents() { 1117 return !isEnabled(); 1118 } 1119 1120 /** 1121 * Sets the key of a Preference that this Preference will depend on. If that 1122 * Preference is not set or is off, this Preference will be disabled. 1123 * 1124 * @param dependencyKey The key of the Preference that this depends on. 1125 */ 1126 public void setDependency(String dependencyKey) { 1127 // Unregister the old dependency, if we had one 1128 unregisterDependency(); 1129 1130 // Register the new 1131 mDependencyKey = dependencyKey; 1132 registerDependency(); 1133 } 1134 1135 /** 1136 * Returns the key of the dependency on this Preference. 1137 * 1138 * @return The key of the dependency. 1139 * @see #setDependency(String) 1140 */ 1141 public String getDependency() { 1142 return mDependencyKey; 1143 } 1144 1145 /** 1146 * Called when this Preference is being removed from the hierarchy. You 1147 * should remove any references to this Preference that you know about. Make 1148 * sure to call through to the superclass implementation. 1149 */ 1150 protected void onPrepareForRemoval() { 1151 unregisterDependency(); 1152 } 1153 1154 /** 1155 * Sets the default value for this Preference, which will be set either if 1156 * persistence is off or persistence is on and the preference is not found 1157 * in the persistent storage. 1158 * 1159 * @param defaultValue The default value. 1160 */ 1161 public void setDefaultValue(Object defaultValue) { 1162 mDefaultValue = defaultValue; 1163 } 1164 1165 private void dispatchSetInitialValue() { 1166 // By now, we know if we are persistent. 1167 final boolean shouldPersist = shouldPersist(); 1168 if (!shouldPersist || !getSharedPreferences().contains(mKey)) { 1169 if (mDefaultValue != null) { 1170 onSetInitialValue(false, mDefaultValue); 1171 } 1172 } else { 1173 onSetInitialValue(true, null); 1174 } 1175 } 1176 1177 /** 1178 * Implement this to set the initial value of the Preference. 1179 * <p> 1180 * If <var>restorePersistedValue</var> is true, you should restore the 1181 * Preference value from the {@link android.content.SharedPreferences}. If 1182 * <var>restorePersistedValue</var> is false, you should set the Preference 1183 * value to defaultValue that is given (and possibly store to SharedPreferences 1184 * if {@link #shouldPersist()} is true). 1185 * <p> 1186 * This may not always be called. One example is if it should not persist 1187 * but there is no default value given. 1188 * 1189 * @param restorePersistedValue True to restore the persisted value; 1190 * false to use the given <var>defaultValue</var>. 1191 * @param defaultValue The default value for this Preference. Only use this 1192 * if <var>restorePersistedValue</var> is false. 1193 */ 1194 protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 1195 } 1196 1197 private void tryCommit(SharedPreferences.Editor editor) { 1198 if (mPreferenceManager.shouldCommit()) { 1199 editor.commit(); 1200 } 1201 } 1202 1203 /** 1204 * Attempts to persist a String to the {@link android.content.SharedPreferences}. 1205 * <p> 1206 * This will check if this Preference is persistent, get an editor from 1207 * the {@link PreferenceManager}, put in the string, and check if we should commit (and 1208 * commit if so). 1209 * 1210 * @param value The value to persist. 1211 * @return True if the Preference is persistent. (This is not whether the 1212 * value was persisted, since we may not necessarily commit if there 1213 * will be a batch commit later.) 1214 * @see #getPersistedString(String) 1215 */ 1216 protected boolean persistString(String value) { 1217 if (shouldPersist()) { 1218 // Shouldn't store null 1219 if (value == getPersistedString(null)) { 1220 // It's already there, so the same as persisting 1221 return true; 1222 } 1223 1224 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1225 editor.putString(mKey, value); 1226 tryCommit(editor); 1227 return true; 1228 } 1229 return false; 1230 } 1231 1232 /** 1233 * Attempts to get a persisted String from the {@link android.content.SharedPreferences}. 1234 * <p> 1235 * This will check if this Preference is persistent, get the SharedPreferences 1236 * from the {@link PreferenceManager}, and get the value. 1237 * 1238 * @param defaultReturnValue The default value to return if either the 1239 * Preference is not persistent or the Preference is not in the 1240 * shared preferences. 1241 * @return The value from the SharedPreferences or the default return 1242 * value. 1243 * @see #persistString(String) 1244 */ 1245 protected String getPersistedString(String defaultReturnValue) { 1246 if (!shouldPersist()) { 1247 return defaultReturnValue; 1248 } 1249 1250 return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue); 1251 } 1252 1253 /** 1254 * Attempts to persist an int to the {@link android.content.SharedPreferences}. 1255 * 1256 * @param value The value to persist. 1257 * @return True if the Preference is persistent. (This is not whether the 1258 * value was persisted, since we may not necessarily commit if there 1259 * will be a batch commit later.) 1260 * @see #persistString(String) 1261 * @see #getPersistedInt(int) 1262 */ 1263 protected boolean persistInt(int value) { 1264 if (shouldPersist()) { 1265 if (value == getPersistedInt(~value)) { 1266 // It's already there, so the same as persisting 1267 return true; 1268 } 1269 1270 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1271 editor.putInt(mKey, value); 1272 tryCommit(editor); 1273 return true; 1274 } 1275 return false; 1276 } 1277 1278 /** 1279 * Attempts to get a persisted int from the {@link android.content.SharedPreferences}. 1280 * 1281 * @param defaultReturnValue The default value to return if either this 1282 * Preference is not persistent or this Preference is not in the 1283 * SharedPreferences. 1284 * @return The value from the SharedPreferences or the default return 1285 * value. 1286 * @see #getPersistedString(String) 1287 * @see #persistInt(int) 1288 */ 1289 protected int getPersistedInt(int defaultReturnValue) { 1290 if (!shouldPersist()) { 1291 return defaultReturnValue; 1292 } 1293 1294 return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue); 1295 } 1296 1297 /** 1298 * Attempts to persist a float to the {@link android.content.SharedPreferences}. 1299 * 1300 * @param value The value to persist. 1301 * @return True if this Preference is persistent. (This is not whether the 1302 * value was persisted, since we may not necessarily commit if there 1303 * will be a batch commit later.) 1304 * @see #persistString(String) 1305 * @see #getPersistedFloat(float) 1306 */ 1307 protected boolean persistFloat(float value) { 1308 if (shouldPersist()) { 1309 if (value == getPersistedFloat(Float.NaN)) { 1310 // It's already there, so the same as persisting 1311 return true; 1312 } 1313 1314 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1315 editor.putFloat(mKey, value); 1316 tryCommit(editor); 1317 return true; 1318 } 1319 return false; 1320 } 1321 1322 /** 1323 * Attempts to get a persisted float from the {@link android.content.SharedPreferences}. 1324 * 1325 * @param defaultReturnValue The default value to return if either this 1326 * Preference is not persistent or this Preference is not in the 1327 * SharedPreferences. 1328 * @return The value from the SharedPreferences or the default return 1329 * value. 1330 * @see #getPersistedString(String) 1331 * @see #persistFloat(float) 1332 */ 1333 protected float getPersistedFloat(float defaultReturnValue) { 1334 if (!shouldPersist()) { 1335 return defaultReturnValue; 1336 } 1337 1338 return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue); 1339 } 1340 1341 /** 1342 * Attempts to persist a long to the {@link android.content.SharedPreferences}. 1343 * 1344 * @param value The value to persist. 1345 * @return True if this Preference is persistent. (This is not whether the 1346 * value was persisted, since we may not necessarily commit if there 1347 * will be a batch commit later.) 1348 * @see #persistString(String) 1349 * @see #getPersistedLong(long) 1350 */ 1351 protected boolean persistLong(long value) { 1352 if (shouldPersist()) { 1353 if (value == getPersistedLong(~value)) { 1354 // It's already there, so the same as persisting 1355 return true; 1356 } 1357 1358 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1359 editor.putLong(mKey, value); 1360 tryCommit(editor); 1361 return true; 1362 } 1363 return false; 1364 } 1365 1366 /** 1367 * Attempts to get a persisted long from the {@link android.content.SharedPreferences}. 1368 * 1369 * @param defaultReturnValue The default value to return if either this 1370 * Preference is not persistent or this Preference is not in the 1371 * SharedPreferences. 1372 * @return The value from the SharedPreferences or the default return 1373 * value. 1374 * @see #getPersistedString(String) 1375 * @see #persistLong(long) 1376 */ 1377 protected long getPersistedLong(long defaultReturnValue) { 1378 if (!shouldPersist()) { 1379 return defaultReturnValue; 1380 } 1381 1382 return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue); 1383 } 1384 1385 /** 1386 * Attempts to persist a boolean to the {@link android.content.SharedPreferences}. 1387 * 1388 * @param value The value to persist. 1389 * @return True if this Preference is persistent. (This is not whether the 1390 * value was persisted, since we may not necessarily commit if there 1391 * will be a batch commit later.) 1392 * @see #persistString(String) 1393 * @see #getPersistedBoolean(boolean) 1394 */ 1395 protected boolean persistBoolean(boolean value) { 1396 if (shouldPersist()) { 1397 if (value == getPersistedBoolean(!value)) { 1398 // It's already there, so the same as persisting 1399 return true; 1400 } 1401 1402 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1403 editor.putBoolean(mKey, value); 1404 tryCommit(editor); 1405 return true; 1406 } 1407 return false; 1408 } 1409 1410 /** 1411 * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}. 1412 * 1413 * @param defaultReturnValue The default value to return if either this 1414 * Preference is not persistent or this Preference is not in the 1415 * SharedPreferences. 1416 * @return The value from the SharedPreferences or the default return 1417 * value. 1418 * @see #getPersistedString(String) 1419 * @see #persistBoolean(boolean) 1420 */ 1421 protected boolean getPersistedBoolean(boolean defaultReturnValue) { 1422 if (!shouldPersist()) { 1423 return defaultReturnValue; 1424 } 1425 1426 return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue); 1427 } 1428 1429 boolean hasSpecifiedLayout() { 1430 return mHasSpecifiedLayout; 1431 } 1432 1433 @Override 1434 public String toString() { 1435 return getFilterableStringBuilder().toString(); 1436 } 1437 1438 /** 1439 * Returns the text that will be used to filter this Preference depending on 1440 * user input. 1441 * <p> 1442 * If overridding and calling through to the superclass, make sure to prepend 1443 * your additions with a space. 1444 * 1445 * @return Text as a {@link StringBuilder} that will be used to filter this 1446 * preference. By default, this is the title and summary 1447 * (concatenated with a space). 1448 */ 1449 StringBuilder getFilterableStringBuilder() { 1450 StringBuilder sb = new StringBuilder(); 1451 CharSequence title = getTitle(); 1452 if (!TextUtils.isEmpty(title)) { 1453 sb.append(title).append(' '); 1454 } 1455 CharSequence summary = getSummary(); 1456 if (!TextUtils.isEmpty(summary)) { 1457 sb.append(summary).append(' '); 1458 } 1459 // Drop the last space 1460 sb.setLength(sb.length() - 1); 1461 return sb; 1462 } 1463 1464 /** 1465 * Store this Preference hierarchy's frozen state into the given container. 1466 * 1467 * @param container The Bundle in which to save the instance of this Preference. 1468 * 1469 * @see #restoreHierarchyState 1470 * @see #onSaveInstanceState 1471 */ 1472 public void saveHierarchyState(Bundle container) { 1473 dispatchSaveInstanceState(container); 1474 } 1475 1476 /** 1477 * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children. 1478 * May be overridden to modify how the save happens for children. For example, some 1479 * Preference objects may want to not store an instance for their children. 1480 * 1481 * @param container The Bundle in which to save the instance of this Preference. 1482 * 1483 * @see #saveHierarchyState 1484 * @see #onSaveInstanceState 1485 */ 1486 void dispatchSaveInstanceState(Bundle container) { 1487 if (hasKey()) { 1488 mBaseMethodCalled = false; 1489 Parcelable state = onSaveInstanceState(); 1490 if (!mBaseMethodCalled) { 1491 throw new IllegalStateException( 1492 "Derived class did not call super.onSaveInstanceState()"); 1493 } 1494 if (state != null) { 1495 container.putParcelable(mKey, state); 1496 } 1497 } 1498 } 1499 1500 /** 1501 * Hook allowing a Preference to generate a representation of its internal 1502 * state that can later be used to create a new instance with that same 1503 * state. This state should only contain information that is not persistent 1504 * or can be reconstructed later. 1505 * 1506 * @return A Parcelable object containing the current dynamic state of 1507 * this Preference, or null if there is nothing interesting to save. 1508 * The default implementation returns null. 1509 * @see #onRestoreInstanceState 1510 * @see #saveHierarchyState 1511 */ 1512 protected Parcelable onSaveInstanceState() { 1513 mBaseMethodCalled = true; 1514 return BaseSavedState.EMPTY_STATE; 1515 } 1516 1517 /** 1518 * Restore this Preference hierarchy's previously saved state from the given container. 1519 * 1520 * @param container The Bundle that holds the previously saved state. 1521 * 1522 * @see #saveHierarchyState 1523 * @see #onRestoreInstanceState 1524 */ 1525 public void restoreHierarchyState(Bundle container) { 1526 dispatchRestoreInstanceState(container); 1527 } 1528 1529 /** 1530 * Called by {@link #restoreHierarchyState} to retrieve the saved state for this 1531 * Preference and its children. May be overridden to modify how restoring 1532 * happens to the children of a Preference. For example, some Preference objects may 1533 * not want to save state for their children. 1534 * 1535 * @param container The Bundle that holds the previously saved state. 1536 * @see #restoreHierarchyState 1537 * @see #onRestoreInstanceState 1538 */ 1539 void dispatchRestoreInstanceState(Bundle container) { 1540 if (hasKey()) { 1541 Parcelable state = container.getParcelable(mKey); 1542 if (state != null) { 1543 mBaseMethodCalled = false; 1544 onRestoreInstanceState(state); 1545 if (!mBaseMethodCalled) { 1546 throw new IllegalStateException( 1547 "Derived class did not call super.onRestoreInstanceState()"); 1548 } 1549 } 1550 } 1551 } 1552 1553 /** 1554 * Hook allowing a Preference to re-apply a representation of its internal 1555 * state that had previously been generated by {@link #onSaveInstanceState}. 1556 * This function will never be called with a null state. 1557 * 1558 * @param state The saved state that had previously been returned by 1559 * {@link #onSaveInstanceState}. 1560 * @see #onSaveInstanceState 1561 * @see #restoreHierarchyState 1562 */ 1563 protected void onRestoreInstanceState(Parcelable state) { 1564 mBaseMethodCalled = true; 1565 if (state != BaseSavedState.EMPTY_STATE && state != null) { 1566 throw new IllegalArgumentException("Wrong state class -- expecting Preference State"); 1567 } 1568 } 1569 1570 /** 1571 * A base class for managing the instance state of a {@link Preference}. 1572 */ 1573 public static class BaseSavedState extends AbsSavedState { 1574 public BaseSavedState(Parcel source) { 1575 super(source); 1576 } 1577 1578 public BaseSavedState(Parcelable superState) { 1579 super(superState); 1580 } 1581 1582 public static final Parcelable.Creator<BaseSavedState> CREATOR = 1583 new Parcelable.Creator<BaseSavedState>() { 1584 public BaseSavedState createFromParcel(Parcel in) { 1585 return new BaseSavedState(in); 1586 } 1587 1588 public BaseSavedState[] newArray(int size) { 1589 return new BaseSavedState[size]; 1590 } 1591 }; 1592 } 1593 1594} 1595