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