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