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