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