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