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