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