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 MultiCheckPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 MultiCheckPreference(Context context, AttributeSet attrs, int defStyleAttr)68 public MultiCheckPreference(Context context, AttributeSet attrs, int defStyleAttr) { 69 this(context, attrs, defStyleAttr, 0); 70 } 71 MultiCheckPreference(Context context, AttributeSet attrs)72 public MultiCheckPreference(Context context, AttributeSet attrs) { 73 this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle); 74 } 75 MultiCheckPreference(Context context)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 */ setEntries(CharSequence[] entries)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 */ setEntries(@rrayRes int entriesResId)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 */ getEntries()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 */ setEntryValues(String[] entryValues)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 */ setEntryValues(@rrayRes int entryValuesResId)130 public void setEntryValues(@ArrayRes int entryValuesResId) { 131 setEntryValuesCS(getContext().getResources().getTextArray(entryValuesResId)); 132 } 133 setEntryValuesCS(CharSequence[] values)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 */ getEntryValues()149 public String[] getEntryValues() { 150 return mEntryValues; 151 } 152 153 /** 154 * Get the boolean state of a given value. 155 */ getValue(int index)156 public boolean getValue(int index) { 157 return mSetValues[index]; 158 } 159 160 /** 161 * Set the boolean state of a given value. 162 */ setValue(int index, boolean state)163 public void setValue(int index, boolean state) { 164 mSetValues[index] = state; 165 } 166 167 /** 168 * Sets the current values. 169 */ setValues(boolean[] values)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 getSummary()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 setSummary(CharSequence summary)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 */ getValues()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 */ findIndexOfValue(String value)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 onPrepareDialogBuilder(Builder builder)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 onDialogClosed(boolean positiveResult)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 onGetDefaultValue(TypedArray a, int index)273 protected Object onGetDefaultValue(TypedArray a, int index) { 274 return a.getString(index); 275 } 276 277 @Override onSetInitialValue(boolean restoreValue, Object defaultValue)278 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 279 } 280 281 @Override onSaveInstanceState()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 onRestoreInstanceState(Parcelable state)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 SavedState(Parcel source)310 public SavedState(Parcel source) { 311 super(source); 312 values = source.createBooleanArray(); 313 } 314 315 @Override writeToParcel(Parcel dest, int flags)316 public void writeToParcel(Parcel dest, int flags) { 317 super.writeToParcel(dest, flags); 318 dest.writeBooleanArray(values); 319 } 320 SavedState(Parcelable superState)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