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