blob: 27003734c2bb06c878f8eae60682e6397bc7a1fb [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.preference;
18
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.ArrayRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.AlertDialog.Builder;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.res.TypedArray;
24import android.os.Parcel;
25import android.os.Parcelable;
Alan Viverette94c02a12013-07-23 14:43:37 -070026import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import 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;
Kenny Rootba636df2010-07-13 07:53:32 -070043 private String mSummary;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 private int mClickedDialogEntryIndex;
Alan Viverette94c02a12013-07-23 14:43:37 -070045 private boolean mValueSet;
Alan Viverette617feb92013-09-09 18:09:13 -070046
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, com.android.internal.R.styleable.ListPreference, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries);
53 mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues);
54 a.recycle();
Kenny Rootba636df2010-07-13 07:53:32 -070055
56 /* Retrieve the Preference summary attribute since it's private
57 * in the Preference class.
58 */
59 a = context.obtainStyledAttributes(attrs,
Alan Viverette617feb92013-09-09 18:09:13 -070060 com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes);
Kenny Rootba636df2010-07-13 07:53:32 -070061 mSummary = a.getString(com.android.internal.R.styleable.Preference_summary);
62 a.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 }
Kenny Rootba636df2010-07-13 07:53:32 -070064
Alan Viverette617feb92013-09-09 18:09:13 -070065 public ListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
66 this(context, attrs, defStyleAttr, 0);
67 }
68
69 public ListPreference(Context context, AttributeSet attrs) {
Alan Viverette599d2a42013-09-16 13:48:29 -070070 this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
Alan Viverette617feb92013-09-09 18:09:13 -070071 }
72
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 public ListPreference(Context context) {
74 this(context, null);
75 }
76
77 /**
78 * Sets the human-readable entries to be shown in the list. This will be
79 * shown in subsequent dialogs.
80 * <p>
81 * Each entry must have a corresponding index in
82 * {@link #setEntryValues(CharSequence[])}.
83 *
84 * @param entries The entries.
85 * @see #setEntryValues(CharSequence[])
86 */
87 public void setEntries(CharSequence[] entries) {
88 mEntries = entries;
89 }
90
91 /**
92 * @see #setEntries(CharSequence[])
93 * @param entriesResId The entries array as a resource.
94 */
Tor Norbye7b9c9122013-05-30 16:48:33 -070095 public void setEntries(@ArrayRes int entriesResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 setEntries(getContext().getResources().getTextArray(entriesResId));
97 }
98
99 /**
100 * The list of entries to be shown in the list in subsequent dialogs.
101 *
102 * @return The list as an array.
103 */
104 public CharSequence[] getEntries() {
105 return mEntries;
106 }
107
108 /**
109 * The array to find the value to save for a preference when an entry from
110 * entries is selected. If a user clicks on the second item in entries, the
111 * second item in this array will be saved to the preference.
112 *
113 * @param entryValues The array to be used as values to save for the preference.
114 */
115 public void setEntryValues(CharSequence[] entryValues) {
116 mEntryValues = entryValues;
117 }
118
119 /**
120 * @see #setEntryValues(CharSequence[])
121 * @param entryValuesResId The entry values array as a resource.
122 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700123 public void setEntryValues(@ArrayRes int entryValuesResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
125 }
126
127 /**
128 * Returns the array of values to be saved for the preference.
129 *
130 * @return The array of values.
131 */
132 public CharSequence[] getEntryValues() {
133 return mEntryValues;
134 }
135
136 /**
137 * Sets the value of the key. This should be one of the entries in
138 * {@link #getEntryValues()}.
139 *
140 * @param value The value to set for the key.
141 */
142 public void setValue(String value) {
Alan Viverette94c02a12013-07-23 14:43:37 -0700143 // Always persist/notify the first time.
144 final boolean changed = !TextUtils.equals(mValue, value);
145 if (changed || !mValueSet) {
146 mValue = value;
147 mValueSet = true;
148 persistString(value);
149 if (changed) {
150 notifyChanged();
151 }
152 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154
155 /**
Kenny Rootba636df2010-07-13 07:53:32 -0700156 * Returns the summary of this ListPreference. If the summary
157 * has a {@linkplain java.lang.String#format String formatting}
158 * marker in it (i.e. "%s" or "%1$s"), then the current entry
159 * value will be substituted in its place.
160 *
161 * @return the summary with appropriate string substitution
162 */
163 @Override
164 public CharSequence getSummary() {
165 final CharSequence entry = getEntry();
Alan Viverette13e13dd2014-10-21 17:05:07 -0700166 if (mSummary == null) {
Kenny Rootba636df2010-07-13 07:53:32 -0700167 return super.getSummary();
168 } else {
Alan Viverette13e13dd2014-10-21 17:05:07 -0700169 return String.format(mSummary, entry == null ? "" : entry);
Kenny Rootba636df2010-07-13 07:53:32 -0700170 }
171 }
172
173 /**
174 * Sets the summary for this Preference with a CharSequence.
175 * If the summary has a
176 * {@linkplain java.lang.String#format String formatting}
177 * marker in it (i.e. "%s" or "%1$s"), then the current entry
178 * value will be substituted in its place when it's retrieved.
179 *
180 * @param summary The summary for the preference.
181 */
182 @Override
183 public void setSummary(CharSequence summary) {
184 super.setSummary(summary);
185 if (summary == null && mSummary != null) {
186 mSummary = null;
187 } else if (summary != null && !summary.equals(mSummary)) {
188 mSummary = summary.toString();
189 }
190 }
191
192 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 * Sets the value to the given index from the entry values.
194 *
195 * @param index The index of the value to set.
196 */
197 public void setValueIndex(int index) {
198 if (mEntryValues != null) {
199 setValue(mEntryValues[index].toString());
200 }
201 }
202
203 /**
204 * Returns the value of the key. This should be one of the entries in
205 * {@link #getEntryValues()}.
206 *
207 * @return The value of the key.
208 */
209 public String getValue() {
210 return mValue;
211 }
212
213 /**
214 * Returns the entry corresponding to the current value.
215 *
216 * @return The entry corresponding to the current value, or null.
217 */
218 public CharSequence getEntry() {
219 int index = getValueIndex();
220 return index >= 0 && mEntries != null ? mEntries[index] : null;
221 }
222
223 /**
224 * Returns the index of the given value (in the entry values array).
225 *
226 * @param value The value whose index should be returned.
227 * @return The index of the value, or -1 if not found.
228 */
229 public int findIndexOfValue(String value) {
230 if (value != null && mEntryValues != null) {
231 for (int i = mEntryValues.length - 1; i >= 0; i--) {
232 if (mEntryValues[i].equals(value)) {
233 return i;
234 }
235 }
236 }
237 return -1;
238 }
239
240 private int getValueIndex() {
241 return findIndexOfValue(mValue);
242 }
243
244 @Override
245 protected void onPrepareDialogBuilder(Builder builder) {
246 super.onPrepareDialogBuilder(builder);
247
248 if (mEntries == null || mEntryValues == null) {
249 throw new IllegalStateException(
250 "ListPreference requires an entries array and an entryValues array.");
251 }
252
253 mClickedDialogEntryIndex = getValueIndex();
254 builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
255 new DialogInterface.OnClickListener() {
256 public void onClick(DialogInterface dialog, int which) {
257 mClickedDialogEntryIndex = which;
258
259 /*
260 * Clicking on an item simulates the positive button
261 * click, and dismisses the dialog.
262 */
263 ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
264 dialog.dismiss();
265 }
266 });
267
268 /*
269 * The typical interaction for list-based dialogs is to have
270 * click-on-an-item dismiss the dialog instead of the user having to
271 * press 'Ok'.
272 */
273 builder.setPositiveButton(null, null);
274 }
275
276 @Override
277 protected void onDialogClosed(boolean positiveResult) {
278 super.onDialogClosed(positiveResult);
279
280 if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
281 String value = mEntryValues[mClickedDialogEntryIndex].toString();
282 if (callChangeListener(value)) {
283 setValue(value);
284 }
285 }
286 }
287
288 @Override
289 protected Object onGetDefaultValue(TypedArray a, int index) {
290 return a.getString(index);
291 }
292
293 @Override
294 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
295 setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
296 }
297
298 @Override
299 protected Parcelable onSaveInstanceState() {
300 final Parcelable superState = super.onSaveInstanceState();
301 if (isPersistent()) {
302 // No need to save instance state since it's persistent
303 return superState;
304 }
305
306 final SavedState myState = new SavedState(superState);
307 myState.value = getValue();
308 return myState;
309 }
310
311 @Override
312 protected void onRestoreInstanceState(Parcelable state) {
313 if (state == null || !state.getClass().equals(SavedState.class)) {
314 // Didn't save state for us in onSaveInstanceState
315 super.onRestoreInstanceState(state);
316 return;
317 }
318
319 SavedState myState = (SavedState) state;
320 super.onRestoreInstanceState(myState.getSuperState());
321 setValue(myState.value);
322 }
323
324 private static class SavedState extends BaseSavedState {
325 String value;
326
327 public SavedState(Parcel source) {
328 super(source);
329 value = source.readString();
330 }
331
332 @Override
333 public void writeToParcel(Parcel dest, int flags) {
334 super.writeToParcel(dest, flags);
335 dest.writeString(value);
336 }
337
338 public SavedState(Parcelable superState) {
339 super(superState);
340 }
341
342 public static final Parcelable.Creator<SavedState> CREATOR =
343 new Parcelable.Creator<SavedState>() {
344 public SavedState createFromParcel(Parcel in) {
345 return new SavedState(in);
346 }
347
348 public SavedState[] newArray(int size) {
349 return new SavedState[size];
350 }
351 };
352 }
353
354}