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