Home | History | Annotate | Download | only in preference
      1 /*
      2  * Copyright (C) 2010 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.util.AttributeSet;
     26 
     27 import java.util.HashSet;
     28 import java.util.Set;
     29 
     30 /**
     31  * A {@link Preference} that displays a list of entries as
     32  * a dialog.
     33  * <p>
     34  * This preference will store a set of strings into the SharedPreferences.
     35  * This set will contain one or more values from the
     36  * {@link #setEntryValues(CharSequence[])} array.
     37  *
     38  * @attr ref android.R.styleable#MultiSelectListPreference_entries
     39  * @attr ref android.R.styleable#MultiSelectListPreference_entryValues
     40  */
     41 public class MultiSelectListPreference extends DialogPreference {
     42     private CharSequence[] mEntries;
     43     private CharSequence[] mEntryValues;
     44     private Set<String> mValues = new HashSet<String>();
     45     private Set<String> mNewValues = new HashSet<String>();
     46     private boolean mPreferenceChanged;
     47 
     48     public MultiSelectListPreference(Context context, AttributeSet attrs) {
     49         super(context, attrs);
     50 
     51         TypedArray a = context.obtainStyledAttributes(attrs,
     52                 com.android.internal.R.styleable.MultiSelectListPreference, 0, 0);
     53         mEntries = a.getTextArray(com.android.internal.R.styleable.MultiSelectListPreference_entries);
     54         mEntryValues = a.getTextArray(com.android.internal.R.styleable.MultiSelectListPreference_entryValues);
     55         a.recycle();
     56     }
     57 
     58     public MultiSelectListPreference(Context context) {
     59         this(context, null);
     60     }
     61 
     62     /**
     63      * Sets the human-readable entries to be shown in the list. This will be
     64      * shown in subsequent dialogs.
     65      * <p>
     66      * Each entry must have a corresponding index in
     67      * {@link #setEntryValues(CharSequence[])}.
     68      *
     69      * @param entries The entries.
     70      * @see #setEntryValues(CharSequence[])
     71      */
     72     public void setEntries(CharSequence[] entries) {
     73         mEntries = entries;
     74     }
     75 
     76     /**
     77      * @see #setEntries(CharSequence[])
     78      * @param entriesResId The entries array as a resource.
     79      */
     80     public void setEntries(int entriesResId) {
     81         setEntries(getContext().getResources().getTextArray(entriesResId));
     82     }
     83 
     84     /**
     85      * The list of entries to be shown in the list in subsequent dialogs.
     86      *
     87      * @return The list as an array.
     88      */
     89     public CharSequence[] getEntries() {
     90         return mEntries;
     91     }
     92 
     93     /**
     94      * The array to find the value to save for a preference when an entry from
     95      * entries is selected. If a user clicks on the second item in entries, the
     96      * second item in this array will be saved to the preference.
     97      *
     98      * @param entryValues The array to be used as values to save for the preference.
     99      */
    100     public void setEntryValues(CharSequence[] entryValues) {
    101         mEntryValues = entryValues;
    102     }
    103 
    104     /**
    105      * @see #setEntryValues(CharSequence[])
    106      * @param entryValuesResId The entry values array as a resource.
    107      */
    108     public void setEntryValues(int entryValuesResId) {
    109         setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
    110     }
    111 
    112     /**
    113      * Returns the array of values to be saved for the preference.
    114      *
    115      * @return The array of values.
    116      */
    117     public CharSequence[] getEntryValues() {
    118         return mEntryValues;
    119     }
    120 
    121     /**
    122      * Sets the value of the key. This should contain entries in
    123      * {@link #getEntryValues()}.
    124      *
    125      * @param values The values to set for the key.
    126      */
    127     public void setValues(Set<String> values) {
    128         mValues.clear();
    129         mValues.addAll(values);
    130 
    131         persistStringSet(values);
    132     }
    133 
    134     /**
    135      * Retrieves the current value of the key.
    136      */
    137     public Set<String> getValues() {
    138         return mValues;
    139     }
    140 
    141     /**
    142      * Returns the index of the given value (in the entry values array).
    143      *
    144      * @param value The value whose index should be returned.
    145      * @return The index of the value, or -1 if not found.
    146      */
    147     public int findIndexOfValue(String value) {
    148         if (value != null && mEntryValues != null) {
    149             for (int i = mEntryValues.length - 1; i >= 0; i--) {
    150                 if (mEntryValues[i].equals(value)) {
    151                     return i;
    152                 }
    153             }
    154         }
    155         return -1;
    156     }
    157 
    158     @Override
    159     protected void onPrepareDialogBuilder(Builder builder) {
    160         super.onPrepareDialogBuilder(builder);
    161 
    162         if (mEntries == null || mEntryValues == null) {
    163             throw new IllegalStateException(
    164                     "MultiSelectListPreference requires an entries array and " +
    165                     "an entryValues array.");
    166         }
    167 
    168         boolean[] checkedItems = getSelectedItems();
    169         builder.setMultiChoiceItems(mEntries, checkedItems,
    170                 new DialogInterface.OnMultiChoiceClickListener() {
    171                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
    172                         if (isChecked) {
    173                             mPreferenceChanged |= mNewValues.add(mEntryValues[which].toString());
    174                         } else {
    175                             mPreferenceChanged |= mNewValues.remove(mEntryValues[which].toString());
    176                         }
    177                     }
    178                 });
    179         mNewValues.clear();
    180         mNewValues.addAll(mValues);
    181     }
    182 
    183     private boolean[] getSelectedItems() {
    184         final CharSequence[] entries = mEntryValues;
    185         final int entryCount = entries.length;
    186         final Set<String> values = mValues;
    187         boolean[] result = new boolean[entryCount];
    188 
    189         for (int i = 0; i < entryCount; i++) {
    190             result[i] = values.contains(entries[i].toString());
    191         }
    192 
    193         return result;
    194     }
    195 
    196     @Override
    197     protected void onDialogClosed(boolean positiveResult) {
    198         super.onDialogClosed(positiveResult);
    199 
    200         if (positiveResult && mPreferenceChanged) {
    201             final Set<String> values = mNewValues;
    202             if (callChangeListener(values)) {
    203                 setValues(values);
    204             }
    205         }
    206         mPreferenceChanged = false;
    207     }
    208 
    209     @Override
    210     protected Object onGetDefaultValue(TypedArray a, int index) {
    211         final CharSequence[] defaultValues = a.getTextArray(index);
    212         final int valueCount = defaultValues.length;
    213         final Set<String> result = new HashSet<String>();
    214 
    215         for (int i = 0; i < valueCount; i++) {
    216             result.add(defaultValues[i].toString());
    217         }
    218 
    219         return result;
    220     }
    221 
    222     @Override
    223     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    224         setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
    225     }
    226 
    227     @Override
    228     protected Parcelable onSaveInstanceState() {
    229         final Parcelable superState = super.onSaveInstanceState();
    230         if (isPersistent()) {
    231             // No need to save instance state
    232             return superState;
    233         }
    234 
    235         final SavedState myState = new SavedState(superState);
    236         myState.values = getValues();
    237         return myState;
    238     }
    239 
    240     private static class SavedState extends BaseSavedState {
    241         Set<String> values;
    242 
    243         public SavedState(Parcel source) {
    244             super(source);
    245             values = new HashSet<String>();
    246             String[] strings = source.readStringArray();
    247 
    248             final int stringCount = strings.length;
    249             for (int i = 0; i < stringCount; i++) {
    250                 values.add(strings[i]);
    251             }
    252         }
    253 
    254         public SavedState(Parcelable superState) {
    255             super(superState);
    256         }
    257 
    258         @Override
    259         public void writeToParcel(Parcel dest, int flags) {
    260             super.writeToParcel(dest, flags);
    261             dest.writeStringArray(values.toArray(new String[0]));
    262         }
    263 
    264         public static final Parcelable.Creator<SavedState> CREATOR =
    265                 new Parcelable.Creator<SavedState>() {
    266             public SavedState createFromParcel(Parcel in) {
    267                 return new SavedState(in);
    268             }
    269 
    270             public SavedState[] newArray(int size) {
    271                 return new SavedState[size];
    272             }
    273         };
    274     }
    275 }
    276