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