Home | History | Annotate | Download | only in preference
      1 /*
      2  * Copyright (C) 2012 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 java.util.Arrays;
     20 
     21 import android.annotation.ArrayRes;
     22 import android.app.AlertDialog.Builder;
     23 import android.content.Context;
     24 import android.content.DialogInterface;
     25 import android.content.res.TypedArray;
     26 import android.os.Parcel;
     27 import android.os.Parcelable;
     28 import android.util.AttributeSet;
     29 
     30 /**
     31  * @hide
     32  * A {@link Preference} that displays a list of entries as
     33  * a dialog which allow the user to toggle each individually on and off.
     34  *
     35  * @attr ref android.R.styleable#ListPreference_entries
     36  * @attr ref android.R.styleable#ListPreference_entryValues
     37  */
     38 public class MultiCheckPreference extends DialogPreference {
     39     private CharSequence[] mEntries;
     40     private String[] mEntryValues;
     41     private boolean[] mSetValues;
     42     private boolean[] mOrigValues;
     43     private String mSummary;
     44 
     45     public MultiCheckPreference(
     46             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         if (mEntries != null) {
     53             setEntries(mEntries);
     54         }
     55         setEntryValuesCS(a.getTextArray(
     56                 com.android.internal.R.styleable.ListPreference_entryValues));
     57         a.recycle();
     58 
     59         /* Retrieve the Preference summary attribute since it's private
     60          * in the Preference class.
     61          */
     62         a = context.obtainStyledAttributes(attrs,
     63                 com.android.internal.R.styleable.Preference, 0, 0);
     64         mSummary = a.getString(com.android.internal.R.styleable.Preference_summary);
     65         a.recycle();
     66     }
     67 
     68     public MultiCheckPreference(Context context, AttributeSet attrs, int defStyleAttr) {
     69         this(context, attrs, defStyleAttr, 0);
     70     }
     71 
     72     public MultiCheckPreference(Context context, AttributeSet attrs) {
     73         this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
     74     }
     75 
     76     public MultiCheckPreference(Context context) {
     77         this(context, null);
     78     }
     79 
     80     /**
     81      * Sets the human-readable entries to be shown in the list. This will be
     82      * shown in subsequent dialogs.
     83      * <p>
     84      * Each entry must have a corresponding index in
     85      * {@link #setEntryValues(CharSequence[])}.
     86      *
     87      * @param entries The entries.
     88      * @see #setEntryValues(CharSequence[])
     89      */
     90     public void setEntries(CharSequence[] entries) {
     91         mEntries = entries;
     92         mSetValues = new boolean[entries.length];
     93         mOrigValues = new boolean[entries.length];
     94     }
     95 
     96     /**
     97      * @see #setEntries(CharSequence[])
     98      * @param entriesResId The entries array as a resource.
     99      */
    100     public void setEntries(@ArrayRes int entriesResId) {
    101         setEntries(getContext().getResources().getTextArray(entriesResId));
    102     }
    103 
    104     /**
    105      * The list of entries to be shown in the list in subsequent dialogs.
    106      *
    107      * @return The list as an array.
    108      */
    109     public CharSequence[] getEntries() {
    110         return mEntries;
    111     }
    112 
    113     /**
    114      * The array to find the value to save for a preference when an entry from
    115      * entries is selected. If a user clicks on the second item in entries, the
    116      * second item in this array will be saved to the preference.
    117      *
    118      * @param entryValues The array to be used as values to save for the preference.
    119      */
    120     public void setEntryValues(String[] entryValues) {
    121         mEntryValues = entryValues;
    122         Arrays.fill(mSetValues, false);
    123         Arrays.fill(mOrigValues, false);
    124     }
    125 
    126     /**
    127      * @see #setEntryValues(CharSequence[])
    128      * @param entryValuesResId The entry values array as a resource.
    129      */
    130     public void setEntryValues(@ArrayRes int entryValuesResId) {
    131         setEntryValuesCS(getContext().getResources().getTextArray(entryValuesResId));
    132     }
    133 
    134     private void setEntryValuesCS(CharSequence[] values) {
    135         setValues(null);
    136         if (values != null) {
    137             mEntryValues = new String[values.length];
    138             for (int i=0; i<values.length; i++) {
    139                 mEntryValues[i] = values[i].toString();
    140             }
    141         }
    142     }
    143 
    144     /**
    145      * Returns the array of values to be saved for the preference.
    146      *
    147      * @return The array of values.
    148      */
    149     public String[] getEntryValues() {
    150         return mEntryValues;
    151     }
    152 
    153     /**
    154      * Get the boolean state of a given value.
    155      */
    156     public boolean getValue(int index) {
    157         return mSetValues[index];
    158     }
    159 
    160     /**
    161      * Set the boolean state of a given value.
    162      */
    163     public void setValue(int index, boolean state) {
    164         mSetValues[index] = state;
    165     }
    166 
    167     /**
    168      * Sets the current values.
    169      */
    170     public void setValues(boolean[] values) {
    171         if (mSetValues != null) {
    172             Arrays.fill(mSetValues, false);
    173             Arrays.fill(mOrigValues, false);
    174             if (values != null) {
    175                 System.arraycopy(values, 0, mSetValues, 0,
    176                         values.length < mSetValues.length ? values.length : mSetValues.length);
    177             }
    178         }
    179     }
    180 
    181     /**
    182      * Returns the summary of this ListPreference. If the summary
    183      * has a {@linkplain java.lang.String#format String formatting}
    184      * marker in it (i.e. "%s" or "%1$s"), then the current entry
    185      * value will be substituted in its place.
    186      *
    187      * @return the summary with appropriate string substitution
    188      */
    189     @Override
    190     public CharSequence getSummary() {
    191         if (mSummary == null) {
    192             return super.getSummary();
    193         } else {
    194             return mSummary;
    195         }
    196     }
    197 
    198     /**
    199      * Sets the summary for this Preference with a CharSequence.
    200      * If the summary has a
    201      * {@linkplain java.lang.String#format String formatting}
    202      * marker in it (i.e. "%s" or "%1$s"), then the current entry
    203      * value will be substituted in its place when it's retrieved.
    204      *
    205      * @param summary The summary for the preference.
    206      */
    207     @Override
    208     public void setSummary(CharSequence summary) {
    209         super.setSummary(summary);
    210         if (summary == null && mSummary != null) {
    211             mSummary = null;
    212         } else if (summary != null && !summary.equals(mSummary)) {
    213             mSummary = summary.toString();
    214         }
    215     }
    216 
    217     /**
    218      * Returns the currently selected values.
    219      */
    220     public boolean[] getValues() {
    221         return mSetValues;
    222     }
    223 
    224     /**
    225      * Returns the index of the given value (in the entry values array).
    226      *
    227      * @param value The value whose index should be returned.
    228      * @return The index of the value, or -1 if not found.
    229      */
    230     public int findIndexOfValue(String value) {
    231         if (value != null && mEntryValues != null) {
    232             for (int i = mEntryValues.length - 1; i >= 0; i--) {
    233                 if (mEntryValues[i].equals(value)) {
    234                     return i;
    235                 }
    236             }
    237         }
    238         return -1;
    239     }
    240 
    241     @Override
    242     protected void onPrepareDialogBuilder(Builder builder) {
    243         super.onPrepareDialogBuilder(builder);
    244 
    245         if (mEntries == null || mEntryValues == null) {
    246             throw new IllegalStateException(
    247                     "ListPreference requires an entries array and an entryValues array.");
    248         }
    249 
    250         mOrigValues = Arrays.copyOf(mSetValues, mSetValues.length);
    251         builder.setMultiChoiceItems(mEntries, mSetValues,
    252                 new DialogInterface.OnMultiChoiceClickListener() {
    253                     @Override
    254                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
    255                         mSetValues[which] = isChecked;
    256                     }
    257         });
    258     }
    259 
    260     @Override
    261     protected void onDialogClosed(boolean positiveResult) {
    262         super.onDialogClosed(positiveResult);
    263 
    264         if (positiveResult) {
    265             if (callChangeListener(getValues())) {
    266                 return;
    267             }
    268         }
    269         System.arraycopy(mOrigValues, 0, mSetValues, 0, mSetValues.length);
    270     }
    271 
    272     @Override
    273     protected Object onGetDefaultValue(TypedArray a, int index) {
    274         return a.getString(index);
    275     }
    276 
    277     @Override
    278     protected void onSetInitialValue(boolean restoreValue, Object 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.values = getValues();
    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         setValues(myState.values);
    305     }
    306 
    307     private static class SavedState extends BaseSavedState {
    308         boolean[] values;
    309 
    310         public SavedState(Parcel source) {
    311             super(source);
    312             values = source.createBooleanArray();
    313         }
    314 
    315         @Override
    316         public void writeToParcel(Parcel dest, int flags) {
    317             super.writeToParcel(dest, flags);
    318             dest.writeBooleanArray(values);
    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