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