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