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 final TextView titleView = (TextView) view.findViewById( 501 com.android.internal.R.id.title); 502 if (titleView != null) { 503 final CharSequence title = getTitle(); 504 if (!TextUtils.isEmpty(title)) { 505 titleView.setText(title); 506 titleView.setVisibility(View.VISIBLE); 507 } else { 508 titleView.setVisibility(View.GONE); 509 } 510 } 511 512 final TextView summaryView = (TextView) view.findViewById( 513 com.android.internal.R.id.summary); 514 if (summaryView != null) { 515 final CharSequence summary = getSummary(); 516 if (!TextUtils.isEmpty(summary)) { 517 summaryView.setText(summary); 518 summaryView.setVisibility(View.VISIBLE); 519 } else { 520 summaryView.setVisibility(View.GONE); 521 } 522 } 523 524 ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon); 525 if (imageView != null) { 526 if (mIconResId != 0 || mIcon != null) { 527 if (mIcon == null) { 528 mIcon = getContext().getResources().getDrawable(mIconResId); 529 } 530 if (mIcon != null) { 531 imageView.setImageDrawable(mIcon); 532 } 533 } 534 imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE); 535 } 536 537 if (mShouldDisableView) { 538 setEnabledStateOnViews(view, isEnabled()); 539 } 540 } 541 542 /** 543 * Makes sure the view (and any children) get the enabled state changed. 544 */ setEnabledStateOnViews(View v, boolean enabled)545 private void setEnabledStateOnViews(View v, boolean enabled) { 546 v.setEnabled(enabled); 547 548 if (v instanceof ViewGroup) { 549 final ViewGroup vg = (ViewGroup) v; 550 for (int i = vg.getChildCount() - 1; i >= 0; i--) { 551 setEnabledStateOnViews(vg.getChildAt(i), enabled); 552 } 553 } 554 } 555 556 /** 557 * Sets the order of this Preference with respect to other 558 * Preference objects on the same level. If this is not specified, the 559 * default behavior is to sort alphabetically. The 560 * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order 561 * Preference objects based on the order they appear in the XML. 562 * 563 * @param order The order for this Preference. A lower value will be shown 564 * first. Use {@link #DEFAULT_ORDER} to sort alphabetically or 565 * allow ordering from XML. 566 * @see PreferenceGroup#setOrderingAsAdded(boolean) 567 * @see #DEFAULT_ORDER 568 */ setOrder(int order)569 public void setOrder(int order) { 570 if (order != mOrder) { 571 mOrder = order; 572 573 // Reorder the list 574 notifyHierarchyChanged(); 575 } 576 } 577 578 /** 579 * Gets the order of this Preference with respect to other Preference objects 580 * on the same level. 581 * 582 * @return The order of this Preference. 583 * @see #setOrder(int) 584 */ getOrder()585 public int getOrder() { 586 return mOrder; 587 } 588 589 /** 590 * Sets the title for this Preference with a CharSequence. 591 * This title will be placed into the ID 592 * {@link android.R.id#title} within the View created by 593 * {@link #onCreateView(ViewGroup)}. 594 * 595 * @param title The title for this Preference. 596 */ setTitle(CharSequence title)597 public void setTitle(CharSequence title) { 598 if (title == null && mTitle != null || title != null && !title.equals(mTitle)) { 599 mTitleRes = 0; 600 mTitle = title; 601 notifyChanged(); 602 } 603 } 604 605 /** 606 * Sets the title for this Preference with a resource ID. 607 * 608 * @see #setTitle(CharSequence) 609 * @param titleResId The title as a resource ID. 610 */ setTitle(int titleResId)611 public void setTitle(int titleResId) { 612 setTitle(mContext.getString(titleResId)); 613 mTitleRes = titleResId; 614 } 615 616 /** 617 * Returns the title resource ID of this Preference. If the title did 618 * not come from a resource, 0 is returned. 619 * 620 * @return The title resource. 621 * @see #setTitle(int) 622 */ getTitleRes()623 public int getTitleRes() { 624 return mTitleRes; 625 } 626 627 /** 628 * Returns the title of this Preference. 629 * 630 * @return The title. 631 * @see #setTitle(CharSequence) 632 */ getTitle()633 public CharSequence getTitle() { 634 return mTitle; 635 } 636 637 /** 638 * Sets the icon for this Preference with a Drawable. 639 * This icon will be placed into the ID 640 * {@link android.R.id#icon} within the View created by 641 * {@link #onCreateView(ViewGroup)}. 642 * 643 * @param icon The optional icon for this Preference. 644 */ setIcon(Drawable icon)645 public void setIcon(Drawable icon) { 646 if ((icon == null && mIcon != null) || (icon != null && mIcon != icon)) { 647 mIcon = icon; 648 649 notifyChanged(); 650 } 651 } 652 653 /** 654 * Sets the icon for this Preference with a resource ID. 655 * 656 * @see #setIcon(Drawable) 657 * @param iconResId The icon as a resource ID. 658 */ setIcon(int iconResId)659 public void setIcon(int iconResId) { 660 mIconResId = iconResId; 661 setIcon(mContext.getResources().getDrawable(iconResId)); 662 } 663 664 /** 665 * Returns the icon of this Preference. 666 * 667 * @return The icon. 668 * @see #setIcon(Drawable) 669 */ getIcon()670 public Drawable getIcon() { 671 return mIcon; 672 } 673 674 /** 675 * Returns the summary of this Preference. 676 * 677 * @return The summary. 678 * @see #setSummary(CharSequence) 679 */ getSummary()680 public CharSequence getSummary() { 681 return mSummary; 682 } 683 684 /** 685 * Sets the summary for this Preference with a CharSequence. 686 * 687 * @param summary The summary for the preference. 688 */ setSummary(CharSequence summary)689 public void setSummary(CharSequence summary) { 690 if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) { 691 mSummary = summary; 692 notifyChanged(); 693 } 694 } 695 696 /** 697 * Sets the summary for this Preference with a resource ID. 698 * 699 * @see #setSummary(CharSequence) 700 * @param summaryResId The summary as a resource. 701 */ setSummary(int summaryResId)702 public void setSummary(int summaryResId) { 703 setSummary(mContext.getString(summaryResId)); 704 } 705 706 /** 707 * Sets whether this Preference is enabled. If disabled, it will 708 * not handle clicks. 709 * 710 * @param enabled Set true to enable it. 711 */ setEnabled(boolean enabled)712 public void setEnabled(boolean enabled) { 713 if (mEnabled != enabled) { 714 mEnabled = enabled; 715 716 // Enabled state can change dependent preferences' states, so notify 717 notifyDependencyChange(shouldDisableDependents()); 718 719 notifyChanged(); 720 } 721 } 722 723 /** 724 * Checks whether this Preference should be enabled in the list. 725 * 726 * @return True if this Preference is enabled, false otherwise. 727 */ isEnabled()728 public boolean isEnabled() { 729 return mEnabled && mDependencyMet; 730 } 731 732 /** 733 * Sets whether this Preference is selectable. 734 * 735 * @param selectable Set true to make it selectable. 736 */ setSelectable(boolean selectable)737 public void setSelectable(boolean selectable) { 738 if (mSelectable != selectable) { 739 mSelectable = selectable; 740 notifyChanged(); 741 } 742 } 743 744 /** 745 * Checks whether this Preference should be selectable in the list. 746 * 747 * @return True if it is selectable, false otherwise. 748 */ isSelectable()749 public boolean isSelectable() { 750 return mSelectable; 751 } 752 753 /** 754 * Sets whether this Preference should disable its view when it gets 755 * disabled. 756 * <p> 757 * For example, set this and {@link #setEnabled(boolean)} to false for 758 * preferences that are only displaying information and 1) should not be 759 * clickable 2) should not have the view set to the disabled state. 760 * 761 * @param shouldDisableView Set true if this preference should disable its view 762 * when the preference is disabled. 763 */ setShouldDisableView(boolean shouldDisableView)764 public void setShouldDisableView(boolean shouldDisableView) { 765 mShouldDisableView = shouldDisableView; 766 notifyChanged(); 767 } 768 769 /** 770 * Checks whether this Preference should disable its view when it's action is disabled. 771 * @see #setShouldDisableView(boolean) 772 * @return True if it should disable the view. 773 */ getShouldDisableView()774 public boolean getShouldDisableView() { 775 return mShouldDisableView; 776 } 777 778 /** 779 * Returns a unique ID for this Preference. This ID should be unique across all 780 * Preference objects in a hierarchy. 781 * 782 * @return A unique ID for this Preference. 783 */ getId()784 long getId() { 785 return mId; 786 } 787 788 /** 789 * Processes a click on the preference. This includes saving the value to 790 * the {@link SharedPreferences}. However, the overridden method should 791 * call {@link #callChangeListener(Object)} to make sure the client wants to 792 * update the preference's state with the new value. 793 */ onClick()794 protected void onClick() { 795 } 796 797 /** 798 * Sets the key for this Preference, which is used as a key to the 799 * {@link SharedPreferences}. This should be unique for the package. 800 * 801 * @param key The key for the preference. 802 */ setKey(String key)803 public void setKey(String key) { 804 mKey = key; 805 806 if (mRequiresKey && !hasKey()) { 807 requireKey(); 808 } 809 } 810 811 /** 812 * Gets the key for this Preference, which is also the key used for storing 813 * values into SharedPreferences. 814 * 815 * @return The key. 816 */ getKey()817 public String getKey() { 818 return mKey; 819 } 820 821 /** 822 * Checks whether the key is present, and if it isn't throws an 823 * exception. This should be called by subclasses that store preferences in 824 * the {@link SharedPreferences}. 825 * 826 * @throws IllegalStateException If there is no key assigned. 827 */ requireKey()828 void requireKey() { 829 if (mKey == null) { 830 throw new IllegalStateException("Preference does not have a key assigned."); 831 } 832 833 mRequiresKey = true; 834 } 835 836 /** 837 * Checks whether this Preference has a valid key. 838 * 839 * @return True if the key exists and is not a blank string, false otherwise. 840 */ hasKey()841 public boolean hasKey() { 842 return !TextUtils.isEmpty(mKey); 843 } 844 845 /** 846 * Checks whether this Preference is persistent. If it is, it stores its value(s) into 847 * the persistent {@link SharedPreferences} storage. 848 * 849 * @return True if it is persistent. 850 */ isPersistent()851 public boolean isPersistent() { 852 return mPersistent; 853 } 854 855 /** 856 * Checks whether, at the given time this method is called, 857 * this Preference should store/restore its value(s) into the 858 * {@link SharedPreferences}. This, at minimum, checks whether this 859 * Preference is persistent and it currently has a key. Before you 860 * save/restore from the {@link SharedPreferences}, check this first. 861 * 862 * @return True if it should persist the value. 863 */ shouldPersist()864 protected boolean shouldPersist() { 865 return mPreferenceManager != null && isPersistent() && hasKey(); 866 } 867 868 /** 869 * Sets whether this Preference is persistent. When persistent, 870 * it stores its value(s) into the persistent {@link SharedPreferences} 871 * storage. 872 * 873 * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}. 874 */ setPersistent(boolean persistent)875 public void setPersistent(boolean persistent) { 876 mPersistent = persistent; 877 } 878 879 /** 880 * Call this method after the user changes the preference, but before the 881 * internal state is set. This allows the client to ignore the user value. 882 * 883 * @param newValue The new value of this Preference. 884 * @return True if the user value should be set as the preference 885 * value (and persisted). 886 */ callChangeListener(Object newValue)887 protected boolean callChangeListener(Object newValue) { 888 return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue); 889 } 890 891 /** 892 * Sets the callback to be invoked when this Preference is changed by the 893 * user (but before the internal state has been updated). 894 * 895 * @param onPreferenceChangeListener The callback to be invoked. 896 */ setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener)897 public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) { 898 mOnChangeListener = onPreferenceChangeListener; 899 } 900 901 /** 902 * Returns the callback to be invoked when this Preference is changed by the 903 * user (but before the internal state has been updated). 904 * 905 * @return The callback to be invoked. 906 */ getOnPreferenceChangeListener()907 public OnPreferenceChangeListener getOnPreferenceChangeListener() { 908 return mOnChangeListener; 909 } 910 911 /** 912 * Sets the callback to be invoked when this Preference is clicked. 913 * 914 * @param onPreferenceClickListener The callback to be invoked. 915 */ setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener)916 public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) { 917 mOnClickListener = onPreferenceClickListener; 918 } 919 920 /** 921 * Returns the callback to be invoked when this Preference is clicked. 922 * 923 * @return The callback to be invoked. 924 */ getOnPreferenceClickListener()925 public OnPreferenceClickListener getOnPreferenceClickListener() { 926 return mOnClickListener; 927 } 928 929 /** 930 * Called when a click should be performed. 931 * 932 * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click 933 * listener should be called in the proper order (between other 934 * processing). May be null. 935 */ performClick(PreferenceScreen preferenceScreen)936 void performClick(PreferenceScreen preferenceScreen) { 937 938 if (!isEnabled()) { 939 return; 940 } 941 942 onClick(); 943 944 if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) { 945 return; 946 } 947 948 PreferenceManager preferenceManager = getPreferenceManager(); 949 if (preferenceManager != null) { 950 PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager 951 .getOnPreferenceTreeClickListener(); 952 if (preferenceScreen != null && listener != null 953 && listener.onPreferenceTreeClick(preferenceScreen, this)) { 954 return; 955 } 956 } 957 958 if (mIntent != null) { 959 Context context = getContext(); 960 context.startActivity(mIntent); 961 } 962 } 963 964 /** 965 * Allows a Preference to intercept key events without having focus. 966 * For example, SeekBarPreference uses this to intercept +/- to adjust 967 * the progress. 968 * @return True if the Preference handled the key. Returns false by default. 969 * @hide 970 */ onKey(View v, int keyCode, KeyEvent event)971 public boolean onKey(View v, int keyCode, KeyEvent event) { 972 return false; 973 } 974 975 /** 976 * Returns the {@link android.content.Context} of this Preference. 977 * Each Preference in a Preference hierarchy can be 978 * from different Context (for example, if multiple activities provide preferences into a single 979 * {@link PreferenceActivity}). This Context will be used to save the Preference values. 980 * 981 * @return The Context of this Preference. 982 */ getContext()983 public Context getContext() { 984 return mContext; 985 } 986 987 /** 988 * Returns the {@link SharedPreferences} where this Preference can read its 989 * value(s). Usually, it's easier to use one of the helper read methods: 990 * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)}, 991 * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)}, 992 * {@link #getPersistedString(String)}. To save values, see 993 * {@link #getEditor()}. 994 * <p> 995 * In some cases, writes to the {@link #getEditor()} will not be committed 996 * right away and hence not show up in the returned 997 * {@link SharedPreferences}, this is intended behavior to improve 998 * performance. 999 * 1000 * @return The {@link SharedPreferences} where this Preference reads its 1001 * value(s), or null if it isn't attached to a Preference hierarchy. 1002 * @see #getEditor() 1003 */ getSharedPreferences()1004 public SharedPreferences getSharedPreferences() { 1005 if (mPreferenceManager == null) { 1006 return null; 1007 } 1008 1009 return mPreferenceManager.getSharedPreferences(); 1010 } 1011 1012 /** 1013 * Returns an {@link SharedPreferences.Editor} where this Preference can 1014 * save its value(s). Usually it's easier to use one of the helper save 1015 * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)}, 1016 * {@link #persistInt(int)}, {@link #persistLong(long)}, 1017 * {@link #persistString(String)}. To read values, see 1018 * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns 1019 * true, it is this Preference's responsibility to commit. 1020 * <p> 1021 * In some cases, writes to this will not be committed right away and hence 1022 * not show up in the SharedPreferences, this is intended behavior to 1023 * improve performance. 1024 * 1025 * @return A {@link SharedPreferences.Editor} where this preference saves 1026 * its value(s), or null if it isn't attached to a Preference 1027 * hierarchy. 1028 * @see #shouldCommit() 1029 * @see #getSharedPreferences() 1030 */ getEditor()1031 public SharedPreferences.Editor getEditor() { 1032 if (mPreferenceManager == null) { 1033 return null; 1034 } 1035 1036 return mPreferenceManager.getEditor(); 1037 } 1038 1039 /** 1040 * Returns whether the {@link Preference} should commit its saved value(s) in 1041 * {@link #getEditor()}. This may return false in situations where batch 1042 * committing is being done (by the manager) to improve performance. 1043 * 1044 * @return Whether the Preference should commit its saved value(s). 1045 * @see #getEditor() 1046 */ shouldCommit()1047 public boolean shouldCommit() { 1048 if (mPreferenceManager == null) { 1049 return false; 1050 } 1051 1052 return mPreferenceManager.shouldCommit(); 1053 } 1054 1055 /** 1056 * Compares Preference objects based on order (if set), otherwise alphabetically on the titles. 1057 * 1058 * @param another The Preference to compare to this one. 1059 * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>; 1060 * greater than 0 if this Preference sorts after <var>another</var>. 1061 */ compareTo(Preference another)1062 public int compareTo(Preference another) { 1063 if (mOrder != DEFAULT_ORDER 1064 || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) { 1065 // Do order comparison 1066 return mOrder - another.mOrder; 1067 } else if (mTitle == null) { 1068 return 1; 1069 } else if (another.mTitle == null) { 1070 return -1; 1071 } else { 1072 // Do name comparison 1073 return CharSequences.compareToIgnoreCase(mTitle, another.mTitle); 1074 } 1075 } 1076 1077 /** 1078 * Sets the internal change listener. 1079 * 1080 * @param listener The listener. 1081 * @see #notifyChanged() 1082 */ setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener)1083 final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) { 1084 mListener = listener; 1085 } 1086 1087 /** 1088 * Should be called when the data of this {@link Preference} has changed. 1089 */ notifyChanged()1090 protected void notifyChanged() { 1091 if (mListener != null) { 1092 mListener.onPreferenceChange(this); 1093 } 1094 } 1095 1096 /** 1097 * Should be called when a Preference has been 1098 * added/removed from this group, or the ordering should be 1099 * re-evaluated. 1100 */ notifyHierarchyChanged()1101 protected void notifyHierarchyChanged() { 1102 if (mListener != null) { 1103 mListener.onPreferenceHierarchyChange(this); 1104 } 1105 } 1106 1107 /** 1108 * Gets the {@link PreferenceManager} that manages this Preference object's tree. 1109 * 1110 * @return The {@link PreferenceManager}. 1111 */ getPreferenceManager()1112 public PreferenceManager getPreferenceManager() { 1113 return mPreferenceManager; 1114 } 1115 1116 /** 1117 * Called when this Preference has been attached to a Preference hierarchy. 1118 * Make sure to call the super implementation. 1119 * 1120 * @param preferenceManager The PreferenceManager of the hierarchy. 1121 */ onAttachedToHierarchy(PreferenceManager preferenceManager)1122 protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { 1123 mPreferenceManager = preferenceManager; 1124 1125 mId = preferenceManager.getNextId(); 1126 1127 dispatchSetInitialValue(); 1128 } 1129 1130 /** 1131 * Called when the Preference hierarchy has been attached to the 1132 * {@link PreferenceActivity}. This can also be called when this 1133 * Preference has been attached to a group that was already attached 1134 * to the {@link PreferenceActivity}. 1135 */ onAttachedToActivity()1136 protected void onAttachedToActivity() { 1137 // At this point, the hierarchy that this preference is in is connected 1138 // with all other preferences. 1139 registerDependency(); 1140 } 1141 registerDependency()1142 private void registerDependency() { 1143 1144 if (TextUtils.isEmpty(mDependencyKey)) return; 1145 1146 Preference preference = findPreferenceInHierarchy(mDependencyKey); 1147 if (preference != null) { 1148 preference.registerDependent(this); 1149 } else { 1150 throw new IllegalStateException("Dependency \"" + mDependencyKey 1151 + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\""); 1152 } 1153 } 1154 unregisterDependency()1155 private void unregisterDependency() { 1156 if (mDependencyKey != null) { 1157 final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey); 1158 if (oldDependency != null) { 1159 oldDependency.unregisterDependent(this); 1160 } 1161 } 1162 } 1163 1164 /** 1165 * Finds a Preference in this hierarchy (the whole thing, 1166 * even above/below your {@link PreferenceScreen} screen break) with the given 1167 * key. 1168 * <p> 1169 * This only functions after we have been attached to a hierarchy. 1170 * 1171 * @param key The key of the Preference to find. 1172 * @return The Preference that uses the given key. 1173 */ findPreferenceInHierarchy(String key)1174 protected Preference findPreferenceInHierarchy(String key) { 1175 if (TextUtils.isEmpty(key) || mPreferenceManager == null) { 1176 return null; 1177 } 1178 1179 return mPreferenceManager.findPreference(key); 1180 } 1181 1182 /** 1183 * Adds a dependent Preference on this Preference so we can notify it. 1184 * Usually, the dependent Preference registers itself (it's good for it to 1185 * know it depends on something), so please use 1186 * {@link Preference#setDependency(String)} on the dependent Preference. 1187 * 1188 * @param dependent The dependent Preference that will be enabled/disabled 1189 * according to the state of this Preference. 1190 */ registerDependent(Preference dependent)1191 private void registerDependent(Preference dependent) { 1192 if (mDependents == null) { 1193 mDependents = new ArrayList<Preference>(); 1194 } 1195 1196 mDependents.add(dependent); 1197 1198 dependent.onDependencyChanged(this, shouldDisableDependents()); 1199 } 1200 1201 /** 1202 * Removes a dependent Preference on this Preference. 1203 * 1204 * @param dependent The dependent Preference that will be enabled/disabled 1205 * according to the state of this Preference. 1206 * @return Returns the same Preference object, for chaining multiple calls 1207 * into a single statement. 1208 */ unregisterDependent(Preference dependent)1209 private void unregisterDependent(Preference dependent) { 1210 if (mDependents != null) { 1211 mDependents.remove(dependent); 1212 } 1213 } 1214 1215 /** 1216 * Notifies any listening dependents of a change that affects the 1217 * dependency. 1218 * 1219 * @param disableDependents Whether this Preference should disable 1220 * its dependents. 1221 */ notifyDependencyChange(boolean disableDependents)1222 public void notifyDependencyChange(boolean disableDependents) { 1223 final List<Preference> dependents = mDependents; 1224 1225 if (dependents == null) { 1226 return; 1227 } 1228 1229 final int dependentsCount = dependents.size(); 1230 for (int i = 0; i < dependentsCount; i++) { 1231 dependents.get(i).onDependencyChanged(this, disableDependents); 1232 } 1233 } 1234 1235 /** 1236 * Called when the dependency changes. 1237 * 1238 * @param dependency The Preference that this Preference depends on. 1239 * @param disableDependent Set true to disable this Preference. 1240 */ onDependencyChanged(Preference dependency, boolean disableDependent)1241 public void onDependencyChanged(Preference dependency, boolean disableDependent) { 1242 if (mDependencyMet == disableDependent) { 1243 mDependencyMet = !disableDependent; 1244 1245 // Enabled state can change dependent preferences' states, so notify 1246 notifyDependencyChange(shouldDisableDependents()); 1247 1248 notifyChanged(); 1249 } 1250 } 1251 1252 /** 1253 * Checks whether this preference's dependents should currently be 1254 * disabled. 1255 * 1256 * @return True if the dependents should be disabled, otherwise false. 1257 */ shouldDisableDependents()1258 public boolean shouldDisableDependents() { 1259 return !isEnabled(); 1260 } 1261 1262 /** 1263 * Sets the key of a Preference that this Preference will depend on. If that 1264 * Preference is not set or is off, this Preference will be disabled. 1265 * 1266 * @param dependencyKey The key of the Preference that this depends on. 1267 */ setDependency(String dependencyKey)1268 public void setDependency(String dependencyKey) { 1269 // Unregister the old dependency, if we had one 1270 unregisterDependency(); 1271 1272 // Register the new 1273 mDependencyKey = dependencyKey; 1274 registerDependency(); 1275 } 1276 1277 /** 1278 * Returns the key of the dependency on this Preference. 1279 * 1280 * @return The key of the dependency. 1281 * @see #setDependency(String) 1282 */ getDependency()1283 public String getDependency() { 1284 return mDependencyKey; 1285 } 1286 1287 /** 1288 * Called when this Preference is being removed from the hierarchy. You 1289 * should remove any references to this Preference that you know about. Make 1290 * sure to call through to the superclass implementation. 1291 */ onPrepareForRemoval()1292 protected void onPrepareForRemoval() { 1293 unregisterDependency(); 1294 } 1295 1296 /** 1297 * Sets the default value for this Preference, which will be set either if 1298 * persistence is off or persistence is on and the preference is not found 1299 * in the persistent storage. 1300 * 1301 * @param defaultValue The default value. 1302 */ setDefaultValue(Object defaultValue)1303 public void setDefaultValue(Object defaultValue) { 1304 mDefaultValue = defaultValue; 1305 } 1306 dispatchSetInitialValue()1307 private void dispatchSetInitialValue() { 1308 // By now, we know if we are persistent. 1309 final boolean shouldPersist = shouldPersist(); 1310 if (!shouldPersist || !getSharedPreferences().contains(mKey)) { 1311 if (mDefaultValue != null) { 1312 onSetInitialValue(false, mDefaultValue); 1313 } 1314 } else { 1315 onSetInitialValue(true, null); 1316 } 1317 } 1318 1319 /** 1320 * Implement this to set the initial value of the Preference. 1321 * <p> 1322 * If <var>restorePersistedValue</var> is true, you should restore the 1323 * Preference value from the {@link android.content.SharedPreferences}. If 1324 * <var>restorePersistedValue</var> is false, you should set the Preference 1325 * value to defaultValue that is given (and possibly store to SharedPreferences 1326 * if {@link #shouldPersist()} is true). 1327 * <p> 1328 * This may not always be called. One example is if it should not persist 1329 * but there is no default value given. 1330 * 1331 * @param restorePersistedValue True to restore the persisted value; 1332 * false to use the given <var>defaultValue</var>. 1333 * @param defaultValue The default value for this Preference. Only use this 1334 * if <var>restorePersistedValue</var> is false. 1335 */ onSetInitialValue(boolean restorePersistedValue, Object defaultValue)1336 protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 1337 } 1338 tryCommit(SharedPreferences.Editor editor)1339 private void tryCommit(SharedPreferences.Editor editor) { 1340 if (mPreferenceManager.shouldCommit()) { 1341 try { 1342 editor.apply(); 1343 } catch (AbstractMethodError unused) { 1344 // The app injected its own pre-Gingerbread 1345 // SharedPreferences.Editor implementation without 1346 // an apply method. 1347 editor.commit(); 1348 } 1349 } 1350 } 1351 1352 /** 1353 * Attempts to persist a String to the {@link android.content.SharedPreferences}. 1354 * <p> 1355 * This will check if this Preference is persistent, get an editor from 1356 * the {@link PreferenceManager}, put in the string, and check if we should commit (and 1357 * commit if so). 1358 * 1359 * @param value The value to persist. 1360 * @return True if the Preference is persistent. (This is not whether the 1361 * value was persisted, since we may not necessarily commit if there 1362 * will be a batch commit later.) 1363 * @see #getPersistedString(String) 1364 */ persistString(String value)1365 protected boolean persistString(String value) { 1366 if (shouldPersist()) { 1367 // Shouldn't store null 1368 if (value == getPersistedString(null)) { 1369 // It's already there, so the same as persisting 1370 return true; 1371 } 1372 1373 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1374 editor.putString(mKey, value); 1375 tryCommit(editor); 1376 return true; 1377 } 1378 return false; 1379 } 1380 1381 /** 1382 * Attempts to get a persisted String from the {@link android.content.SharedPreferences}. 1383 * <p> 1384 * This will check if this Preference is persistent, get the SharedPreferences 1385 * from the {@link PreferenceManager}, and get the value. 1386 * 1387 * @param defaultReturnValue The default value to return if either the 1388 * Preference is not persistent or the Preference is not in the 1389 * shared preferences. 1390 * @return The value from the SharedPreferences or the default return 1391 * value. 1392 * @see #persistString(String) 1393 */ getPersistedString(String defaultReturnValue)1394 protected String getPersistedString(String defaultReturnValue) { 1395 if (!shouldPersist()) { 1396 return defaultReturnValue; 1397 } 1398 1399 return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue); 1400 } 1401 1402 /** 1403 * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}. 1404 * <p> 1405 * This will check if this Preference is persistent, get an editor from 1406 * the {@link PreferenceManager}, put in the strings, and check if we should commit (and 1407 * commit if so). 1408 * 1409 * @param values The values to persist. 1410 * @return True if the Preference is persistent. (This is not whether the 1411 * value was persisted, since we may not necessarily commit if there 1412 * will be a batch commit later.) 1413 * @see #getPersistedString(Set) 1414 * 1415 * @hide Pending API approval 1416 */ persistStringSet(Set<String> values)1417 protected boolean persistStringSet(Set<String> values) { 1418 if (shouldPersist()) { 1419 // Shouldn't store null 1420 if (values.equals(getPersistedStringSet(null))) { 1421 // It's already there, so the same as persisting 1422 return true; 1423 } 1424 1425 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1426 editor.putStringSet(mKey, values); 1427 tryCommit(editor); 1428 return true; 1429 } 1430 return false; 1431 } 1432 1433 /** 1434 * Attempts to get a persisted set of Strings from the 1435 * {@link android.content.SharedPreferences}. 1436 * <p> 1437 * This will check if this Preference is persistent, get the SharedPreferences 1438 * from the {@link PreferenceManager}, and get the value. 1439 * 1440 * @param defaultReturnValue The default value to return if either the 1441 * Preference is not persistent or the Preference is not in the 1442 * shared preferences. 1443 * @return The value from the SharedPreferences or the default return 1444 * value. 1445 * @see #persistStringSet(Set) 1446 * 1447 * @hide Pending API approval 1448 */ getPersistedStringSet(Set<String> defaultReturnValue)1449 protected Set<String> getPersistedStringSet(Set<String> defaultReturnValue) { 1450 if (!shouldPersist()) { 1451 return defaultReturnValue; 1452 } 1453 1454 return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue); 1455 } 1456 1457 /** 1458 * Attempts to persist an int to the {@link android.content.SharedPreferences}. 1459 * 1460 * @param value The value to persist. 1461 * @return True if the Preference is persistent. (This is not whether the 1462 * value was persisted, since we may not necessarily commit if there 1463 * will be a batch commit later.) 1464 * @see #persistString(String) 1465 * @see #getPersistedInt(int) 1466 */ persistInt(int value)1467 protected boolean persistInt(int value) { 1468 if (shouldPersist()) { 1469 if (value == getPersistedInt(~value)) { 1470 // It's already there, so the same as persisting 1471 return true; 1472 } 1473 1474 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1475 editor.putInt(mKey, value); 1476 tryCommit(editor); 1477 return true; 1478 } 1479 return false; 1480 } 1481 1482 /** 1483 * Attempts to get a persisted int from the {@link android.content.SharedPreferences}. 1484 * 1485 * @param defaultReturnValue The default value to return if either this 1486 * Preference is not persistent or this Preference is not in the 1487 * SharedPreferences. 1488 * @return The value from the SharedPreferences or the default return 1489 * value. 1490 * @see #getPersistedString(String) 1491 * @see #persistInt(int) 1492 */ getPersistedInt(int defaultReturnValue)1493 protected int getPersistedInt(int defaultReturnValue) { 1494 if (!shouldPersist()) { 1495 return defaultReturnValue; 1496 } 1497 1498 return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue); 1499 } 1500 1501 /** 1502 * Attempts to persist a float to the {@link android.content.SharedPreferences}. 1503 * 1504 * @param value The value to persist. 1505 * @return True if this Preference is persistent. (This is not whether the 1506 * value was persisted, since we may not necessarily commit if there 1507 * will be a batch commit later.) 1508 * @see #persistString(String) 1509 * @see #getPersistedFloat(float) 1510 */ persistFloat(float value)1511 protected boolean persistFloat(float value) { 1512 if (shouldPersist()) { 1513 if (value == getPersistedFloat(Float.NaN)) { 1514 // It's already there, so the same as persisting 1515 return true; 1516 } 1517 1518 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1519 editor.putFloat(mKey, value); 1520 tryCommit(editor); 1521 return true; 1522 } 1523 return false; 1524 } 1525 1526 /** 1527 * Attempts to get a persisted float from the {@link android.content.SharedPreferences}. 1528 * 1529 * @param defaultReturnValue The default value to return if either this 1530 * Preference is not persistent or this Preference is not in the 1531 * SharedPreferences. 1532 * @return The value from the SharedPreferences or the default return 1533 * value. 1534 * @see #getPersistedString(String) 1535 * @see #persistFloat(float) 1536 */ getPersistedFloat(float defaultReturnValue)1537 protected float getPersistedFloat(float defaultReturnValue) { 1538 if (!shouldPersist()) { 1539 return defaultReturnValue; 1540 } 1541 1542 return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue); 1543 } 1544 1545 /** 1546 * Attempts to persist a long to the {@link android.content.SharedPreferences}. 1547 * 1548 * @param value The value to persist. 1549 * @return True if this Preference is persistent. (This is not whether the 1550 * value was persisted, since we may not necessarily commit if there 1551 * will be a batch commit later.) 1552 * @see #persistString(String) 1553 * @see #getPersistedLong(long) 1554 */ persistLong(long value)1555 protected boolean persistLong(long value) { 1556 if (shouldPersist()) { 1557 if (value == getPersistedLong(~value)) { 1558 // It's already there, so the same as persisting 1559 return true; 1560 } 1561 1562 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1563 editor.putLong(mKey, value); 1564 tryCommit(editor); 1565 return true; 1566 } 1567 return false; 1568 } 1569 1570 /** 1571 * Attempts to get a persisted long from the {@link android.content.SharedPreferences}. 1572 * 1573 * @param defaultReturnValue The default value to return if either this 1574 * Preference is not persistent or this Preference is not in the 1575 * SharedPreferences. 1576 * @return The value from the SharedPreferences or the default return 1577 * value. 1578 * @see #getPersistedString(String) 1579 * @see #persistLong(long) 1580 */ getPersistedLong(long defaultReturnValue)1581 protected long getPersistedLong(long defaultReturnValue) { 1582 if (!shouldPersist()) { 1583 return defaultReturnValue; 1584 } 1585 1586 return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue); 1587 } 1588 1589 /** 1590 * Attempts to persist a boolean to the {@link android.content.SharedPreferences}. 1591 * 1592 * @param value The value to persist. 1593 * @return True if this Preference is persistent. (This is not whether the 1594 * value was persisted, since we may not necessarily commit if there 1595 * will be a batch commit later.) 1596 * @see #persistString(String) 1597 * @see #getPersistedBoolean(boolean) 1598 */ persistBoolean(boolean value)1599 protected boolean persistBoolean(boolean value) { 1600 if (shouldPersist()) { 1601 if (value == getPersistedBoolean(!value)) { 1602 // It's already there, so the same as persisting 1603 return true; 1604 } 1605 1606 SharedPreferences.Editor editor = mPreferenceManager.getEditor(); 1607 editor.putBoolean(mKey, value); 1608 tryCommit(editor); 1609 return true; 1610 } 1611 return false; 1612 } 1613 1614 /** 1615 * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}. 1616 * 1617 * @param defaultReturnValue The default value to return if either this 1618 * Preference is not persistent or this Preference is not in the 1619 * SharedPreferences. 1620 * @return The value from the SharedPreferences or the default return 1621 * value. 1622 * @see #getPersistedString(String) 1623 * @see #persistBoolean(boolean) 1624 */ getPersistedBoolean(boolean defaultReturnValue)1625 protected boolean getPersistedBoolean(boolean defaultReturnValue) { 1626 if (!shouldPersist()) { 1627 return defaultReturnValue; 1628 } 1629 1630 return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue); 1631 } 1632 hasSpecifiedLayout()1633 boolean hasSpecifiedLayout() { 1634 return mHasSpecifiedLayout; 1635 } 1636 1637 @Override toString()1638 public String toString() { 1639 return getFilterableStringBuilder().toString(); 1640 } 1641 1642 /** 1643 * Returns the text that will be used to filter this Preference depending on 1644 * user input. 1645 * <p> 1646 * If overridding and calling through to the superclass, make sure to prepend 1647 * your additions with a space. 1648 * 1649 * @return Text as a {@link StringBuilder} that will be used to filter this 1650 * preference. By default, this is the title and summary 1651 * (concatenated with a space). 1652 */ getFilterableStringBuilder()1653 StringBuilder getFilterableStringBuilder() { 1654 StringBuilder sb = new StringBuilder(); 1655 CharSequence title = getTitle(); 1656 if (!TextUtils.isEmpty(title)) { 1657 sb.append(title).append(' '); 1658 } 1659 CharSequence summary = getSummary(); 1660 if (!TextUtils.isEmpty(summary)) { 1661 sb.append(summary).append(' '); 1662 } 1663 if (sb.length() > 0) { 1664 // Drop the last space 1665 sb.setLength(sb.length() - 1); 1666 } 1667 return sb; 1668 } 1669 1670 /** 1671 * Store this Preference hierarchy's frozen state into the given container. 1672 * 1673 * @param container The Bundle in which to save the instance of this Preference. 1674 * 1675 * @see #restoreHierarchyState 1676 * @see #onSaveInstanceState 1677 */ saveHierarchyState(Bundle container)1678 public void saveHierarchyState(Bundle container) { 1679 dispatchSaveInstanceState(container); 1680 } 1681 1682 /** 1683 * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children. 1684 * May be overridden to modify how the save happens for children. For example, some 1685 * Preference objects may want to not store an instance for their children. 1686 * 1687 * @param container The Bundle in which to save the instance of this Preference. 1688 * 1689 * @see #saveHierarchyState 1690 * @see #onSaveInstanceState 1691 */ dispatchSaveInstanceState(Bundle container)1692 void dispatchSaveInstanceState(Bundle container) { 1693 if (hasKey()) { 1694 mBaseMethodCalled = false; 1695 Parcelable state = onSaveInstanceState(); 1696 if (!mBaseMethodCalled) { 1697 throw new IllegalStateException( 1698 "Derived class did not call super.onSaveInstanceState()"); 1699 } 1700 if (state != null) { 1701 container.putParcelable(mKey, state); 1702 } 1703 } 1704 } 1705 1706 /** 1707 * Hook allowing a Preference to generate a representation of its internal 1708 * state that can later be used to create a new instance with that same 1709 * state. This state should only contain information that is not persistent 1710 * or can be reconstructed later. 1711 * 1712 * @return A Parcelable object containing the current dynamic state of 1713 * this Preference, or null if there is nothing interesting to save. 1714 * The default implementation returns null. 1715 * @see #onRestoreInstanceState 1716 * @see #saveHierarchyState 1717 */ onSaveInstanceState()1718 protected Parcelable onSaveInstanceState() { 1719 mBaseMethodCalled = true; 1720 return BaseSavedState.EMPTY_STATE; 1721 } 1722 1723 /** 1724 * Restore this Preference hierarchy's previously saved state from the given container. 1725 * 1726 * @param container The Bundle that holds the previously saved state. 1727 * 1728 * @see #saveHierarchyState 1729 * @see #onRestoreInstanceState 1730 */ restoreHierarchyState(Bundle container)1731 public void restoreHierarchyState(Bundle container) { 1732 dispatchRestoreInstanceState(container); 1733 } 1734 1735 /** 1736 * Called by {@link #restoreHierarchyState} to retrieve the saved state for this 1737 * Preference and its children. May be overridden to modify how restoring 1738 * happens to the children of a Preference. For example, some Preference objects may 1739 * not want to save state for their children. 1740 * 1741 * @param container The Bundle that holds the previously saved state. 1742 * @see #restoreHierarchyState 1743 * @see #onRestoreInstanceState 1744 */ dispatchRestoreInstanceState(Bundle container)1745 void dispatchRestoreInstanceState(Bundle container) { 1746 if (hasKey()) { 1747 Parcelable state = container.getParcelable(mKey); 1748 if (state != null) { 1749 mBaseMethodCalled = false; 1750 onRestoreInstanceState(state); 1751 if (!mBaseMethodCalled) { 1752 throw new IllegalStateException( 1753 "Derived class did not call super.onRestoreInstanceState()"); 1754 } 1755 } 1756 } 1757 } 1758 1759 /** 1760 * Hook allowing a Preference to re-apply a representation of its internal 1761 * state that had previously been generated by {@link #onSaveInstanceState}. 1762 * This function will never be called with a null state. 1763 * 1764 * @param state The saved state that had previously been returned by 1765 * {@link #onSaveInstanceState}. 1766 * @see #onSaveInstanceState 1767 * @see #restoreHierarchyState 1768 */ onRestoreInstanceState(Parcelable state)1769 protected void onRestoreInstanceState(Parcelable state) { 1770 mBaseMethodCalled = true; 1771 if (state != BaseSavedState.EMPTY_STATE && state != null) { 1772 throw new IllegalArgumentException("Wrong state class -- expecting Preference State"); 1773 } 1774 } 1775 1776 /** 1777 * A base class for managing the instance state of a {@link Preference}. 1778 */ 1779 public static class BaseSavedState extends AbsSavedState { BaseSavedState(Parcel source)1780 public BaseSavedState(Parcel source) { 1781 super(source); 1782 } 1783 BaseSavedState(Parcelable superState)1784 public BaseSavedState(Parcelable superState) { 1785 super(superState); 1786 } 1787 1788 public static final Parcelable.Creator<BaseSavedState> CREATOR = 1789 new Parcelable.Creator<BaseSavedState>() { 1790 public BaseSavedState createFromParcel(Parcel in) { 1791 return new BaseSavedState(in); 1792 } 1793 1794 public BaseSavedState[] newArray(int size) { 1795 return new BaseSavedState[size]; 1796 } 1797 }; 1798 } 1799 1800 } 1801