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