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