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