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