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