Home | History | Annotate | Download | only in preference
      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.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          */
    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          */
    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          */
    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          */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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
    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      */
    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
    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      */
    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
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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
   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)
   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      */
   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      */
   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      */
   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      */
   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
   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
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   1379     void assignParent(@Nullable PreferenceGroup parentGroup) {
   1380         mParentGroup = parentGroup;
   1381     }
   1382 
   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 
   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      */
   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
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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
   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
   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      */
   1574     public void setDefaultValue(Object defaultValue) {
   1575         mDefaultValue = defaultValue;
   1576     }
   1577 
   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      */
   1615     protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
   1616     }
   1617 
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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 {
   2103         public BaseSavedState(Parcel source) {
   2104             super(source);
   2105         }
   2106 
   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