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