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