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