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