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