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         mHasSingleLineTitleAttr = true;
938         mSingleLineTitle = singleLineTitle;
939         notifyChanged();
940     }
941 
942     /**
943      * Gets whether the title of this preference is constrained to a single line.
944      *
945      * @see #setSingleLineTitle(boolean)
946      * @return {@code true} if the title of this preference is constrained to a single line
947      */
isSingleLineTitle()948     public boolean isSingleLineTitle() {
949         return mSingleLineTitle;
950     }
951 
952     /**
953      * Sets whether to reserve the space of this Preference icon view when no icon is provided.
954      *
955      * @param iconSpaceReserved set {@code true} if the space for the icon view should be reserved
956      */
setIconSpaceReserved(boolean iconSpaceReserved)957     public void setIconSpaceReserved(boolean iconSpaceReserved) {
958         mIconSpaceReserved = iconSpaceReserved;
959         notifyChanged();
960     }
961 
962     /**
963      * Gets whether the space this preference icon view is reserved.
964      *
965      * @see #setIconSpaceReserved(boolean)
966      * @return {@code true} if the space of this preference icon view is reserved
967      */
isIconSpaceReserved()968     public boolean isIconSpaceReserved() {
969         return mIconSpaceReserved;
970     }
971     /**
972      * Returns a unique ID for this Preference.  This ID should be unique across all
973      * Preference objects in a hierarchy.
974      *
975      * @return A unique ID for this Preference.
976      */
getId()977     long getId() {
978         return mId;
979     }
980 
981     /**
982      * Processes a click on the preference. This includes saving the value to
983      * the {@link SharedPreferences}. However, the overridden method should
984      * call {@link #callChangeListener(Object)} to make sure the client wants to
985      * update the preference's state with the new value.
986      */
onClick()987     protected void onClick() {
988     }
989 
990     /**
991      * Sets the key for this Preference, which is used as a key to the {@link SharedPreferences} or
992      * {@link PreferenceDataStore}. This should be unique for the package.
993      *
994      * @param key The key for the preference.
995      */
setKey(String key)996     public void setKey(String key) {
997         mKey = key;
998 
999         if (mRequiresKey && !hasKey()) {
1000             requireKey();
1001         }
1002     }
1003 
1004     /**
1005      * Gets the key for this Preference, which is also the key used for storing values into
1006      * {@link SharedPreferences} or {@link PreferenceDataStore}.
1007      *
1008      * @return The key.
1009      */
getKey()1010     public String getKey() {
1011         return mKey;
1012     }
1013 
1014     /**
1015      * Checks whether the key is present, and if it isn't throws an
1016      * exception. This should be called by subclasses that persist their preferences.
1017      *
1018      * @throws IllegalStateException If there is no key assigned.
1019      */
requireKey()1020     void requireKey() {
1021         if (mKey == null) {
1022             throw new IllegalStateException("Preference does not have a key assigned.");
1023         }
1024 
1025         mRequiresKey = true;
1026     }
1027 
1028     /**
1029      * Checks whether this Preference has a valid key.
1030      *
1031      * @return True if the key exists and is not a blank string, false otherwise.
1032      */
hasKey()1033     public boolean hasKey() {
1034         return !TextUtils.isEmpty(mKey);
1035     }
1036 
1037     /**
1038      * Checks whether this Preference is persistent. If it is, it stores its value(s) into
1039      * the persistent {@link SharedPreferences} storage by default or into
1040      * {@link PreferenceDataStore} if assigned.
1041      *
1042      * @return True if it is persistent.
1043      */
isPersistent()1044     public boolean isPersistent() {
1045         return mPersistent;
1046     }
1047 
1048     /**
1049      * Checks whether, at the given time this method is called, this Preference should store/restore
1050      * its value(s) into the {@link SharedPreferences} or into {@link PreferenceDataStore} if
1051      * assigned. This, at minimum, checks whether this Preference is persistent and it currently has
1052      * a key. Before you save/restore from the storage, check this first.
1053      *
1054      * @return True if it should persist the value.
1055      */
shouldPersist()1056     protected boolean shouldPersist() {
1057         return mPreferenceManager != null && isPersistent() && hasKey();
1058     }
1059 
1060     /**
1061      * Sets whether this Preference is persistent. When persistent, it stores its value(s) into
1062      * the persistent {@link SharedPreferences} storage by default or into
1063      * {@link PreferenceDataStore} if assigned.
1064      *
1065      * @param persistent set {@code true} if it should store its value(s) into the storage.
1066      */
setPersistent(boolean persistent)1067     public void setPersistent(boolean persistent) {
1068         mPersistent = persistent;
1069     }
1070 
1071     /**
1072      * Call this method after the user changes the preference, but before the
1073      * internal state is set. This allows the client to ignore the user value.
1074      *
1075      * @param newValue The new value of this Preference.
1076      * @return True if the user value should be set as the preference
1077      *         value (and persisted).
1078      */
callChangeListener(Object newValue)1079     protected boolean callChangeListener(Object newValue) {
1080         return mOnChangeListener == null || mOnChangeListener.onPreferenceChange(this, newValue);
1081     }
1082 
1083     /**
1084      * Sets the callback to be invoked when this Preference is changed by the
1085      * user (but before the internal state has been updated).
1086      *
1087      * @param onPreferenceChangeListener The callback to be invoked.
1088      */
setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener)1089     public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
1090         mOnChangeListener = onPreferenceChangeListener;
1091     }
1092 
1093     /**
1094      * Returns the callback to be invoked when this Preference is changed by the
1095      * user (but before the internal state has been updated).
1096      *
1097      * @return The callback to be invoked.
1098      */
getOnPreferenceChangeListener()1099     public OnPreferenceChangeListener getOnPreferenceChangeListener() {
1100         return mOnChangeListener;
1101     }
1102 
1103     /**
1104      * Sets the callback to be invoked when this Preference is clicked.
1105      *
1106      * @param onPreferenceClickListener The callback to be invoked.
1107      */
setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener)1108     public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
1109         mOnClickListener = onPreferenceClickListener;
1110     }
1111 
1112     /**
1113      * Returns the callback to be invoked when this Preference is clicked.
1114      *
1115      * @return The callback to be invoked.
1116      */
getOnPreferenceClickListener()1117     public OnPreferenceClickListener getOnPreferenceClickListener() {
1118         return mOnClickListener;
1119     }
1120 
1121     /**
1122      * Called when a click should be performed.
1123      *
1124      * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
1125      *            listener should be called in the proper order (between other
1126      *            processing). May be {@code null}.
1127      * @hide
1128      */
performClick(PreferenceScreen preferenceScreen)1129     public void performClick(PreferenceScreen preferenceScreen) {
1130 
1131         if (!isEnabled()) {
1132             return;
1133         }
1134 
1135         onClick();
1136 
1137         if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
1138             return;
1139         }
1140 
1141         PreferenceManager preferenceManager = getPreferenceManager();
1142         if (preferenceManager != null) {
1143             PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
1144                     .getOnPreferenceTreeClickListener();
1145             if (preferenceScreen != null && listener != null
1146                     && listener.onPreferenceTreeClick(preferenceScreen, this)) {
1147                 return;
1148             }
1149         }
1150 
1151         if (mIntent != null) {
1152             Context context = getContext();
1153             context.startActivity(mIntent);
1154         }
1155     }
1156 
1157     /**
1158      * Allows a Preference to intercept key events without having focus.
1159      * For example, SeekBarPreference uses this to intercept +/- to adjust
1160      * the progress.
1161      * @return True if the Preference handled the key. Returns false by default.
1162      * @hide
1163      */
onKey(View v, int keyCode, KeyEvent event)1164     public boolean onKey(View v, int keyCode, KeyEvent event) {
1165         return false;
1166     }
1167 
1168     /**
1169      * Returns the {@link android.content.Context} of this Preference.
1170      * Each Preference in a Preference hierarchy can be
1171      * from different Context (for example, if multiple activities provide preferences into a single
1172      * {@link PreferenceActivity}). This Context will be used to save the Preference values.
1173      *
1174      * @return The Context of this Preference.
1175      */
getContext()1176     public Context getContext() {
1177         return mContext;
1178     }
1179 
1180     /**
1181      * Returns the {@link SharedPreferences} where this Preference can read its
1182      * value(s). Usually, it's easier to use one of the helper read methods:
1183      * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
1184      * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
1185      * {@link #getPersistedString(String)}. To save values, see
1186      * {@link #getEditor()}.
1187      * <p>
1188      * In some cases, writes to the {@link #getEditor()} will not be committed
1189      * right away and hence not show up in the returned
1190      * {@link SharedPreferences}, this is intended behavior to improve
1191      * performance.
1192      *
1193      * @return the {@link SharedPreferences} where this Preference reads its value(s). If
1194      *         this preference isn't attached to a Preference hierarchy or if
1195      *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
1196      * @see #getEditor()
1197      * @see #setPreferenceDataStore(PreferenceDataStore)
1198      */
getSharedPreferences()1199     public SharedPreferences getSharedPreferences() {
1200         if (mPreferenceManager == null || getPreferenceDataStore() != null) {
1201             return null;
1202         }
1203 
1204         return mPreferenceManager.getSharedPreferences();
1205     }
1206 
1207     /**
1208      * Returns an {@link SharedPreferences.Editor} where this Preference can
1209      * save its value(s). Usually it's easier to use one of the helper save
1210      * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)},
1211      * {@link #persistInt(int)}, {@link #persistLong(long)},
1212      * {@link #persistString(String)}. To read values, see
1213      * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns
1214      * true, it is this Preference's responsibility to commit.
1215      * <p>
1216      * In some cases, writes to this will not be committed right away and hence
1217      * not show up in the SharedPreferences, this is intended behavior to
1218      * improve performance.
1219      *
1220      * @return a {@link SharedPreferences.Editor} where this preference saves its value(s). If
1221      *         this preference isn't attached to a Preference hierarchy or if
1222      *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
1223      * @see #shouldCommit()
1224      * @see #getSharedPreferences()
1225      * @see #setPreferenceDataStore(PreferenceDataStore)
1226      */
getEditor()1227     public SharedPreferences.Editor getEditor() {
1228         if (mPreferenceManager == null || getPreferenceDataStore() != null) {
1229             return null;
1230         }
1231 
1232         return mPreferenceManager.getEditor();
1233     }
1234 
1235     /**
1236      * Returns whether the {@link Preference} should commit its saved value(s) in
1237      * {@link #getEditor()}. This may return false in situations where batch
1238      * committing is being done (by the manager) to improve performance.
1239      *
1240      * <p>If this preference is using {@link PreferenceDataStore} this value is irrelevant.
1241      *
1242      * @return Whether the Preference should commit its saved value(s).
1243      * @see #getEditor()
1244      */
shouldCommit()1245     public boolean shouldCommit() {
1246         if (mPreferenceManager == null) {
1247             return false;
1248         }
1249 
1250         return mPreferenceManager.shouldCommit();
1251     }
1252 
1253     /**
1254      * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
1255      *
1256      * @param another The Preference to compare to this one.
1257      * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
1258      *          greater than 0 if this Preference sorts after <var>another</var>.
1259      */
1260     @Override
compareTo(Preference another)1261     public int compareTo(Preference another) {
1262         if (mOrder != another.mOrder) {
1263             // Do order comparison
1264             return mOrder - another.mOrder;
1265         } else if (mTitle == another.mTitle) {
1266             // If titles are null or share same object comparison
1267             return 0;
1268         } else if (mTitle == null) {
1269             return 1;
1270         } else if (another.mTitle == null) {
1271             return -1;
1272         } else {
1273             // Do name comparison
1274             return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
1275         }
1276     }
1277 
1278     /**
1279      * Sets the internal change listener.
1280      *
1281      * @param listener The listener.
1282      * @see #notifyChanged()
1283      */
setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener)1284     final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
1285         mListener = listener;
1286     }
1287 
1288     /**
1289      * Should be called when the data of this {@link Preference} has changed.
1290      */
notifyChanged()1291     protected void notifyChanged() {
1292         if (mListener != null) {
1293             mListener.onPreferenceChange(this);
1294         }
1295     }
1296 
1297     /**
1298      * Should be called when a Preference has been
1299      * added/removed from this group, or the ordering should be
1300      * re-evaluated.
1301      */
notifyHierarchyChanged()1302     protected void notifyHierarchyChanged() {
1303         if (mListener != null) {
1304             mListener.onPreferenceHierarchyChange(this);
1305         }
1306     }
1307 
1308     /**
1309      * Gets the {@link PreferenceManager} that manages this Preference object's tree.
1310      *
1311      * @return The {@link PreferenceManager}.
1312      */
getPreferenceManager()1313     public PreferenceManager getPreferenceManager() {
1314         return mPreferenceManager;
1315     }
1316 
1317     /**
1318      * Called when this Preference has been attached to a Preference hierarchy.
1319      * Make sure to call the super implementation.
1320      *
1321      * @param preferenceManager The PreferenceManager of the hierarchy.
1322      */
onAttachedToHierarchy(PreferenceManager preferenceManager)1323     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
1324         mPreferenceManager = preferenceManager;
1325 
1326         mId = preferenceManager.getNextId();
1327 
1328         dispatchSetInitialValue();
1329     }
1330 
1331     /**
1332      * Called when the Preference hierarchy has been attached to the
1333      * {@link PreferenceActivity}. This can also be called when this
1334      * Preference has been attached to a group that was already attached
1335      * to the {@link PreferenceActivity}.
1336      */
onAttachedToActivity()1337     protected void onAttachedToActivity() {
1338         // At this point, the hierarchy that this preference is in is connected
1339         // with all other preferences.
1340         registerDependency();
1341     }
1342 
1343     /**
1344      * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set {@code null} to
1345      * remove the current parent.
1346      *
1347      * @param parentGroup Parent preference group of this Preference or {@code null} if none.
1348      */
assignParent(@ullable PreferenceGroup parentGroup)1349     void assignParent(@Nullable PreferenceGroup parentGroup) {
1350         mParentGroup = parentGroup;
1351     }
1352 
registerDependency()1353     private void registerDependency() {
1354 
1355         if (TextUtils.isEmpty(mDependencyKey)) return;
1356 
1357         Preference preference = findPreferenceInHierarchy(mDependencyKey);
1358         if (preference != null) {
1359             preference.registerDependent(this);
1360         } else {
1361             throw new IllegalStateException("Dependency \"" + mDependencyKey
1362                     + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
1363         }
1364     }
1365 
unregisterDependency()1366     private void unregisterDependency() {
1367         if (mDependencyKey != null) {
1368             final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
1369             if (oldDependency != null) {
1370                 oldDependency.unregisterDependent(this);
1371             }
1372         }
1373     }
1374 
1375     /**
1376      * Finds a Preference in this hierarchy (the whole thing,
1377      * even above/below your {@link PreferenceScreen} screen break) with the given
1378      * key.
1379      * <p>
1380      * This only functions after we have been attached to a hierarchy.
1381      *
1382      * @param key The key of the Preference to find.
1383      * @return The Preference that uses the given key.
1384      */
findPreferenceInHierarchy(String key)1385     protected Preference findPreferenceInHierarchy(String key) {
1386         if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
1387             return null;
1388         }
1389 
1390         return mPreferenceManager.findPreference(key);
1391     }
1392 
1393     /**
1394      * Adds a dependent Preference on this Preference so we can notify it.
1395      * Usually, the dependent Preference registers itself (it's good for it to
1396      * know it depends on something), so please use
1397      * {@link Preference#setDependency(String)} on the dependent Preference.
1398      *
1399      * @param dependent The dependent Preference that will be enabled/disabled
1400      *            according to the state of this Preference.
1401      */
registerDependent(Preference dependent)1402     private void registerDependent(Preference dependent) {
1403         if (mDependents == null) {
1404             mDependents = new ArrayList<Preference>();
1405         }
1406 
1407         mDependents.add(dependent);
1408 
1409         dependent.onDependencyChanged(this, shouldDisableDependents());
1410     }
1411 
1412     /**
1413      * Removes a dependent Preference on this Preference.
1414      *
1415      * @param dependent The dependent Preference that will be enabled/disabled
1416      *            according to the state of this Preference.
1417      * @return Returns the same Preference object, for chaining multiple calls
1418      *         into a single statement.
1419      */
unregisterDependent(Preference dependent)1420     private void unregisterDependent(Preference dependent) {
1421         if (mDependents != null) {
1422             mDependents.remove(dependent);
1423         }
1424     }
1425 
1426     /**
1427      * Notifies any listening dependents of a change that affects the
1428      * dependency.
1429      *
1430      * @param disableDependents Whether this Preference should disable
1431      *            its dependents.
1432      */
notifyDependencyChange(boolean disableDependents)1433     public void notifyDependencyChange(boolean disableDependents) {
1434         final List<Preference> dependents = mDependents;
1435 
1436         if (dependents == null) {
1437             return;
1438         }
1439 
1440         final int dependentsCount = dependents.size();
1441         for (int i = 0; i < dependentsCount; i++) {
1442             dependents.get(i).onDependencyChanged(this, disableDependents);
1443         }
1444     }
1445 
1446     /**
1447      * Called when the dependency changes.
1448      *
1449      * @param dependency The Preference that this Preference depends on.
1450      * @param disableDependent Set true to disable this Preference.
1451      */
onDependencyChanged(Preference dependency, boolean disableDependent)1452     public void onDependencyChanged(Preference dependency, boolean disableDependent) {
1453         if (mDependencyMet == disableDependent) {
1454             mDependencyMet = !disableDependent;
1455 
1456             // Enabled state can change dependent preferences' states, so notify
1457             notifyDependencyChange(shouldDisableDependents());
1458 
1459             notifyChanged();
1460         }
1461     }
1462 
1463     /**
1464      * Called when the implicit parent dependency changes.
1465      *
1466      * @param parent The Preference that this Preference depends on.
1467      * @param disableChild Set true to disable this Preference.
1468      */
onParentChanged(Preference parent, boolean disableChild)1469     public void onParentChanged(Preference parent, boolean disableChild) {
1470         if (mParentDependencyMet == disableChild) {
1471             mParentDependencyMet = !disableChild;
1472 
1473             // Enabled state can change dependent preferences' states, so notify
1474             notifyDependencyChange(shouldDisableDependents());
1475 
1476             notifyChanged();
1477         }
1478     }
1479 
1480     /**
1481      * Checks whether this preference's dependents should currently be
1482      * disabled.
1483      *
1484      * @return True if the dependents should be disabled, otherwise false.
1485      */
shouldDisableDependents()1486     public boolean shouldDisableDependents() {
1487         return !isEnabled();
1488     }
1489 
1490     /**
1491      * Sets the key of a Preference that this Preference will depend on. If that
1492      * Preference is not set or is off, this Preference will be disabled.
1493      *
1494      * @param dependencyKey The key of the Preference that this depends on.
1495      */
setDependency(String dependencyKey)1496     public void setDependency(String dependencyKey) {
1497         // Unregister the old dependency, if we had one
1498         unregisterDependency();
1499 
1500         // Register the new
1501         mDependencyKey = dependencyKey;
1502         registerDependency();
1503     }
1504 
1505     /**
1506      * Returns the key of the dependency on this Preference.
1507      *
1508      * @return The key of the dependency.
1509      * @see #setDependency(String)
1510      */
getDependency()1511     public String getDependency() {
1512         return mDependencyKey;
1513     }
1514 
1515     /**
1516      * Returns the {@link PreferenceGroup} which is this Preference assigned to or {@code null} if
1517      * this preference is not assigned to any group or is a root Preference.
1518      *
1519      * @return the parent PreferenceGroup or {@code null} if not attached to any
1520      */
1521     @Nullable
getParent()1522     public PreferenceGroup getParent() {
1523         return mParentGroup;
1524     }
1525 
1526     /**
1527      * Called when this Preference is being removed from the hierarchy. You
1528      * should remove any references to this Preference that you know about. Make
1529      * sure to call through to the superclass implementation.
1530      */
1531     @CallSuper
onPrepareForRemoval()1532     protected void onPrepareForRemoval() {
1533         unregisterDependency();
1534     }
1535 
1536     /**
1537      * Sets the default value for this Preference, which will be set either if
1538      * persistence is off or persistence is on and the preference is not found
1539      * in the persistent storage.
1540      *
1541      * @param defaultValue The default value.
1542      */
setDefaultValue(Object defaultValue)1543     public void setDefaultValue(Object defaultValue) {
1544         mDefaultValue = defaultValue;
1545     }
1546 
dispatchSetInitialValue()1547     private void dispatchSetInitialValue() {
1548         if (getPreferenceDataStore() != null) {
1549             onSetInitialValue(true, mDefaultValue);
1550             return;
1551         }
1552 
1553         // By now, we know if we are persistent.
1554         final boolean shouldPersist = shouldPersist();
1555         if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
1556             if (mDefaultValue != null) {
1557                 onSetInitialValue(false, mDefaultValue);
1558             }
1559         } else {
1560             onSetInitialValue(true, null);
1561         }
1562     }
1563 
1564     /**
1565      * Implement this to set the initial value of the Preference.
1566      *
1567      * <p>If <var>restorePersistedValue</var> is true, you should restore the
1568      * Preference value from the {@link android.content.SharedPreferences}. If
1569      * <var>restorePersistedValue</var> is false, you should set the Preference
1570      * value to defaultValue that is given (and possibly store to SharedPreferences
1571      * if {@link #shouldPersist()} is true).
1572      *
1573      * <p>In case of using {@link PreferenceDataStore}, the <var>restorePersistedValue</var> is
1574      * always {@code true}. But the default value (if provided) is set.
1575      *
1576      * <p>This may not always be called. One example is if it should not persist
1577      * but there is no default value given.
1578      *
1579      * @param restorePersistedValue True to restore the persisted value;
1580      *            false to use the given <var>defaultValue</var>.
1581      * @param defaultValue The default value for this Preference. Only use this
1582      *            if <var>restorePersistedValue</var> is false.
1583      */
onSetInitialValue(boolean restorePersistedValue, Object defaultValue)1584     protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
1585     }
1586 
tryCommit(SharedPreferences.Editor editor)1587     private void tryCommit(SharedPreferences.Editor editor) {
1588         if (mPreferenceManager.shouldCommit()) {
1589             try {
1590                 editor.apply();
1591             } catch (AbstractMethodError unused) {
1592                 // The app injected its own pre-Gingerbread
1593                 // SharedPreferences.Editor implementation without
1594                 // an apply method.
1595                 editor.commit();
1596             }
1597         }
1598     }
1599 
1600     /**
1601      * Attempts to persist a String if this Preference is persistent.
1602      *
1603      * @param value The value to persist.
1604      * @return True if this Preference is persistent. (This is not whether the
1605      *         value was persisted, since we may not necessarily commit if there
1606      *         will be a batch commit later.)
1607      * @see #getPersistedString(String)
1608      */
persistString(String value)1609     protected boolean persistString(String value) {
1610         if (!shouldPersist()) {
1611             return false;
1612         }
1613 
1614         // Shouldn't store null
1615         if (TextUtils.equals(value, getPersistedString(null))) {
1616             // It's already there, so the same as persisting
1617             return true;
1618         }
1619 
1620         PreferenceDataStore dataStore = getPreferenceDataStore();
1621         if (dataStore != null) {
1622             dataStore.putString(mKey, value);
1623         } else {
1624             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1625             editor.putString(mKey, value);
1626             tryCommit(editor);
1627         }
1628         return true;
1629     }
1630 
1631     /**
1632      * Attempts to get a persisted String if this Preference is persistent.
1633      *
1634      * @param defaultReturnValue The default value to return if either this
1635      *            Preference is not persistent or this Preference is not present.
1636      * @return The value from the data store or the default return
1637      *         value.
1638      * @see #persistString(String)
1639      */
getPersistedString(String defaultReturnValue)1640     protected String getPersistedString(String defaultReturnValue) {
1641         if (!shouldPersist()) {
1642             return defaultReturnValue;
1643         }
1644 
1645         PreferenceDataStore dataStore = getPreferenceDataStore();
1646         if (dataStore != null) {
1647             return dataStore.getString(mKey, defaultReturnValue);
1648         }
1649 
1650         return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
1651     }
1652 
1653     /**
1654      * Attempts to persist a set of Strings if this Preference is persistent.
1655      *
1656      * @param values The values to persist.
1657      * @return True if this Preference is persistent. (This is not whether the
1658      *         value was persisted, since we may not necessarily commit if there
1659      *         will be a batch commit later.)
1660      * @see #getPersistedStringSet(Set)
1661      */
persistStringSet(Set<String> values)1662     public boolean persistStringSet(Set<String>  values) {
1663         if (!shouldPersist()) {
1664             return false;
1665         }
1666 
1667         // Shouldn't store null
1668         if (values.equals(getPersistedStringSet(null))) {
1669             // It's already there, so the same as persisting
1670             return true;
1671         }
1672 
1673         PreferenceDataStore dataStore = getPreferenceDataStore();
1674         if (dataStore != null) {
1675             dataStore.putStringSet(mKey, values);
1676         } else {
1677             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1678             editor.putStringSet(mKey, values);
1679             tryCommit(editor);
1680         }
1681         return true;
1682     }
1683 
1684     /**
1685      * Attempts to get a persisted set of Strings if this Preference is persistent.
1686      *
1687      * @param defaultReturnValue The default value to return if either this
1688      *            Preference is not persistent or this Preference is not present.
1689      * @return The value from the data store or the default return
1690      *         value.
1691      * @see #persistStringSet(Set)
1692      */
getPersistedStringSet(Set<String> defaultReturnValue)1693     public Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
1694         if (!shouldPersist()) {
1695             return defaultReturnValue;
1696         }
1697 
1698         PreferenceDataStore dataStore = getPreferenceDataStore();
1699         if (dataStore != null) {
1700             return dataStore.getStringSet(mKey, defaultReturnValue);
1701         }
1702 
1703         return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
1704     }
1705 
1706     /**
1707      * Attempts to persist an int if this Preference is persistent.
1708      *
1709      * @param value The value to persist.
1710      * @return True if this Preference is persistent. (This is not whether the
1711      *         value was persisted, since we may not necessarily commit if there
1712      *         will be a batch commit later.)
1713      * @see #persistString(String)
1714      * @see #getPersistedInt(int)
1715      */
persistInt(int value)1716     protected boolean persistInt(int value) {
1717         if (!shouldPersist()) {
1718             return false;
1719         }
1720 
1721         if (value == getPersistedInt(~value)) {
1722             // It's already there, so the same as persisting
1723             return true;
1724         }
1725 
1726         PreferenceDataStore dataStore = getPreferenceDataStore();
1727         if (dataStore != null) {
1728             dataStore.putInt(mKey, value);
1729         } else {
1730             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1731             editor.putInt(mKey, value);
1732             tryCommit(editor);
1733         }
1734         return true;
1735     }
1736 
1737     /**
1738      * Attempts to get a persisted int if this Preference is persistent.
1739      *
1740      * @param defaultReturnValue The default value to return if either this
1741      *            Preference is not persistent or this Preference is not present.
1742      * @return The value from the data store or the default return
1743      *         value.
1744      * @see #getPersistedString(String)
1745      * @see #persistInt(int)
1746      */
getPersistedInt(int defaultReturnValue)1747     protected int getPersistedInt(int defaultReturnValue) {
1748         if (!shouldPersist()) {
1749             return defaultReturnValue;
1750         }
1751 
1752         PreferenceDataStore dataStore = getPreferenceDataStore();
1753         if (dataStore != null) {
1754             return dataStore.getInt(mKey, defaultReturnValue);
1755         }
1756 
1757         return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
1758     }
1759 
1760     /**
1761      * Attempts to persist a long if this Preference is persistent.
1762      *
1763      * @param value The value to persist.
1764      * @return True if this Preference is persistent. (This is not whether the
1765      *         value was persisted, since we may not necessarily commit if there
1766      *         will be a batch commit later.)
1767      * @see #persistString(String)
1768      * @see #getPersistedFloat(float)
1769      */
persistFloat(float value)1770     protected boolean persistFloat(float value) {
1771         if (!shouldPersist()) {
1772             return false;
1773         }
1774 
1775         if (value == getPersistedFloat(Float.NaN)) {
1776             // It's already there, so the same as persisting
1777             return true;
1778         }
1779 
1780         PreferenceDataStore dataStore = getPreferenceDataStore();
1781         if (dataStore != null) {
1782             dataStore.putFloat(mKey, value);
1783         } else {
1784             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1785             editor.putFloat(mKey, value);
1786             tryCommit(editor);
1787         }
1788         return true;
1789     }
1790 
1791     /**
1792      * Attempts to get a persisted float if this Preference is persistent.
1793      *
1794      * @param defaultReturnValue The default value to return if either this
1795      *            Preference is not persistent or this Preference is not present.
1796      * @return The value from the data store or the default return
1797      *         value.
1798      * @see #getPersistedString(String)
1799      * @see #persistFloat(float)
1800      */
getPersistedFloat(float defaultReturnValue)1801     protected float getPersistedFloat(float defaultReturnValue) {
1802         if (!shouldPersist()) {
1803             return defaultReturnValue;
1804         }
1805 
1806         PreferenceDataStore dataStore = getPreferenceDataStore();
1807         if (dataStore != null) {
1808             return dataStore.getFloat(mKey, defaultReturnValue);
1809         }
1810 
1811         return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
1812     }
1813 
1814     /**
1815      * Attempts to persist a long if this Preference is persistent.
1816      *
1817      * @param value The value to persist.
1818      * @return True if this Preference is persistent. (This is not whether the
1819      *         value was persisted, since we may not necessarily commit if there
1820      *         will be a batch commit later.)
1821      * @see #persistString(String)
1822      * @see #getPersistedLong(long)
1823      */
persistLong(long value)1824     protected boolean persistLong(long value) {
1825         if (!shouldPersist()) {
1826             return false;
1827         }
1828 
1829         if (value == getPersistedLong(~value)) {
1830             // It's already there, so the same as persisting
1831             return true;
1832         }
1833 
1834         PreferenceDataStore dataStore = getPreferenceDataStore();
1835         if (dataStore != null) {
1836             dataStore.putLong(mKey, value);
1837         } else {
1838             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1839             editor.putLong(mKey, value);
1840             tryCommit(editor);
1841         }
1842         return true;
1843     }
1844 
1845     /**
1846      * Attempts to get a persisted long if this Preference is persistent.
1847      *
1848      * @param defaultReturnValue The default value to return if either this
1849      *            Preference is not persistent or this Preference is not present.
1850      * @return The value from the data store or the default return
1851      *         value.
1852      * @see #getPersistedString(String)
1853      * @see #persistLong(long)
1854      */
getPersistedLong(long defaultReturnValue)1855     protected long getPersistedLong(long defaultReturnValue) {
1856         if (!shouldPersist()) {
1857             return defaultReturnValue;
1858         }
1859 
1860         PreferenceDataStore dataStore = getPreferenceDataStore();
1861         if (dataStore != null) {
1862             return dataStore.getLong(mKey, defaultReturnValue);
1863         }
1864 
1865         return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
1866     }
1867 
1868     /**
1869      * Attempts to persist a boolean if this Preference is persistent.
1870      *
1871      * @param value The value to persist.
1872      * @return True if this Preference is persistent. (This is not whether the
1873      *         value was persisted, since we may not necessarily commit if there
1874      *         will be a batch commit later.)
1875      * @see #persistString(String)
1876      * @see #getPersistedBoolean(boolean)
1877      */
persistBoolean(boolean value)1878     protected boolean persistBoolean(boolean value) {
1879         if (!shouldPersist()) {
1880             return false;
1881         }
1882 
1883         if (value == getPersistedBoolean(!value)) {
1884             // It's already there, so the same as persisting
1885             return true;
1886         }
1887 
1888         PreferenceDataStore dataStore = getPreferenceDataStore();
1889         if (dataStore != null) {
1890             dataStore.putBoolean(mKey, value);
1891         } else {
1892             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
1893             editor.putBoolean(mKey, value);
1894             tryCommit(editor);
1895         }
1896         return true;
1897     }
1898 
1899     /**
1900      * Attempts to get a persisted boolean if this Preference is persistent.
1901      *
1902      * @param defaultReturnValue The default value to return if either this
1903      *            Preference is not persistent or this Preference is not present.
1904      * @return The value from the data store or the default return
1905      *         value.
1906      * @see #getPersistedString(String)
1907      * @see #persistBoolean(boolean)
1908      */
getPersistedBoolean(boolean defaultReturnValue)1909     protected boolean getPersistedBoolean(boolean defaultReturnValue) {
1910         if (!shouldPersist()) {
1911             return defaultReturnValue;
1912         }
1913 
1914         PreferenceDataStore dataStore = getPreferenceDataStore();
1915         if (dataStore != null) {
1916             return dataStore.getBoolean(mKey, defaultReturnValue);
1917         }
1918 
1919         return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
1920     }
1921 
1922     @Override
toString()1923     public String toString() {
1924         return getFilterableStringBuilder().toString();
1925     }
1926 
1927     /**
1928      * Returns the text that will be used to filter this Preference depending on
1929      * user input.
1930      * <p>
1931      * If overridding and calling through to the superclass, make sure to prepend
1932      * your additions with a space.
1933      *
1934      * @return Text as a {@link StringBuilder} that will be used to filter this
1935      *         preference. By default, this is the title and summary
1936      *         (concatenated with a space).
1937      */
getFilterableStringBuilder()1938     StringBuilder getFilterableStringBuilder() {
1939         StringBuilder sb = new StringBuilder();
1940         CharSequence title = getTitle();
1941         if (!TextUtils.isEmpty(title)) {
1942             sb.append(title).append(' ');
1943         }
1944         CharSequence summary = getSummary();
1945         if (!TextUtils.isEmpty(summary)) {
1946             sb.append(summary).append(' ');
1947         }
1948         if (sb.length() > 0) {
1949             // Drop the last space
1950             sb.setLength(sb.length() - 1);
1951         }
1952         return sb;
1953     }
1954 
1955     /**
1956      * Store this Preference hierarchy's frozen state into the given container.
1957      *
1958      * @param container The Bundle in which to save the instance of this Preference.
1959      *
1960      * @see #restoreHierarchyState
1961      * @see #onSaveInstanceState
1962      */
saveHierarchyState(Bundle container)1963     public void saveHierarchyState(Bundle container) {
1964         dispatchSaveInstanceState(container);
1965     }
1966 
1967     /**
1968      * Called by {@link #saveHierarchyState} to store the instance for this Preference and its
1969      * children. May be overridden to modify how the save happens for children. For example, some
1970      * Preference objects may want to not store an instance for their children.
1971      *
1972      * @param container The Bundle in which to save the instance of this Preference.
1973      *
1974      * @see #saveHierarchyState
1975      * @see #onSaveInstanceState
1976      */
dispatchSaveInstanceState(Bundle container)1977     void dispatchSaveInstanceState(Bundle container) {
1978         if (hasKey()) {
1979             mBaseMethodCalled = false;
1980             Parcelable state = onSaveInstanceState();
1981             if (!mBaseMethodCalled) {
1982                 throw new IllegalStateException(
1983                         "Derived class did not call super.onSaveInstanceState()");
1984             }
1985             if (state != null) {
1986                 container.putParcelable(mKey, state);
1987             }
1988         }
1989     }
1990 
1991     /**
1992      * Hook allowing a Preference to generate a representation of its internal
1993      * state that can later be used to create a new instance with that same
1994      * state. This state should only contain information that is not persistent
1995      * or can be reconstructed later.
1996      *
1997      * @return A Parcelable object containing the current dynamic state of this Preference, or
1998      *         {@code null} if there is nothing interesting to save. The default implementation
1999      *         returns {@code null}.
2000      * @see #onRestoreInstanceState
2001      * @see #saveHierarchyState
2002      */
onSaveInstanceState()2003     protected Parcelable onSaveInstanceState() {
2004         mBaseMethodCalled = true;
2005         return BaseSavedState.EMPTY_STATE;
2006     }
2007 
2008     /**
2009      * Restore this Preference hierarchy's previously saved state from the given container.
2010      *
2011      * @param container The Bundle that holds the previously saved state.
2012      *
2013      * @see #saveHierarchyState
2014      * @see #onRestoreInstanceState
2015      */
restoreHierarchyState(Bundle container)2016     public void restoreHierarchyState(Bundle container) {
2017         dispatchRestoreInstanceState(container);
2018     }
2019 
2020     /**
2021      * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
2022      * Preference and its children. May be overridden to modify how restoring
2023      * happens to the children of a Preference. For example, some Preference objects may
2024      * not want to save state for their children.
2025      *
2026      * @param container The Bundle that holds the previously saved state.
2027      * @see #restoreHierarchyState
2028      * @see #onRestoreInstanceState
2029      */
dispatchRestoreInstanceState(Bundle container)2030     void dispatchRestoreInstanceState(Bundle container) {
2031         if (hasKey()) {
2032             Parcelable state = container.getParcelable(mKey);
2033             if (state != null) {
2034                 mBaseMethodCalled = false;
2035                 onRestoreInstanceState(state);
2036                 if (!mBaseMethodCalled) {
2037                     throw new IllegalStateException(
2038                             "Derived class did not call super.onRestoreInstanceState()");
2039                 }
2040             }
2041         }
2042     }
2043 
2044     /**
2045      * Hook allowing a Preference to re-apply a representation of its internal state that had
2046      * previously been generated by {@link #onSaveInstanceState}. This function will never be called
2047      * with a {@code null} state.
2048      *
2049      * @param state The saved state that had previously been returned by
2050      *            {@link #onSaveInstanceState}.
2051      * @see #onSaveInstanceState
2052      * @see #restoreHierarchyState
2053      */
onRestoreInstanceState(Parcelable state)2054     protected void onRestoreInstanceState(Parcelable state) {
2055         mBaseMethodCalled = true;
2056         if (state != BaseSavedState.EMPTY_STATE && state != null) {
2057             throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
2058         }
2059     }
2060 
2061     /**
2062      * A base class for managing the instance state of a {@link Preference}.
2063      */
2064     public static class BaseSavedState extends AbsSavedState {
BaseSavedState(Parcel source)2065         public BaseSavedState(Parcel source) {
2066             super(source);
2067         }
2068 
BaseSavedState(Parcelable superState)2069         public BaseSavedState(Parcelable superState) {
2070             super(superState);
2071         }
2072 
2073         public static final Parcelable.Creator<BaseSavedState> CREATOR =
2074                 new Parcelable.Creator<BaseSavedState>() {
2075                     public BaseSavedState createFromParcel(Parcel in) {
2076                         return new BaseSavedState(in);
2077                     }
2078 
2079                     public BaseSavedState[] newArray(int size) {
2080                         return new BaseSavedState[size];
2081                     }
2082                 };
2083     }
2084 
2085 }
2086