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 import android.annotation.ArrayRes; 20 import android.app.AlertDialog.Builder; 21 import android.compat.annotation.UnsupportedAppUsage; 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.text.TextUtils; 28 import android.util.AttributeSet; 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 ref android.R.styleable#ListPreference_entries 38 * @attr ref android.R.styleable#ListPreference_entryValues 39 * 40 * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> 41 * <a href="{@docRoot}reference/androidx/preference/package-summary.html"> 42 * Preference Library</a> for consistent behavior across all devices. For more information on 43 * using the AndroidX Preference Library see 44 * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>. 45 */ 46 @Deprecated 47 public class ListPreference extends DialogPreference { 48 private CharSequence[] mEntries; 49 private CharSequence[] mEntryValues; 50 private String mValue; 51 private String mSummary; 52 @UnsupportedAppUsage 53 private int mClickedDialogEntryIndex; 54 private boolean mValueSet; 55 ListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)56 public ListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 57 super(context, attrs, defStyleAttr, defStyleRes); 58 59 TypedArray a = context.obtainStyledAttributes( 60 attrs, com.android.internal.R.styleable.ListPreference, defStyleAttr, defStyleRes); 61 mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries); 62 mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues); 63 a.recycle(); 64 65 /* Retrieve the Preference summary attribute since it's private 66 * in the Preference class. 67 */ 68 a = context.obtainStyledAttributes(attrs, 69 com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes); 70 mSummary = a.getString(com.android.internal.R.styleable.Preference_summary); 71 a.recycle(); 72 } 73 ListPreference(Context context, AttributeSet attrs, int defStyleAttr)74 public ListPreference(Context context, AttributeSet attrs, int defStyleAttr) { 75 this(context, attrs, defStyleAttr, 0); 76 } 77 ListPreference(Context context, AttributeSet attrs)78 public ListPreference(Context context, AttributeSet attrs) { 79 this(context, attrs, com.android.internal.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 onPrepareDialogBuilder(Builder builder)254 protected void onPrepareDialogBuilder(Builder builder) { 255 super.onPrepareDialogBuilder(builder); 256 257 if (mEntries == null || mEntryValues == null) { 258 throw new IllegalStateException( 259 "ListPreference requires an entries array and an entryValues array."); 260 } 261 262 mClickedDialogEntryIndex = getValueIndex(); 263 builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, 264 new DialogInterface.OnClickListener() { 265 public void onClick(DialogInterface dialog, int which) { 266 mClickedDialogEntryIndex = which; 267 268 /* 269 * Clicking on an item simulates the positive button 270 * click, and dismisses the dialog. 271 */ 272 ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE); 273 postDismiss(); 274 } 275 }); 276 277 /* 278 * The typical interaction for list-based dialogs is to have 279 * click-on-an-item dismiss the dialog instead of the user having to 280 * press 'Ok'. 281 */ 282 builder.setPositiveButton(null, null); 283 } 284 285 @Override onDialogClosed(boolean positiveResult)286 protected void onDialogClosed(boolean positiveResult) { 287 super.onDialogClosed(positiveResult); 288 289 if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) { 290 String value = mEntryValues[mClickedDialogEntryIndex].toString(); 291 if (callChangeListener(value)) { 292 setValue(value); 293 } 294 } 295 } 296 297 @Override onGetDefaultValue(TypedArray a, int index)298 protected Object onGetDefaultValue(TypedArray a, int index) { 299 return a.getString(index); 300 } 301 302 @Override onSetInitialValue(boolean restoreValue, Object defaultValue)303 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 304 setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue); 305 } 306 307 @Override onSaveInstanceState()308 protected Parcelable onSaveInstanceState() { 309 final Parcelable superState = super.onSaveInstanceState(); 310 if (isPersistent()) { 311 // No need to save instance state since it's persistent 312 return superState; 313 } 314 315 final SavedState myState = new SavedState(superState); 316 myState.value = getValue(); 317 return myState; 318 } 319 320 @Override onRestoreInstanceState(Parcelable state)321 protected void onRestoreInstanceState(Parcelable state) { 322 if (state == null || !state.getClass().equals(SavedState.class)) { 323 // Didn't save state for us in onSaveInstanceState 324 super.onRestoreInstanceState(state); 325 return; 326 } 327 328 SavedState myState = (SavedState) state; 329 super.onRestoreInstanceState(myState.getSuperState()); 330 setValue(myState.value); 331 } 332 333 private static class SavedState extends BaseSavedState { 334 String value; 335 SavedState(Parcel source)336 public SavedState(Parcel source) { 337 super(source); 338 value = source.readString(); 339 } 340 341 @Override writeToParcel(Parcel dest, int flags)342 public void writeToParcel(Parcel dest, int flags) { 343 super.writeToParcel(dest, flags); 344 dest.writeString(value); 345 } 346 SavedState(Parcelable superState)347 public SavedState(Parcelable superState) { 348 super(superState); 349 } 350 351 public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR = 352 new Parcelable.Creator<SavedState>() { 353 public SavedState createFromParcel(Parcel in) { 354 return new SavedState(in); 355 } 356 357 public SavedState[] newArray(int size) { 358 return new SavedState[size]; 359 } 360 }; 361 } 362 363 } 364