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 java.util.ArrayList;
     20 import java.util.List;
     21 
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.SharedPreferences;
     25 import android.content.res.TypedArray;
     26 import android.os.Bundle;
     27 import android.os.Parcel;
     28 import android.os.Parcelable;
     29 import android.text.TextUtils;
     30 import android.util.AttributeSet;
     31 import com.android.internal.util.CharSequences;
     32 import android.view.AbsSavedState;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.view.ViewGroup;
     36 import android.widget.ListView;
     37 import android.widget.TextView;
     38 
     39 /**
     40  * Represents the basic Preference UI building
     41  * block displayed by a {@link PreferenceActivity} in the form of a
     42  * {@link ListView}. This class provides the {@link View} to be displayed in
     43  * the activity and associates with a {@link SharedPreferences} to
     44  * store/retrieve the preference data.
     45  * <p>
     46  * When specifying a preference hierarchy in XML, each element can point to a
     47  * subclass of {@link Preference}, similar to the view hierarchy and layouts.
     48  * <p>
     49  * This class contains a {@code key} that will be used as the key into the
     50  * {@link SharedPreferences}. It is up to the subclass to decide how to store
     51  * the value.
     52  *
     53  * @attr ref android.R.styleable#Preference_key
     54  * @attr ref android.R.styleable#Preference_title
     55  * @attr ref android.R.styleable#Preference_summary
     56  * @attr ref android.R.styleable#Preference_order
     57  * @attr ref android.R.styleable#Preference_layout
     58  * @attr ref android.R.styleable#Preference_widgetLayout
     59  * @attr ref android.R.styleable#Preference_enabled
     60  * @attr ref android.R.styleable#Preference_selectable
     61  * @attr ref android.R.styleable#Preference_dependency
     62  * @attr ref android.R.styleable#Preference_persistent
     63  * @attr ref android.R.styleable#Preference_defaultValue
     64  * @attr ref android.R.styleable#Preference_shouldDisableView
     65  */
     66 public class Preference implements Comparable<Preference>, OnDependencyChangeListener {
     67     /**
     68      * Specify for {@link #setOrder(int)} if a specific order is not required.
     69      */
     70     public static final int DEFAULT_ORDER = Integer.MAX_VALUE;
     71 
     72     private Context mContext;
     73     private PreferenceManager mPreferenceManager;
     74 
     75     /**
     76      * Set when added to hierarchy since we need a unique ID within that
     77      * hierarchy.
     78      */
     79     private long mId;
     80 
     81     private OnPreferenceChangeListener mOnChangeListener;
     82     private OnPreferenceClickListener mOnClickListener;
     83 
     84     private int mOrder = DEFAULT_ORDER;
     85     private CharSequence mTitle;
     86     private CharSequence mSummary;
     87     private String mKey;
     88     private Intent mIntent;
     89     private boolean mEnabled = true;
     90     private boolean mSelectable = true;
     91     private boolean mRequiresKey;
     92     private boolean mPersistent = true;
     93     private String mDependencyKey;
     94     private Object mDefaultValue;
     95     private boolean mDependencyMet = true;
     96 
     97     /**
     98      * @see #setShouldDisableView(boolean)
     99      */
    100     private boolean mShouldDisableView = true;
    101 
    102     private int mLayoutResId = com.android.internal.R.layout.preference;
    103     private int mWidgetLayoutResId;
    104     private boolean mHasSpecifiedLayout = false;
    105 
    106     private OnPreferenceChangeInternalListener mListener;
    107 
    108     private List<Preference> mDependents;
    109 
    110     private boolean mBaseMethodCalled;
    111 
    112     /**
    113      * Interface definition for a callback to be invoked when the value of this
    114      * {@link Preference} has been changed by the user and is
    115      * about to be set and/or persisted.  This gives the client a chance
    116      * to prevent setting and/or persisting the value.
    117      */
    118     public interface OnPreferenceChangeListener {
    119         /**
    120          * Called when a Preference has been changed by the user. This is
    121          * called before the state of the Preference is about to be updated and
    122          * before the state is persisted.
    123          *
    124          * @param preference The changed Preference.
    125          * @param newValue The new value of the Preference.
    126          * @return True to update the state of the Preference with the new value.
    127          */
    128         boolean onPreferenceChange(Preference preference, Object newValue);
    129     }
    130 
    131     /**
    132      * Interface definition for a callback to be invoked when a {@link Preference} is
    133      * clicked.
    134      */
    135     public interface OnPreferenceClickListener {
    136         /**
    137          * Called when a Preference has been clicked.
    138          *
    139          * @param preference The Preference that was clicked.
    140          * @return True if the click was handled.
    141          */
    142         boolean onPreferenceClick(Preference preference);
    143     }
    144 
    145     /**
    146      * Interface definition for a callback to be invoked when this
    147      * {@link Preference} is changed or, if this is a group, there is an
    148      * addition/removal of {@link Preference}(s). This is used internally.
    149      */
    150     interface OnPreferenceChangeInternalListener {
    151         /**
    152          * Called when this Preference has changed.
    153          *
    154          * @param preference This preference.
    155          */
    156         void onPreferenceChange(Preference preference);
    157 
    158         /**
    159          * Called when this group has added/removed {@link Preference}(s).
    160          *
    161          * @param preference This Preference.
    162          */
    163         void onPreferenceHierarchyChange(Preference preference);
    164     }
    165 
    166     /**
    167      * Perform inflation from XML and apply a class-specific base style. This
    168      * constructor of Preference allows subclasses to use their own base
    169      * style when they are inflating. For example, a {@link CheckBoxPreference}
    170      * constructor calls this version of the super class constructor and
    171      * supplies {@code android.R.attr.checkBoxPreferenceStyle} for <var>defStyle</var>.
    172      * This allows the theme's checkbox preference style to modify all of the base
    173      * preference attributes as well as the {@link CheckBoxPreference} class's
    174      * attributes.
    175      *
    176      * @param context The Context this is associated with, through which it can
    177      *            access the current theme, resources, {@link SharedPreferences},
    178      *            etc.
    179      * @param attrs The attributes of the XML tag that is inflating the preference.
    180      * @param defStyle The default style to apply to this preference. If 0, no style
    181      *            will be applied (beyond what is included in the theme). This
    182      *            may either be an attribute resource, whose value will be
    183      *            retrieved from the current theme, or an explicit style
    184      *            resource.
    185      * @see #Preference(Context, AttributeSet)
    186      */
    187     public Preference(Context context, AttributeSet attrs, int defStyle) {
    188         mContext = context;
    189 
    190         TypedArray a = context.obtainStyledAttributes(attrs,
    191                 com.android.internal.R.styleable.Preference, defStyle, 0);
    192         for (int i = a.getIndexCount(); i >= 0; i--) {
    193             int attr = a.getIndex(i);
    194             switch (attr) {
    195                 case com.android.internal.R.styleable.Preference_key:
    196                     mKey = a.getString(attr);
    197                     break;
    198 
    199                 case com.android.internal.R.styleable.Preference_title:
    200                     mTitle = a.getString(attr);
    201                     break;
    202 
    203                 case com.android.internal.R.styleable.Preference_summary:
    204                     mSummary = a.getString(attr);
    205                     break;
    206 
    207                 case com.android.internal.R.styleable.Preference_order:
    208                     mOrder = a.getInt(attr, mOrder);
    209                     break;
    210 
    211                 case com.android.internal.R.styleable.Preference_layout:
    212                     mLayoutResId = a.getResourceId(attr, mLayoutResId);
    213                     break;
    214 
    215                 case com.android.internal.R.styleable.Preference_widgetLayout:
    216                     mWidgetLayoutResId = a.getResourceId(attr, mWidgetLayoutResId);
    217                     break;
    218 
    219                 case com.android.internal.R.styleable.Preference_enabled:
    220                     mEnabled = a.getBoolean(attr, true);
    221                     break;
    222 
    223                 case com.android.internal.R.styleable.Preference_selectable:
    224                     mSelectable = a.getBoolean(attr, true);
    225                     break;
    226 
    227                 case com.android.internal.R.styleable.Preference_persistent:
    228                     mPersistent = a.getBoolean(attr, mPersistent);
    229                     break;
    230 
    231                 case com.android.internal.R.styleable.Preference_dependency:
    232                     mDependencyKey = a.getString(attr);
    233                     break;
    234 
    235                 case com.android.internal.R.styleable.Preference_defaultValue:
    236                     mDefaultValue = onGetDefaultValue(a, attr);
    237                     break;
    238 
    239                 case com.android.internal.R.styleable.Preference_shouldDisableView:
    240                     mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
    241                     break;
    242             }
    243         }
    244         a.recycle();
    245 
    246         if (!getClass().getName().startsWith("android.preference")) {
    247             // For subclasses not in this package, assume the worst and don't cache views
    248             mHasSpecifiedLayout = true;
    249         }
    250     }
    251 
    252     /**
    253      * Constructor that is called when inflating a Preference from XML. This is
    254      * called when a Preference is being constructed from an XML file, supplying
    255      * attributes that were specified in the XML file. This version uses a
    256      * default style of 0, so the only attribute values applied are those in the
    257      * Context's Theme and the given AttributeSet.
    258      *
    259      * @param context The Context this is associated with, through which it can
    260      *            access the current theme, resources, {@link SharedPreferences},
    261      *            etc.
    262      * @param attrs The attributes of the XML tag that is inflating the
    263      *            preference.
    264      * @see #Preference(Context, AttributeSet, int)
    265      */
    266     public Preference(Context context, AttributeSet attrs) {
    267         this(context, attrs, 0);
    268     }
    269 
    270     /**
    271      * Constructor to create a Preference.
    272      *
    273      * @param context The Context in which to store Preference values.
    274      */
    275     public Preference(Context context) {
    276         this(context, null);
    277     }
    278 
    279     /**
    280      * Called when a Preference is being inflated and the default value
    281      * attribute needs to be read. Since different Preference types have
    282      * different value types, the subclass should get and return the default
    283      * value which will be its value type.
    284      * <p>
    285      * For example, if the value type is String, the body of the method would
    286      * proxy to {@link TypedArray#getString(int)}.
    287      *
    288      * @param a The set of attributes.
    289      * @param index The index of the default value attribute.
    290      * @return The default value of this preference type.
    291      */
    292     protected Object onGetDefaultValue(TypedArray a, int index) {
    293         return null;
    294     }
    295 
    296     /**
    297      * Sets an {@link Intent} to be used for
    298      * {@link Context#startActivity(Intent)} when this Preference is clicked.
    299      *
    300      * @param intent The intent associated with this Preference.
    301      */
    302     public void setIntent(Intent intent) {
    303         mIntent = intent;
    304     }
    305 
    306     /**
    307      * Return the {@link Intent} associated with this Preference.
    308      *
    309      * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML.
    310      */
    311     public Intent getIntent() {
    312         return mIntent;
    313     }
    314 
    315     /**
    316      * Sets the layout resource that is inflated as the {@link View} to be shown
    317      * for this Preference. In most cases, the default layout is sufficient for
    318      * custom Preference objects and only the widget layout needs to be changed.
    319      * <p>
    320      * This layout should contain a {@link ViewGroup} with ID
    321      * {@link android.R.id#widget_frame} to be the parent of the specific widget
    322      * for this Preference. It should similarly contain
    323      * {@link android.R.id#title} and {@link android.R.id#summary}.
    324      *
    325      * @param layoutResId The layout resource ID to be inflated and returned as
    326      *            a {@link View}.
    327      * @see #setWidgetLayoutResource(int)
    328      */
    329     public void setLayoutResource(int layoutResId) {
    330         if (layoutResId != mLayoutResId) {
    331             // Layout changed
    332             mHasSpecifiedLayout = true;
    333         }
    334 
    335         mLayoutResId = layoutResId;
    336     }
    337 
    338     /**
    339      * Gets the layout resource that will be shown as the {@link View} for this Preference.
    340      *
    341      * @return The layout resource ID.
    342      */
    343     public int getLayoutResource() {
    344         return mLayoutResId;
    345     }
    346 
    347     /**
    348      * Sets The layout for the controllable widget portion of this Preference. This
    349      * is inflated into the main layout. For example, a {@link CheckBoxPreference}
    350      * would specify a custom layout (consisting of just the CheckBox) here,
    351      * instead of creating its own main layout.
    352      *
    353      * @param widgetLayoutResId The layout resource ID to be inflated into the
    354      *            main layout.
    355      * @see #setLayoutResource(int)
    356      */
    357     public void setWidgetLayoutResource(int widgetLayoutResId) {
    358         if (widgetLayoutResId != mWidgetLayoutResId) {
    359             // Layout changed
    360             mHasSpecifiedLayout = true;
    361         }
    362         mWidgetLayoutResId = widgetLayoutResId;
    363     }
    364 
    365     /**
    366      * Gets the layout resource for the controllable widget portion of this Preference.
    367      *
    368      * @return The layout resource ID.
    369      */
    370     public int getWidgetLayoutResource() {
    371         return mWidgetLayoutResId;
    372     }
    373 
    374     /**
    375      * Gets the View that will be shown in the {@link PreferenceActivity}.
    376      *
    377      * @param convertView The old View to reuse, if possible. Note: You should
    378      *            check that this View is non-null and of an appropriate type
    379      *            before using. If it is not possible to convert this View to
    380      *            display the correct data, this method can create a new View.
    381      * @param parent The parent that this View will eventually be attached to.
    382      * @return Returns the same Preference object, for chaining multiple calls
    383      *         into a single statement.
    384      * @see #onCreateView(ViewGroup)
    385      * @see #onBindView(View)
    386      */
    387     public View getView(View convertView, ViewGroup parent) {
    388         if (convertView == null) {
    389             convertView = onCreateView(parent);
    390         }
    391         onBindView(convertView);
    392         return convertView;
    393     }
    394 
    395     /**
    396      * Creates the View to be shown for this Preference in the
    397      * {@link PreferenceActivity}. The default behavior is to inflate the main
    398      * layout of this Preference (see {@link #setLayoutResource(int)}. If
    399      * changing this behavior, please specify a {@link ViewGroup} with ID
    400      * {@link android.R.id#widget_frame}.
    401      * <p>
    402      * Make sure to call through to the superclass's implementation.
    403      *
    404      * @param parent The parent that this View will eventually be attached to.
    405      * @return The View that displays this Preference.
    406      * @see #onBindView(View)
    407      */
    408     protected View onCreateView(ViewGroup parent) {
    409         final LayoutInflater layoutInflater =
    410             (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    411 
    412         final View layout = layoutInflater.inflate(mLayoutResId, parent, false);
    413 
    414         if (mWidgetLayoutResId != 0) {
    415             final ViewGroup widgetFrame = (ViewGroup)layout.findViewById(com.android.internal.R.id.widget_frame);
    416             layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);
    417         }
    418 
    419         return layout;
    420     }
    421 
    422     /**
    423      * Binds the created View to the data for this Preference.
    424      * <p>
    425      * This is a good place to grab references to custom Views in the layout and
    426      * set properties on them.
    427      * <p>
    428      * Make sure to call through to the superclass's implementation.
    429      *
    430      * @param view The View that shows this Preference.
    431      * @see #onCreateView(ViewGroup)
    432      */
    433     protected void onBindView(View view) {
    434         TextView textView = (TextView) view.findViewById(com.android.internal.R.id.title);
    435         if (textView != null) {
    436             textView.setText(getTitle());
    437         }
    438 
    439         textView = (TextView) view.findViewById(com.android.internal.R.id.summary);
    440         if (textView != null) {
    441             final CharSequence summary = getSummary();
    442             if (!TextUtils.isEmpty(summary)) {
    443                 if (textView.getVisibility() != View.VISIBLE) {
    444                     textView.setVisibility(View.VISIBLE);
    445                 }
    446 
    447                 textView.setText(getSummary());
    448             } else {
    449                 if (textView.getVisibility() != View.GONE) {
    450                     textView.setVisibility(View.GONE);
    451                 }
    452             }
    453         }
    454 
    455         if (mShouldDisableView) {
    456             setEnabledStateOnViews(view, isEnabled());
    457         }
    458     }
    459 
    460     /**
    461      * Makes sure the view (and any children) get the enabled state changed.
    462      */
    463     private void setEnabledStateOnViews(View v, boolean enabled) {
    464         v.setEnabled(enabled);
    465 
    466         if (v instanceof ViewGroup) {
    467             final ViewGroup vg = (ViewGroup) v;
    468             for (int i = vg.getChildCount() - 1; i >= 0; i--) {
    469                 setEnabledStateOnViews(vg.getChildAt(i), enabled);
    470             }
    471         }
    472     }
    473 
    474     /**
    475      * Sets the order of this Preference with respect to other
    476      * Preference objects on the same level. If this is not specified, the
    477      * default behavior is to sort alphabetically. The
    478      * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
    479      * Preference objects based on the order they appear in the XML.
    480      *
    481      * @param order The order for this Preference. A lower value will be shown
    482      *            first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
    483      *            allow ordering from XML.
    484      * @see PreferenceGroup#setOrderingAsAdded(boolean)
    485      * @see #DEFAULT_ORDER
    486      */
    487     public void setOrder(int order) {
    488         if (order != mOrder) {
    489             mOrder = order;
    490 
    491             // Reorder the list
    492             notifyHierarchyChanged();
    493         }
    494     }
    495 
    496     /**
    497      * Gets the order of this Preference with respect to other Preference objects
    498      * on the same level.
    499      *
    500      * @return The order of this Preference.
    501      * @see #setOrder(int)
    502      */
    503     public int getOrder() {
    504         return mOrder;
    505     }
    506 
    507     /**
    508      * Sets the title for this Preference with a CharSequence.
    509      * This title will be placed into the ID
    510      * {@link android.R.id#title} within the View created by
    511      * {@link #onCreateView(ViewGroup)}.
    512      *
    513      * @param title The title for this Preference.
    514      */
    515     public void setTitle(CharSequence title) {
    516         if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
    517             mTitle = title;
    518             notifyChanged();
    519         }
    520     }
    521 
    522     /**
    523      * Sets the title for this Preference with a resource ID.
    524      *
    525      * @see #setTitle(CharSequence)
    526      * @param titleResId The title as a resource ID.
    527      */
    528     public void setTitle(int titleResId) {
    529         setTitle(mContext.getString(titleResId));
    530     }
    531 
    532     /**
    533      * Returns the title of this Preference.
    534      *
    535      * @return The title.
    536      * @see #setTitle(CharSequence)
    537      */
    538     public CharSequence getTitle() {
    539         return mTitle;
    540     }
    541 
    542     /**
    543      * Returns the summary of this Preference.
    544      *
    545      * @return The summary.
    546      * @see #setSummary(CharSequence)
    547      */
    548     public CharSequence getSummary() {
    549         return mSummary;
    550     }
    551 
    552     /**
    553      * Sets the summary for this Preference with a CharSequence.
    554      *
    555      * @param summary The summary for the preference.
    556      */
    557     public void setSummary(CharSequence summary) {
    558         if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
    559             mSummary = summary;
    560             notifyChanged();
    561         }
    562     }
    563 
    564     /**
    565      * Sets the summary for this Preference with a resource ID.
    566      *
    567      * @see #setSummary(CharSequence)
    568      * @param summaryResId The summary as a resource.
    569      */
    570     public void setSummary(int summaryResId) {
    571         setSummary(mContext.getString(summaryResId));
    572     }
    573 
    574     /**
    575      * Sets whether this Preference is enabled. If disabled, it will
    576      * not handle clicks.
    577      *
    578      * @param enabled Set true to enable it.
    579      */
    580     public void setEnabled(boolean enabled) {
    581         if (mEnabled != enabled) {
    582             mEnabled = enabled;
    583 
    584             // Enabled state can change dependent preferences' states, so notify
    585             notifyDependencyChange(shouldDisableDependents());
    586 
    587             notifyChanged();
    588         }
    589     }
    590 
    591     /**
    592      * Checks whether this Preference should be enabled in the list.
    593      *
    594      * @return True if this Preference is enabled, false otherwise.
    595      */
    596     public boolean isEnabled() {
    597         return mEnabled && mDependencyMet;
    598     }
    599 
    600     /**
    601      * Sets whether this Preference is selectable.
    602      *
    603      * @param selectable Set true to make it selectable.
    604      */
    605     public void setSelectable(boolean selectable) {
    606         if (mSelectable != selectable) {
    607             mSelectable = selectable;
    608             notifyChanged();
    609         }
    610     }
    611 
    612     /**
    613      * Checks whether this Preference should be selectable in the list.
    614      *
    615      * @return True if it is selectable, false otherwise.
    616      */
    617     public boolean isSelectable() {
    618         return mSelectable;
    619     }
    620 
    621     /**
    622      * Sets whether this Preference should disable its view when it gets
    623      * disabled.
    624      * <p>
    625      * For example, set this and {@link #setEnabled(boolean)} to false for
    626      * preferences that are only displaying information and 1) should not be
    627      * clickable 2) should not have the view set to the disabled state.
    628      *
    629      * @param shouldDisableView Set true if this preference should disable its view
    630      *            when the preference is disabled.
    631      */
    632     public void setShouldDisableView(boolean shouldDisableView) {
    633         mShouldDisableView = shouldDisableView;
    634         notifyChanged();
    635     }
    636 
    637     /**
    638      * Checks whether this Preference should disable its view when it's action is disabled.
    639      * @see #setShouldDisableView(boolean)
    640      * @return True if it should disable the view.
    641      */
    642     public boolean getShouldDisableView() {
    643         return mShouldDisableView;
    644     }
    645 
    646     /**
    647      * Returns a unique ID for this Preference.  This ID should be unique across all
    648      * Preference objects in a hierarchy.
    649      *
    650      * @return A unique ID for this Preference.
    651      */
    652     long getId() {
    653         return mId;
    654     }
    655 
    656     /**
    657      * Processes a click on the preference. This includes saving the value to
    658      * the {@link SharedPreferences}. However, the overridden method should
    659      * call {@link #callChangeListener(Object)} to make sure the client wants to
    660      * update the preference's state with the new value.
    661      */
    662     protected void onClick() {
    663     }
    664 
    665     /**
    666      * Sets the key for this Preference, which is used as a key to the
    667      * {@link SharedPreferences}. This should be unique for the package.
    668      *
    669      * @param key The key for the preference.
    670      */
    671     public void setKey(String key) {
    672         mKey = key;
    673 
    674         if (mRequiresKey && !hasKey()) {
    675             requireKey();
    676         }
    677     }
    678 
    679     /**
    680      * Gets the key for this Preference, which is also the key used for storing
    681      * values into SharedPreferences.
    682      *
    683      * @return The key.
    684      */
    685     public String getKey() {
    686         return mKey;
    687     }
    688 
    689     /**
    690      * Checks whether the key is present, and if it isn't throws an
    691      * exception. This should be called by subclasses that store preferences in
    692      * the {@link SharedPreferences}.
    693      *
    694      * @throws IllegalStateException If there is no key assigned.
    695      */
    696     void requireKey() {
    697         if (mKey == null) {
    698             throw new IllegalStateException("Preference does not have a key assigned.");
    699         }
    700 
    701         mRequiresKey = true;
    702     }
    703 
    704     /**
    705      * Checks whether this Preference has a valid key.
    706      *
    707      * @return True if the key exists and is not a blank string, false otherwise.
    708      */
    709     public boolean hasKey() {
    710         return !TextUtils.isEmpty(mKey);
    711     }
    712 
    713     /**
    714      * Checks whether this Preference is persistent. If it is, it stores its value(s) into
    715      * the persistent {@link SharedPreferences} storage.
    716      *
    717      * @return True if it is persistent.
    718      */
    719     public boolean isPersistent() {
    720         return mPersistent;
    721     }
    722 
    723     /**
    724      * Checks whether, at the given time this method is called,
    725      * this Preference should store/restore its value(s) into the
    726      * {@link SharedPreferences}. This, at minimum, checks whether this
    727      * Preference is persistent and it currently has a key. Before you
    728      * save/restore from the {@link SharedPreferences}, check this first.
    729      *
    730      * @return True if it should persist the value.
    731      */
    732     protected boolean shouldPersist() {
    733         return mPreferenceManager != null && isPersistent() && hasKey();
    734     }
    735 
    736     /**
    737      * Sets whether this Preference is persistent. When persistent,
    738      * it stores its value(s) into the persistent {@link SharedPreferences}
    739      * storage.
    740      *
    741      * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}.
    742      */
    743     public void setPersistent(boolean persistent) {
    744         mPersistent = persistent;
    745     }
    746 
    747     /**
    748      * Call this method after the user changes the preference, but before the
    749      * internal state is set. This allows the client to ignore the user value.
    750      *
    751      * @param newValue The new value of this Preference.
    752      * @return True if the user value should be set as the preference
    753      *         value (and persisted).
    754      */
    755     protected boolean callChangeListener(Object newValue) {
    756         return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue);
    757     }
    758 
    759     /**
    760      * Sets the callback to be invoked when this Preference is changed by the
    761      * user (but before the internal state has been updated).
    762      *
    763      * @param onPreferenceChangeListener The callback to be invoked.
    764      */
    765     public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
    766         mOnChangeListener = onPreferenceChangeListener;
    767     }
    768 
    769     /**
    770      * Returns the callback to be invoked when this Preference is changed by the
    771      * user (but before the internal state has been updated).
    772      *
    773      * @return The callback to be invoked.
    774      */
    775     public OnPreferenceChangeListener getOnPreferenceChangeListener() {
    776         return mOnChangeListener;
    777     }
    778 
    779     /**
    780      * Sets the callback to be invoked when this Preference is clicked.
    781      *
    782      * @param onPreferenceClickListener The callback to be invoked.
    783      */
    784     public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
    785         mOnClickListener = onPreferenceClickListener;
    786     }
    787 
    788     /**
    789      * Returns the callback to be invoked when this Preference is clicked.
    790      *
    791      * @return The callback to be invoked.
    792      */
    793     public OnPreferenceClickListener getOnPreferenceClickListener() {
    794         return mOnClickListener;
    795     }
    796 
    797     /**
    798      * Called when a click should be performed.
    799      *
    800      * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
    801      *            listener should be called in the proper order (between other
    802      *            processing). May be null.
    803      */
    804     void performClick(PreferenceScreen preferenceScreen) {
    805 
    806         if (!isEnabled()) {
    807             return;
    808         }
    809 
    810         onClick();
    811 
    812         if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
    813             return;
    814         }
    815 
    816         PreferenceManager preferenceManager = getPreferenceManager();
    817         if (preferenceManager != null) {
    818             PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
    819                     .getOnPreferenceTreeClickListener();
    820             if (preferenceScreen != null && listener != null
    821                     && listener.onPreferenceTreeClick(preferenceScreen, this)) {
    822                 return;
    823             }
    824         }
    825 
    826         if (mIntent != null) {
    827             Context context = getContext();
    828             context.startActivity(mIntent);
    829         }
    830     }
    831 
    832     /**
    833      * Returns the {@link android.content.Context} of this Preference.
    834      * Each Preference in a Preference hierarchy can be
    835      * from different Context (for example, if multiple activities provide preferences into a single
    836      * {@link PreferenceActivity}). This Context will be used to save the Preference values.
    837      *
    838      * @return The Context of this Preference.
    839      */
    840     public Context getContext() {
    841         return mContext;
    842     }
    843 
    844     /**
    845      * Returns the {@link SharedPreferences} where this Preference can read its
    846      * value(s). Usually, it's easier to use one of the helper read methods:
    847      * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
    848      * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
    849      * {@link #getPersistedString(String)}. To save values, see
    850      * {@link #getEditor()}.
    851      * <p>
    852      * In some cases, writes to the {@link #getEditor()} will not be committed
    853      * right away and hence not show up in the returned
    854      * {@link SharedPreferences}, this is intended behavior to improve
    855      * performance.
    856      *
    857      * @return The {@link SharedPreferences} where this Preference reads its
    858      *         value(s), or null if it isn't attached to a Preference hierarchy.
    859      * @see #getEditor()
    860      */
    861     public SharedPreferences getSharedPreferences() {
    862         if (mPreferenceManager == null) {
    863             return null;
    864         }
    865 
    866         return mPreferenceManager.getSharedPreferences();
    867     }
    868 
    869     /**
    870      * Returns an {@link SharedPreferences.Editor} where this Preference can
    871      * save its value(s). Usually it's easier to use one of the helper save
    872      * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)},
    873      * {@link #persistInt(int)}, {@link #persistLong(long)},
    874      * {@link #persistString(String)}. To read values, see
    875      * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns
    876      * true, it is this Preference's responsibility to commit.
    877      * <p>
    878      * In some cases, writes to this will not be committed right away and hence
    879      * not show up in the SharedPreferences, this is intended behavior to
    880      * improve performance.
    881      *
    882      * @return A {@link SharedPreferences.Editor} where this preference saves
    883      *         its value(s), or null if it isn't attached to a Preference
    884      *         hierarchy.
    885      * @see #shouldCommit()
    886      * @see #getSharedPreferences()
    887      */
    888     public SharedPreferences.Editor getEditor() {
    889         if (mPreferenceManager == null) {
    890             return null;
    891         }
    892 
    893         return mPreferenceManager.getEditor();
    894     }
    895 
    896     /**
    897      * Returns whether the {@link Preference} should commit its saved value(s) in
    898      * {@link #getEditor()}. This may return false in situations where batch
    899      * committing is being done (by the manager) to improve performance.
    900      *
    901      * @return Whether the Preference should commit its saved value(s).
    902      * @see #getEditor()
    903      */
    904     public boolean shouldCommit() {
    905         if (mPreferenceManager == null) {
    906             return false;
    907         }
    908 
    909         return mPreferenceManager.shouldCommit();
    910     }
    911 
    912     /**
    913      * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
    914      *
    915      * @param another The Preference to compare to this one.
    916      * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
    917      *          greater than 0 if this Preference sorts after <var>another</var>.
    918      */
    919     public int compareTo(Preference another) {
    920         if (mOrder != DEFAULT_ORDER
    921                 || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) {
    922             // Do order comparison
    923             return mOrder - another.mOrder;
    924         } else if (mTitle == null) {
    925             return 1;
    926         } else if (another.mTitle == null) {
    927             return -1;
    928         } else {
    929             // Do name comparison
    930             return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
    931         }
    932     }
    933 
    934     /**
    935      * Sets the internal change listener.
    936      *
    937      * @param listener The listener.
    938      * @see #notifyChanged()
    939      */
    940     final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
    941         mListener = listener;
    942     }
    943 
    944     /**
    945      * Should be called when the data of this {@link Preference} has changed.
    946      */
    947     protected void notifyChanged() {
    948         if (mListener != null) {
    949             mListener.onPreferenceChange(this);
    950         }
    951     }
    952 
    953     /**
    954      * Should be called when a Preference has been
    955      * added/removed from this group, or the ordering should be
    956      * re-evaluated.
    957      */
    958     protected void notifyHierarchyChanged() {
    959         if (mListener != null) {
    960             mListener.onPreferenceHierarchyChange(this);
    961         }
    962     }
    963 
    964     /**
    965      * Gets the {@link PreferenceManager} that manages this Preference object's tree.
    966      *
    967      * @return The {@link PreferenceManager}.
    968      */
    969     public PreferenceManager getPreferenceManager() {
    970         return mPreferenceManager;
    971     }
    972 
    973     /**
    974      * Called when this Preference has been attached to a Preference hierarchy.
    975      * Make sure to call the super implementation.
    976      *
    977      * @param preferenceManager The PreferenceManager of the hierarchy.
    978      */
    979     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
    980         mPreferenceManager = preferenceManager;
    981 
    982         mId = preferenceManager.getNextId();
    983 
    984         dispatchSetInitialValue();
    985     }
    986 
    987     /**
    988      * Called when the Preference hierarchy has been attached to the
    989      * {@link PreferenceActivity}. This can also be called when this
    990      * Preference has been attached to a group that was already attached
    991      * to the {@link PreferenceActivity}.
    992      */
    993     protected void onAttachedToActivity() {
    994         // At this point, the hierarchy that this preference is in is connected
    995         // with all other preferences.
    996         registerDependency();
    997     }
    998 
    999     private void registerDependency() {
   1000 
   1001         if (TextUtils.isEmpty(mDependencyKey)) return;
   1002 
   1003         Preference preference = findPreferenceInHierarchy(mDependencyKey);
   1004         if (preference != null) {
   1005             preference.registerDependent(this);
   1006         } else {
   1007             throw new IllegalStateException("Dependency \"" + mDependencyKey
   1008                     + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
   1009         }
   1010     }
   1011 
   1012     private void unregisterDependency() {
   1013         if (mDependencyKey != null) {
   1014             final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
   1015             if (oldDependency != null) {
   1016                 oldDependency.unregisterDependent(this);
   1017             }
   1018         }
   1019     }
   1020 
   1021     /**
   1022      * Finds a Preference in this hierarchy (the whole thing,
   1023      * even above/below your {@link PreferenceScreen} screen break) with the given
   1024      * key.
   1025      * <p>
   1026      * This only functions after we have been attached to a hierarchy.
   1027      *
   1028      * @param key The key of the Preference to find.
   1029      * @return The Preference that uses the given key.
   1030      */
   1031     protected Preference findPreferenceInHierarchy(String key) {
   1032         if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
   1033             return null;
   1034         }
   1035 
   1036         return mPreferenceManager.findPreference(key);
   1037     }
   1038 
   1039     /**
   1040      * Adds a dependent Preference on this Preference so we can notify it.
   1041      * Usually, the dependent Preference registers itself (it's good for it to
   1042      * know it depends on something), so please use
   1043      * {@link Preference#setDependency(String)} on the dependent Preference.
   1044      *
   1045      * @param dependent The dependent Preference that will be enabled/disabled
   1046      *            according to the state of this Preference.
   1047      */
   1048     private void registerDependent(Preference dependent) {
   1049         if (mDependents == null) {
   1050             mDependents = new ArrayList<Preference>();
   1051         }
   1052 
   1053         mDependents.add(dependent);
   1054 
   1055         dependent.onDependencyChanged(this, shouldDisableDependents());
   1056     }
   1057 
   1058     /**
   1059      * Removes a dependent Preference on this Preference.
   1060      *
   1061      * @param dependent The dependent Preference that will be enabled/disabled
   1062      *            according to the state of this Preference.
   1063      * @return Returns the same Preference object, for chaining multiple calls
   1064      *         into a single statement.
   1065      */
   1066     private void unregisterDependent(Preference dependent) {
   1067         if (mDependents != null) {
   1068             mDependents.remove(dependent);
   1069         }
   1070     }
   1071 
   1072     /**
   1073      * Notifies any listening dependents of a change that affects the
   1074      * dependency.
   1075      *
   1076      * @param disableDependents Whether this Preference should disable
   1077      *            its dependents.
   1078      */
   1079     public void notifyDependencyChange(boolean disableDependents) {
   1080         final List<Preference> dependents = mDependents;
   1081 
   1082         if (dependents == null) {
   1083             return;
   1084         }
   1085 
   1086         final int dependentsCount = dependents.size();
   1087         for (int i = 0; i < dependentsCount; i++) {
   1088             dependents.get(i).onDependencyChanged(this, disableDependents);
   1089         }
   1090     }
   1091 
   1092     /**
   1093      * Called when the dependency changes.
   1094      *
   1095      * @param dependency The Preference that this Preference depends on.
   1096      * @param disableDependent Set true to disable this Preference.
   1097      */
   1098     public void onDependencyChanged(Preference dependency, boolean disableDependent) {
   1099         if (mDependencyMet == disableDependent) {
   1100             mDependencyMet = !disableDependent;
   1101 
   1102             // Enabled state can change dependent preferences' states, so notify
   1103             notifyDependencyChange(shouldDisableDependents());
   1104 
   1105             notifyChanged();
   1106         }
   1107     }
   1108 
   1109     /**
   1110      * Checks whether this preference's dependents should currently be
   1111      * disabled.
   1112      *
   1113      * @return True if the dependents should be disabled, otherwise false.
   1114      */
   1115     public boolean shouldDisableDependents() {
   1116         return !isEnabled();
   1117     }
   1118 
   1119     /**
   1120      * Sets the key of a Preference that this Preference will depend on. If that
   1121      * Preference is not set or is off, this Preference will be disabled.
   1122      *
   1123      * @param dependencyKey The key of the Preference that this depends on.
   1124      */
   1125     public void setDependency(String dependencyKey) {
   1126         // Unregister the old dependency, if we had one
   1127         unregisterDependency();
   1128 
   1129         // Register the new
   1130         mDependencyKey = dependencyKey;
   1131         registerDependency();
   1132     }
   1133 
   1134     /**
   1135      * Returns the key of the dependency on this Preference.
   1136      *
   1137      * @return The key of the dependency.
   1138      * @see #setDependency(String)
   1139      */
   1140     public String getDependency() {
   1141         return mDependencyKey;
   1142     }
   1143 
   1144     /**
   1145      * Called when this Preference is being removed from the hierarchy. You
   1146      * should remove any references to this Preference that you know about. Make
   1147      * sure to call through to the superclass implementation.
   1148      */
   1149     protected void onPrepareForRemoval() {
   1150         unregisterDependency();
   1151     }
   1152 
   1153     /**
   1154      * Sets the default value for this Preference, which will be set either if
   1155      * persistence is off or persistence is on and the preference is not found
   1156      * in the persistent storage.
   1157      *
   1158      * @param defaultValue The default value.
   1159      */
   1160     public void setDefaultValue(Object defaultValue) {
   1161         mDefaultValue = defaultValue;
   1162     }
   1163 
   1164     private void dispatchSetInitialValue() {
   1165         // By now, we know if we are persistent.
   1166         final boolean shouldPersist = shouldPersist();
   1167         if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
   1168             if (mDefaultValue != null) {
   1169                 onSetInitialValue(false, mDefaultValue);
   1170             }
   1171         } else {
   1172             onSetInitialValue(true, null);
   1173         }
   1174     }
   1175 
   1176     /**
   1177      * Implement this to set the initial value of the Preference.
   1178      * <p>
   1179      * If <var>restorePersistedValue</var> is true, you should restore the
   1180      * Preference value from the {@link android.content.SharedPreferences}. If
   1181      * <var>restorePersistedValue</var> is false, you should set the Preference
   1182      * value to defaultValue that is given (and possibly store to SharedPreferences
   1183      * if {@link #shouldPersist()} is true).
   1184      * <p>
   1185      * This may not always be called. One example is if it should not persist
   1186      * but there is no default value given.
   1187      *
   1188      * @param restorePersistedValue True to restore the persisted value;
   1189      *            false to use the given <var>defaultValue</var>.
   1190      * @param defaultValue The default value for this Preference. Only use this
   1191      *            if <var>restorePersistedValue</var> is false.
   1192      */
   1193     protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
   1194     }
   1195 
   1196     private void tryCommit(SharedPreferences.Editor editor) {
   1197         if (mPreferenceManager.shouldCommit()) {
   1198             try {
   1199                 editor.apply();
   1200             } catch (AbstractMethodError unused) {
   1201                 // The app injected its own pre-Gingerbread
   1202                 // SharedPreferences.Editor implementation without
   1203                 // an apply method.
   1204                 editor.commit();
   1205             }
   1206         }
   1207     }
   1208 
   1209     /**
   1210      * Attempts to persist a String to the {@link android.content.SharedPreferences}.
   1211      * <p>
   1212      * This will check if this Preference is persistent, get an editor from
   1213      * the {@link PreferenceManager}, put in the string, and check if we should commit (and
   1214      * commit if so).
   1215      *
   1216      * @param value The value to persist.
   1217      * @return True if the Preference is persistent. (This is not whether the
   1218      *         value was persisted, since we may not necessarily commit if there
   1219      *         will be a batch commit later.)
   1220      * @see #getPersistedString(String)
   1221      */
   1222     protected boolean persistString(String value) {
   1223         if (shouldPersist()) {
   1224             // Shouldn't store null
   1225             if (value == getPersistedString(null)) {
   1226                 // It's already there, so the same as persisting
   1227                 return true;
   1228             }
   1229 
   1230             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
   1231             editor.putString(mKey, value);
   1232             tryCommit(editor);
   1233             return true;
   1234         }
   1235         return false;
   1236     }
   1237 
   1238     /**
   1239      * Attempts to get a persisted String from the {@link android.content.SharedPreferences}.
   1240      * <p>
   1241      * This will check if this Preference is persistent, get the SharedPreferences
   1242      * from the {@link PreferenceManager}, and get the value.
   1243      *
   1244      * @param defaultReturnValue The default value to return if either the
   1245      *            Preference is not persistent or the Preference is not in the
   1246      *            shared preferences.
   1247      * @return The value from the SharedPreferences or the default return
   1248      *         value.
   1249      * @see #persistString(String)
   1250      */
   1251     protected String getPersistedString(String defaultReturnValue) {
   1252         if (!shouldPersist()) {
   1253             return defaultReturnValue;
   1254         }
   1255 
   1256         return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
   1257     }
   1258 
   1259     /**
   1260      * Attempts to persist an int to the {@link android.content.SharedPreferences}.
   1261      *
   1262      * @param value The value to persist.
   1263      * @return True if the Preference is persistent. (This is not whether the
   1264      *         value was persisted, since we may not necessarily commit if there
   1265      *         will be a batch commit later.)
   1266      * @see #persistString(String)
   1267      * @see #getPersistedInt(int)
   1268      */
   1269     protected boolean persistInt(int value) {
   1270         if (shouldPersist()) {
   1271             if (value == getPersistedInt(~value)) {
   1272                 // It's already there, so the same as persisting
   1273                 return true;
   1274             }
   1275 
   1276             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
   1277             editor.putInt(mKey, value);
   1278             tryCommit(editor);
   1279             return true;
   1280         }
   1281         return false;
   1282     }
   1283 
   1284     /**
   1285      * Attempts to get a persisted int from the {@link android.content.SharedPreferences}.
   1286      *
   1287      * @param defaultReturnValue The default value to return if either this
   1288      *            Preference is not persistent or this Preference is not in the
   1289      *            SharedPreferences.
   1290      * @return The value from the SharedPreferences or the default return
   1291      *         value.
   1292      * @see #getPersistedString(String)
   1293      * @see #persistInt(int)
   1294      */
   1295     protected int getPersistedInt(int defaultReturnValue) {
   1296         if (!shouldPersist()) {
   1297             return defaultReturnValue;
   1298         }
   1299 
   1300         return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
   1301     }
   1302 
   1303     /**
   1304      * Attempts to persist a float to the {@link android.content.SharedPreferences}.
   1305      *
   1306      * @param value The value to persist.
   1307      * @return True if this Preference is persistent. (This is not whether the
   1308      *         value was persisted, since we may not necessarily commit if there
   1309      *         will be a batch commit later.)
   1310      * @see #persistString(String)
   1311      * @see #getPersistedFloat(float)
   1312      */
   1313     protected boolean persistFloat(float value) {
   1314         if (shouldPersist()) {
   1315             if (value == getPersistedFloat(Float.NaN)) {
   1316                 // It's already there, so the same as persisting
   1317                 return true;
   1318             }
   1319 
   1320             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
   1321             editor.putFloat(mKey, value);
   1322             tryCommit(editor);
   1323             return true;
   1324         }
   1325         return false;
   1326     }
   1327 
   1328     /**
   1329      * Attempts to get a persisted float from the {@link android.content.SharedPreferences}.
   1330      *
   1331      * @param defaultReturnValue The default value to return if either this
   1332      *            Preference is not persistent or this Preference is not in the
   1333      *            SharedPreferences.
   1334      * @return The value from the SharedPreferences or the default return
   1335      *         value.
   1336      * @see #getPersistedString(String)
   1337      * @see #persistFloat(float)
   1338      */
   1339     protected float getPersistedFloat(float defaultReturnValue) {
   1340         if (!shouldPersist()) {
   1341             return defaultReturnValue;
   1342         }
   1343 
   1344         return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
   1345     }
   1346 
   1347     /**
   1348      * Attempts to persist a long to the {@link android.content.SharedPreferences}.
   1349      *
   1350      * @param value The value to persist.
   1351      * @return True if this Preference is persistent. (This is not whether the
   1352      *         value was persisted, since we may not necessarily commit if there
   1353      *         will be a batch commit later.)
   1354      * @see #persistString(String)
   1355      * @see #getPersistedLong(long)
   1356      */
   1357     protected boolean persistLong(long value) {
   1358         if (shouldPersist()) {
   1359             if (value == getPersistedLong(~value)) {
   1360                 // It's already there, so the same as persisting
   1361                 return true;
   1362             }
   1363 
   1364             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
   1365             editor.putLong(mKey, value);
   1366             tryCommit(editor);
   1367             return true;
   1368         }
   1369         return false;
   1370     }
   1371 
   1372     /**
   1373      * Attempts to get a persisted long from the {@link android.content.SharedPreferences}.
   1374      *
   1375      * @param defaultReturnValue The default value to return if either this
   1376      *            Preference is not persistent or this Preference is not in the
   1377      *            SharedPreferences.
   1378      * @return The value from the SharedPreferences or the default return
   1379      *         value.
   1380      * @see #getPersistedString(String)
   1381      * @see #persistLong(long)
   1382      */
   1383     protected long getPersistedLong(long defaultReturnValue) {
   1384         if (!shouldPersist()) {
   1385             return defaultReturnValue;
   1386         }
   1387 
   1388         return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
   1389     }
   1390 
   1391     /**
   1392      * Attempts to persist a boolean to the {@link android.content.SharedPreferences}.
   1393      *
   1394      * @param value The value to persist.
   1395      * @return True if this Preference is persistent. (This is not whether the
   1396      *         value was persisted, since we may not necessarily commit if there
   1397      *         will be a batch commit later.)
   1398      * @see #persistString(String)
   1399      * @see #getPersistedBoolean(boolean)
   1400      */
   1401     protected boolean persistBoolean(boolean value) {
   1402         if (shouldPersist()) {
   1403             if (value == getPersistedBoolean(!value)) {
   1404                 // It's already there, so the same as persisting
   1405                 return true;
   1406             }
   1407 
   1408             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
   1409             editor.putBoolean(mKey, value);
   1410             tryCommit(editor);
   1411             return true;
   1412         }
   1413         return false;
   1414     }
   1415 
   1416     /**
   1417      * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}.
   1418      *
   1419      * @param defaultReturnValue The default value to return if either this
   1420      *            Preference is not persistent or this Preference is not in the
   1421      *            SharedPreferences.
   1422      * @return The value from the SharedPreferences or the default return
   1423      *         value.
   1424      * @see #getPersistedString(String)
   1425      * @see #persistBoolean(boolean)
   1426      */
   1427     protected boolean getPersistedBoolean(boolean defaultReturnValue) {
   1428         if (!shouldPersist()) {
   1429             return defaultReturnValue;
   1430         }
   1431 
   1432         return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
   1433     }
   1434 
   1435     boolean hasSpecifiedLayout() {
   1436         return mHasSpecifiedLayout;
   1437     }
   1438 
   1439     @Override
   1440     public String toString() {
   1441         return getFilterableStringBuilder().toString();
   1442     }
   1443 
   1444     /**
   1445      * Returns the text that will be used to filter this Preference depending on
   1446      * user input.
   1447      * <p>
   1448      * If overridding and calling through to the superclass, make sure to prepend
   1449      * your additions with a space.
   1450      *
   1451      * @return Text as a {@link StringBuilder} that will be used to filter this
   1452      *         preference. By default, this is the title and summary
   1453      *         (concatenated with a space).
   1454      */
   1455     StringBuilder getFilterableStringBuilder() {
   1456         StringBuilder sb = new StringBuilder();
   1457         CharSequence title = getTitle();
   1458         if (!TextUtils.isEmpty(title)) {
   1459             sb.append(title).append(' ');
   1460         }
   1461         CharSequence summary = getSummary();
   1462         if (!TextUtils.isEmpty(summary)) {
   1463             sb.append(summary).append(' ');
   1464         }
   1465         if (sb.length() > 0) {
   1466             // Drop the last space
   1467             sb.setLength(sb.length() - 1);
   1468         }
   1469         return sb;
   1470     }
   1471 
   1472     /**
   1473      * Store this Preference hierarchy's frozen state into the given container.
   1474      *
   1475      * @param container The Bundle in which to save the instance of this Preference.
   1476      *
   1477      * @see #restoreHierarchyState
   1478      * @see #onSaveInstanceState
   1479      */
   1480     public void saveHierarchyState(Bundle container) {
   1481         dispatchSaveInstanceState(container);
   1482     }
   1483 
   1484     /**
   1485      * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children.
   1486      * May be overridden to modify how the save happens for children. For example, some
   1487      * Preference objects may want to not store an instance for their children.
   1488      *
   1489      * @param container The Bundle in which to save the instance of this Preference.
   1490      *
   1491      * @see #saveHierarchyState
   1492      * @see #onSaveInstanceState
   1493      */
   1494     void dispatchSaveInstanceState(Bundle container) {
   1495         if (hasKey()) {
   1496             mBaseMethodCalled = false;
   1497             Parcelable state = onSaveInstanceState();
   1498             if (!mBaseMethodCalled) {
   1499                 throw new IllegalStateException(
   1500                         "Derived class did not call super.onSaveInstanceState()");
   1501             }
   1502             if (state != null) {
   1503                 container.putParcelable(mKey, state);
   1504             }
   1505         }
   1506     }
   1507 
   1508     /**
   1509      * Hook allowing a Preference to generate a representation of its internal
   1510      * state that can later be used to create a new instance with that same
   1511      * state. This state should only contain information that is not persistent
   1512      * or can be reconstructed later.
   1513      *
   1514      * @return A Parcelable object containing the current dynamic state of
   1515      *         this Preference, or null if there is nothing interesting to save.
   1516      *         The default implementation returns null.
   1517      * @see #onRestoreInstanceState
   1518      * @see #saveHierarchyState
   1519      */
   1520     protected Parcelable onSaveInstanceState() {
   1521         mBaseMethodCalled = true;
   1522         return BaseSavedState.EMPTY_STATE;
   1523     }
   1524 
   1525     /**
   1526      * Restore this Preference hierarchy's previously saved state from the given container.
   1527      *
   1528      * @param container The Bundle that holds the previously saved state.
   1529      *
   1530      * @see #saveHierarchyState
   1531      * @see #onRestoreInstanceState
   1532      */
   1533     public void restoreHierarchyState(Bundle container) {
   1534         dispatchRestoreInstanceState(container);
   1535     }
   1536 
   1537     /**
   1538      * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
   1539      * Preference and its children. May be overridden to modify how restoring
   1540      * happens to the children of a Preference. For example, some Preference objects may
   1541      * not want to save state for their children.
   1542      *
   1543      * @param container The Bundle that holds the previously saved state.
   1544      * @see #restoreHierarchyState
   1545      * @see #onRestoreInstanceState
   1546      */
   1547     void dispatchRestoreInstanceState(Bundle container) {
   1548         if (hasKey()) {
   1549             Parcelable state = container.getParcelable(mKey);
   1550             if (state != null) {
   1551                 mBaseMethodCalled = false;
   1552                 onRestoreInstanceState(state);
   1553                 if (!mBaseMethodCalled) {
   1554                     throw new IllegalStateException(
   1555                             "Derived class did not call super.onRestoreInstanceState()");
   1556                 }
   1557             }
   1558         }
   1559     }
   1560 
   1561     /**
   1562      * Hook allowing a Preference to re-apply a representation of its internal
   1563      * state that had previously been generated by {@link #onSaveInstanceState}.
   1564      * This function will never be called with a null state.
   1565      *
   1566      * @param state The saved state that had previously been returned by
   1567      *            {@link #onSaveInstanceState}.
   1568      * @see #onSaveInstanceState
   1569      * @see #restoreHierarchyState
   1570      */
   1571     protected void onRestoreInstanceState(Parcelable state) {
   1572         mBaseMethodCalled = true;
   1573         if (state != BaseSavedState.EMPTY_STATE && state != null) {
   1574             throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
   1575         }
   1576     }
   1577 
   1578     /**
   1579      * A base class for managing the instance state of a {@link Preference}.
   1580      */
   1581     public static class BaseSavedState extends AbsSavedState {
   1582         public BaseSavedState(Parcel source) {
   1583             super(source);
   1584         }
   1585 
   1586         public BaseSavedState(Parcelable superState) {
   1587             super(superState);
   1588         }
   1589 
   1590         public static final Parcelable.Creator<BaseSavedState> CREATOR =
   1591                 new Parcelable.Creator<BaseSavedState>() {
   1592             public BaseSavedState createFromParcel(Parcel in) {
   1593                 return new BaseSavedState(in);
   1594             }
   1595 
   1596             public BaseSavedState[] newArray(int size) {
   1597                 return new BaseSavedState[size];
   1598             }
   1599         };
   1600     }
   1601 
   1602 }
   1603