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