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.ArrayRes;
     20 import android.app.AlertDialog.Builder;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.res.TypedArray;
     24 import android.os.Parcel;
     25 import android.os.Parcelable;
     26 import android.text.TextUtils;
     27 import android.util.AttributeSet;
     28 
     29 /**
     30  * A {@link Preference} that displays a list of entries as
     31  * a dialog.
     32  * <p>
     33  * This preference will store a string into the SharedPreferences. This string will be the value
     34  * from the {@link #setEntryValues(CharSequence[])} array.
     35  *
     36  * @attr ref android.R.styleable#ListPreference_entries
     37  * @attr ref android.R.styleable#ListPreference_entryValues
     38  */
     39 public class ListPreference extends DialogPreference {
     40     private CharSequence[] mEntries;
     41     private CharSequence[] mEntryValues;
     42     private String mValue;
     43     private String mSummary;
     44     private int mClickedDialogEntryIndex;
     45     private boolean mValueSet;
     46 
     47     public ListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     48         super(context, attrs, defStyleAttr, defStyleRes);
     49 
     50         TypedArray a = context.obtainStyledAttributes(
     51                 attrs, com.android.internal.R.styleable.ListPreference, defStyleAttr, defStyleRes);
     52         mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries);
     53         mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues);
     54         a.recycle();
     55 
     56         /* Retrieve the Preference summary attribute since it's private
     57          * in the Preference class.
     58          */
     59         a = context.obtainStyledAttributes(attrs,
     60                 com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes);
     61         mSummary = a.getString(com.android.internal.R.styleable.Preference_summary);
     62         a.recycle();
     63     }
     64 
     65     public ListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
     66         this(context, attrs, defStyleAttr, 0);
     67     }
     68 
     69     public ListPreference(Context context, AttributeSet attrs) {
     70         this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
     71     }
     72 
     73     public ListPreference(Context context) {
     74         this(context, null);
     75     }
     76 
     77     /**
     78      * Sets the human-readable entries to be shown in the list. This will be
     79      * shown in subsequent dialogs.
     80      * <p>
     81      * Each entry must have a corresponding index in
     82      * {@link #setEntryValues(CharSequence[])}.
     83      *
     84      * @param entries The entries.
     85      * @see #setEntryValues(CharSequence[])
     86      */
     87     public void setEntries(CharSequence[] entries) {
     88         mEntries = entries;
     89     }
     90 
     91     /**
     92      * @see #setEntries(CharSequence[])
     93      * @param entriesResId The entries array as a resource.
     94      */
     95     public void setEntries(@ArrayRes int entriesResId) {
     96         setEntries(getContext().getResources().getTextArray(entriesResId));
     97     }
     98 
     99     /**
    100      * The list of entries to be shown in the list in subsequent dialogs.
    101      *
    102      * @return The list as an array.
    103      */
    104     public CharSequence[] getEntries() {
    105         return mEntries;
    106     }
    107 
    108     /**
    109      * The array to find the value to save for a preference when an entry from
    110      * entries is selected. If a user clicks on the second item in entries, the
    111      * second item in this array will be saved to the preference.
    112      *
    113      * @param entryValues The array to be used as values to save for the preference.
    114      */
    115     public void setEntryValues(CharSequence[] entryValues) {
    116         mEntryValues = entryValues;
    117     }
    118 
    119     /**
    120      * @see #setEntryValues(CharSequence[])
    121      * @param entryValuesResId The entry values array as a resource.
    122      */
    123     public void setEntryValues(@ArrayRes int entryValuesResId) {
    124         setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
    125     }
    126 
    127     /**
    128      * Returns the array of values to be saved for the preference.
    129      *
    130      * @return The array of values.
    131      */
    132     public CharSequence[] getEntryValues() {
    133         return mEntryValues;
    134     }
    135 
    136     /**
    137      * Sets the value of the key. This should be one of the entries in
    138      * {@link #getEntryValues()}.
    139      *
    140      * @param value The value to set for the key.
    141      */
    142     public void setValue(String value) {
    143         // Always persist/notify the first time.
    144         final boolean changed = !TextUtils.equals(mValue, value);
    145         if (changed || !mValueSet) {
    146             mValue = value;
    147             mValueSet = true;
    148             persistString(value);
    149             if (changed) {
    150                 notifyChanged();
    151             }
    152         }
    153     }
    154 
    155     /**
    156      * Returns the summary of this ListPreference. If the summary
    157      * has a {@linkplain java.lang.String#format String formatting}
    158      * marker in it (i.e. "%s" or "%1$s"), then the current entry
    159      * value will be substituted in its place.
    160      *
    161      * @return the summary with appropriate string substitution
    162      */
    163     @Override
    164     public CharSequence getSummary() {
    165         final CharSequence entry = getEntry();
    166         if (mSummary == null) {
    167             return super.getSummary();
    168         } else {
    169             return String.format(mSummary, entry == null ? "" : entry);
    170         }
    171     }
    172 
    173     /**
    174      * Sets the summary for this Preference with a CharSequence.
    175      * If the summary has a
    176      * {@linkplain java.lang.String#format String formatting}
    177      * marker in it (i.e. "%s" or "%1$s"), then the current entry
    178      * value will be substituted in its place when it's retrieved.
    179      *
    180      * @param summary The summary for the preference.
    181      */
    182     @Override
    183     public void setSummary(CharSequence summary) {
    184         super.setSummary(summary);
    185         if (summary == null && mSummary != null) {
    186             mSummary = null;
    187         } else if (summary != null && !summary.equals(mSummary)) {
    188             mSummary = summary.toString();
    189         }
    190     }
    191 
    192     /**
    193      * Sets the value to the given index from the entry values.
    194      *
    195      * @param index The index of the value to set.
    196      */
    197     public void setValueIndex(int index) {
    198         if (mEntryValues != null) {
    199             setValue(mEntryValues[index].toString());
    200         }
    201     }
    202 
    203     /**
    204      * Returns the value of the key. This should be one of the entries in
    205      * {@link #getEntryValues()}.
    206      *
    207      * @return The value of the key.
    208      */
    209     public String getValue() {
    210         return mValue;
    211     }
    212 
    213     /**
    214      * Returns the entry corresponding to the current value.
    215      *
    216      * @return The entry corresponding to the current value, or null.
    217      */
    218     public CharSequence getEntry() {
    219         int index = getValueIndex();
    220         return index >= 0 && mEntries != null ? mEntries[index] : null;
    221     }
    222 
    223     /**
    224      * Returns the index of the given value (in the entry values array).
    225      *
    226      * @param value The value whose index should be returned.
    227      * @return The index of the value, or -1 if not found.
    228      */
    229     public int findIndexOfValue(String value) {
    230         if (value != null && mEntryValues != null) {
    231             for (int i = mEntryValues.length - 1; i >= 0; i--) {
    232                 if (mEntryValues[i].equals(value)) {
    233                     return i;
    234                 }
    235             }
    236         }
    237         return -1;
    238     }
    239 
    240     private int getValueIndex() {
    241         return findIndexOfValue(mValue);
    242     }
    243 
    244     @Override
    245     protected void onPrepareDialogBuilder(Builder builder) {
    246         super.onPrepareDialogBuilder(builder);
    247 
    248         if (mEntries == null || mEntryValues == null) {
    249             throw new IllegalStateException(
    250                     "ListPreference requires an entries array and an entryValues array.");
    251         }
    252 
    253         mClickedDialogEntryIndex = getValueIndex();
    254         builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
    255                 new DialogInterface.OnClickListener() {
    256                     public void onClick(DialogInterface dialog, int which) {
    257                         mClickedDialogEntryIndex = which;
    258 
    259                         /*
    260                          * Clicking on an item simulates the positive button
    261                          * click, and dismisses the dialog.
    262                          */
    263                         ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
    264                         dialog.dismiss();
    265                     }
    266         });
    267 
    268         /*
    269          * The typical interaction for list-based dialogs is to have
    270          * click-on-an-item dismiss the dialog instead of the user having to
    271          * press 'Ok'.
    272          */
    273         builder.setPositiveButton(null, null);
    274     }
    275 
    276     @Override
    277     protected void onDialogClosed(boolean positiveResult) {
    278         super.onDialogClosed(positiveResult);
    279 
    280         if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
    281             String value = mEntryValues[mClickedDialogEntryIndex].toString();
    282             if (callChangeListener(value)) {
    283                 setValue(value);
    284             }
    285         }
    286     }
    287 
    288     @Override
    289     protected Object onGetDefaultValue(TypedArray a, int index) {
    290         return a.getString(index);
    291     }
    292 
    293     @Override
    294     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    295         setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
    296     }
    297 
    298     @Override
    299     protected Parcelable onSaveInstanceState() {
    300         final Parcelable superState = super.onSaveInstanceState();
    301         if (isPersistent()) {
    302             // No need to save instance state since it's persistent
    303             return superState;
    304         }
    305 
    306         final SavedState myState = new SavedState(superState);
    307         myState.value = getValue();
    308         return myState;
    309     }
    310 
    311     @Override
    312     protected void onRestoreInstanceState(Parcelable state) {
    313         if (state == null || !state.getClass().equals(SavedState.class)) {
    314             // Didn't save state for us in onSaveInstanceState
    315             super.onRestoreInstanceState(state);
    316             return;
    317         }
    318 
    319         SavedState myState = (SavedState) state;
    320         super.onRestoreInstanceState(myState.getSuperState());
    321         setValue(myState.value);
    322     }
    323 
    324     private static class SavedState extends BaseSavedState {
    325         String value;
    326 
    327         public SavedState(Parcel source) {
    328             super(source);
    329             value = source.readString();
    330         }
    331 
    332         @Override
    333         public void writeToParcel(Parcel dest, int flags) {
    334             super.writeToParcel(dest, flags);
    335             dest.writeString(value);
    336         }
    337 
    338         public SavedState(Parcelable superState) {
    339             super(superState);
    340         }
    341 
    342         public static final Parcelable.Creator<SavedState> CREATOR =
    343                 new Parcelable.Creator<SavedState>() {
    344             public SavedState createFromParcel(Parcel in) {
    345                 return new SavedState(in);
    346             }
    347 
    348             public SavedState[] newArray(int size) {
    349                 return new SavedState[size];
    350             }
    351         };
    352     }
    353 
    354 }
    355