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