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