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