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