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 = values;
    129 
    130         persistStringSet(values);
    131     }
    132 
    133     /**
    134      * Retrieves the current value of the key.
    135      */
    136     public Set<String> getValues() {
    137         return mValues;
    138     }
    139 
    140     /**
    141      * Returns the index of the given value (in the entry values array).
    142      *
    143      * @param value The value whose index should be returned.
    144      * @return The index of the value, or -1 if not found.
    145      */
    146     public int findIndexOfValue(String value) {
    147         if (value != null && mEntryValues != null) {
    148             for (int i = mEntryValues.length - 1; i >= 0; i--) {
    149                 if (mEntryValues[i].equals(value)) {
    150                     return i;
    151                 }
    152             }
    153         }
    154         return -1;
    155     }
    156 
    157     @Override
    158     protected void onPrepareDialogBuilder(Builder builder) {
    159         super.onPrepareDialogBuilder(builder);
    160 
    161         if (mEntries == null || mEntryValues == null) {
    162             throw new IllegalStateException(
    163                     "MultiSelectListPreference requires an entries array and " +
    164                     "an entryValues array.");
    165         }
    166 
    167         boolean[] checkedItems = getSelectedItems();
    168         builder.setMultiChoiceItems(mEntries, checkedItems,
    169                 new DialogInterface.OnMultiChoiceClickListener() {
    170                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
    171                         if (isChecked) {
    172                             mPreferenceChanged |= mNewValues.add(mEntryValues[which].toString());
    173                         } else {
    174                             mPreferenceChanged |= mNewValues.remove(mEntryValues[which].toString());
    175                         }
    176                     }
    177                 });
    178         mNewValues.clear();
    179         mNewValues.addAll(mValues);
    180     }
    181 
    182     private boolean[] getSelectedItems() {
    183         final CharSequence[] entries = mEntryValues;
    184         final int entryCount = entries.length;
    185         final Set<String> values = mValues;
    186         boolean[] result = new boolean[entryCount];
    187 
    188         for (int i = 0; i < entryCount; i++) {
    189             result[i] = values.contains(entries[i].toString());
    190         }
    191 
    192         return result;
    193     }
    194 
    195     @Override
    196     protected void onDialogClosed(boolean positiveResult) {
    197         super.onDialogClosed(positiveResult);
    198 
    199         if (positiveResult && mPreferenceChanged) {
    200             final Set<String> values = mNewValues;
    201             if (callChangeListener(values)) {
    202                 setValues(values);
    203             }
    204         }
    205         mPreferenceChanged = false;
    206     }
    207 
    208     @Override
    209     protected Object onGetDefaultValue(TypedArray a, int index) {
    210         final CharSequence[] defaultValues = a.getTextArray(index);
    211         final int valueCount = defaultValues.length;
    212         final Set<String> result = new HashSet<String>();
    213 
    214         for (int i = 0; i < valueCount; i++) {
    215             result.add(defaultValues[i].toString());
    216         }
    217 
    218         return result;
    219     }
    220 
    221     @Override
    222     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    223         setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
    224     }
    225 
    226     @Override
    227     protected Parcelable onSaveInstanceState() {
    228         final Parcelable superState = super.onSaveInstanceState();
    229         if (isPersistent()) {
    230             // No need to save instance state
    231             return superState;
    232         }
    233 
    234         final SavedState myState = new SavedState(superState);
    235         myState.values = getValues();
    236         return myState;
    237     }
    238 
    239     private static class SavedState extends BaseSavedState {
    240         Set<String> values;
    241 
    242         public SavedState(Parcel source) {
    243             super(source);
    244             values = new HashSet<String>();
    245             String[] strings = source.readStringArray();
    246 
    247             final int stringCount = strings.length;
    248             for (int i = 0; i < stringCount; i++) {
    249                 values.add(strings[i]);
    250             }
    251         }
    252 
    253         public SavedState(Parcelable superState) {
    254             super(superState);
    255         }
    256 
    257         @Override
    258         public void writeToParcel(Parcel dest, int flags) {
    259             super.writeToParcel(dest, flags);
    260             dest.writeStringArray(values.toArray(new String[0]));
    261         }
    262 
    263         public static final Parcelable.Creator<SavedState> CREATOR =
    264                 new Parcelable.Creator<SavedState>() {
    265             public SavedState createFromParcel(Parcel in) {
    266                 return new SavedState(in);
    267             }
    268 
    269             public SavedState[] newArray(int size) {
    270                 return new SavedState[size];
    271             }
    272         };
    273     }
    274 }
    275