1/* 2 * Copyright (C) 2015 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 17package android.support.v7.preference; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.support.annotation.ArrayRes; 24import android.support.annotation.NonNull; 25import android.support.v4.content.res.TypedArrayUtils; 26import android.text.TextUtils; 27import android.util.AttributeSet; 28 29/** 30 * A {@link Preference} that displays a list of entries as 31 * a dialog. 32 * <p> 33 * This preference will store a string into the SharedPreferences. This string will be the value 34 * from the {@link #setEntryValues(CharSequence[])} array. 35 * 36 * @attr ref android.R.styleable#ListPreference_entries 37 * @attr ref android.R.styleable#ListPreference_entryValues 38 */ 39public class ListPreference extends DialogPreference { 40 private CharSequence[] mEntries; 41 private CharSequence[] mEntryValues; 42 private String mValue; 43 private String mSummary; 44 private boolean mValueSet; 45 46 public ListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 47 super(context, attrs, defStyleAttr, defStyleRes); 48 49 TypedArray a = context.obtainStyledAttributes( 50 attrs, R.styleable.ListPreference, defStyleAttr, defStyleRes); 51 52 mEntries = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entries, 53 R.styleable.ListPreference_android_entries); 54 55 mEntryValues = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entryValues, 56 R.styleable.ListPreference_android_entryValues); 57 58 a.recycle(); 59 60 /* Retrieve the Preference summary attribute since it's private 61 * in the Preference class. 62 */ 63 a = context.obtainStyledAttributes(attrs, 64 R.styleable.Preference, defStyleAttr, defStyleRes); 65 66 mSummary = TypedArrayUtils.getString(a, R.styleable.Preference_summary, 67 R.styleable.Preference_android_summary); 68 69 a.recycle(); 70 } 71 72 public ListPreference(Context context, AttributeSet attrs, int defStyleAttr) { 73 this(context, attrs, defStyleAttr, 0); 74 } 75 76 public ListPreference(Context context, AttributeSet attrs) { 77 this(context, attrs, R.attr.dialogPreferenceStyle); 78 } 79 80 public ListPreference(Context context) { 81 this(context, null); 82 } 83 84 /** 85 * Sets the human-readable entries to be shown in the list. This will be 86 * shown in subsequent dialogs. 87 * <p> 88 * Each entry must have a corresponding index in 89 * {@link #setEntryValues(CharSequence[])}. 90 * 91 * @param entries The entries. 92 * @see #setEntryValues(CharSequence[]) 93 */ 94 public void setEntries(CharSequence[] entries) { 95 mEntries = entries; 96 } 97 98 /** 99 * @see #setEntries(CharSequence[]) 100 * @param entriesResId The entries array as a resource. 101 */ 102 public void setEntries(@ArrayRes int entriesResId) { 103 setEntries(getContext().getResources().getTextArray(entriesResId)); 104 } 105 106 /** 107 * The list of entries to be shown in the list in subsequent dialogs. 108 * 109 * @return The list as an array. 110 */ 111 public CharSequence[] getEntries() { 112 return mEntries; 113 } 114 115 /** 116 * The array to find the value to save for a preference when an entry from 117 * entries is selected. If a user clicks on the second item in entries, the 118 * second item in this array will be saved to the preference. 119 * 120 * @param entryValues The array to be used as values to save for the preference. 121 */ 122 public void setEntryValues(CharSequence[] entryValues) { 123 mEntryValues = entryValues; 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 setEntryValues(getContext().getResources().getTextArray(entryValuesResId)); 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 CharSequence[] getEntryValues() { 140 return mEntryValues; 141 } 142 143 /** 144 * Sets the value of the key. This should be one of the entries in 145 * {@link #getEntryValues()}. 146 * 147 * @param value The value to set for the key. 148 */ 149 public void setValue(String value) { 150 // Always persist/notify the first time. 151 final boolean changed = !TextUtils.equals(mValue, value); 152 if (changed || !mValueSet) { 153 mValue = value; 154 mValueSet = true; 155 persistString(value); 156 if (changed) { 157 notifyChanged(); 158 } 159 } 160 } 161 162 /** 163 * Returns the summary of this ListPreference. If the summary 164 * has a {@linkplain java.lang.String#format String formatting} 165 * marker in it (i.e. "%s" or "%1$s"), then the current entry 166 * value will be substituted in its place. 167 * 168 * @return the summary with appropriate string substitution 169 */ 170 @Override 171 public CharSequence getSummary() { 172 final CharSequence entry = getEntry(); 173 if (mSummary == null) { 174 return super.getSummary(); 175 } else { 176 return String.format(mSummary, entry == null ? "" : entry); 177 } 178 } 179 180 /** 181 * Sets the summary for this Preference with a CharSequence. 182 * If the summary has a 183 * {@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 when it's retrieved. 186 * 187 * @param summary The summary for the preference. 188 */ 189 @Override 190 public void setSummary(CharSequence summary) { 191 super.setSummary(summary); 192 if (summary == null && mSummary != null) { 193 mSummary = null; 194 } else if (summary != null && !summary.equals(mSummary)) { 195 mSummary = summary.toString(); 196 } 197 } 198 199 /** 200 * Sets the value to the given index from the entry values. 201 * 202 * @param index The index of the value to set. 203 */ 204 public void setValueIndex(int index) { 205 if (mEntryValues != null) { 206 setValue(mEntryValues[index].toString()); 207 } 208 } 209 210 /** 211 * Returns the value of the key. This should be one of the entries in 212 * {@link #getEntryValues()}. 213 * 214 * @return The value of the key. 215 */ 216 public String getValue() { 217 return mValue; 218 } 219 220 /** 221 * Returns the entry corresponding to the current value. 222 * 223 * @return The entry corresponding to the current value, or null. 224 */ 225 public CharSequence getEntry() { 226 int index = getValueIndex(); 227 return index >= 0 && mEntries != null ? mEntries[index] : null; 228 } 229 230 /** 231 * Returns the index of the given value (in the entry values array). 232 * 233 * @param value The value whose index should be returned. 234 * @return The index of the value, or -1 if not found. 235 */ 236 public int findIndexOfValue(String value) { 237 if (value != null && mEntryValues != null) { 238 for (int i = mEntryValues.length - 1; i >= 0; i--) { 239 if (mEntryValues[i].equals(value)) { 240 return i; 241 } 242 } 243 } 244 return -1; 245 } 246 247 private int getValueIndex() { 248 return findIndexOfValue(mValue); 249 } 250 251 @Override 252 protected Object onGetDefaultValue(TypedArray a, int index) { 253 return a.getString(index); 254 } 255 256 @Override 257 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 258 setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue); 259 } 260 261 @Override 262 protected Parcelable onSaveInstanceState() { 263 final Parcelable superState = super.onSaveInstanceState(); 264 if (isPersistent()) { 265 // No need to save instance state since it's persistent 266 return superState; 267 } 268 269 final SavedState myState = new SavedState(superState); 270 myState.value = getValue(); 271 return myState; 272 } 273 274 @Override 275 protected void onRestoreInstanceState(Parcelable state) { 276 if (state == null || !state.getClass().equals(SavedState.class)) { 277 // Didn't save state for us in onSaveInstanceState 278 super.onRestoreInstanceState(state); 279 return; 280 } 281 282 SavedState myState = (SavedState) state; 283 super.onRestoreInstanceState(myState.getSuperState()); 284 setValue(myState.value); 285 } 286 287 private static class SavedState extends BaseSavedState { 288 String value; 289 290 public SavedState(Parcel source) { 291 super(source); 292 value = source.readString(); 293 } 294 295 @Override 296 public void writeToParcel(@NonNull Parcel dest, int flags) { 297 super.writeToParcel(dest, flags); 298 dest.writeString(value); 299 } 300 301 public SavedState(Parcelable superState) { 302 super(superState); 303 } 304 305 public static final Parcelable.Creator<SavedState> CREATOR = 306 new Parcelable.Creator<SavedState>() { 307 public SavedState createFromParcel(Parcel in) { 308 return new SavedState(in); 309 } 310 311 public SavedState[] newArray(int size) { 312 return new SavedState[size]; 313 } 314 }; 315 } 316 317} 318