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