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