|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.preferences; |
|
7 |
|
8 import org.mozilla.gecko.R; |
|
9 import org.mozilla.gecko.GeckoSharedPrefs; |
|
10 import org.mozilla.gecko.util.ThreadUtils; |
|
11 |
|
12 import android.app.AlertDialog; |
|
13 import android.app.AlertDialog.Builder; |
|
14 import android.content.Context; |
|
15 import android.content.DialogInterface; |
|
16 import android.content.res.TypedArray; |
|
17 import android.preference.DialogPreference; |
|
18 import android.util.AttributeSet; |
|
19 import android.widget.Button; |
|
20 |
|
21 class MultiChoicePreference extends DialogPreference { |
|
22 private static final String LOGTAG = "GeckoMultiChoicePreference"; |
|
23 |
|
24 private boolean mValues[]; |
|
25 private boolean mPrevValues[]; |
|
26 private CharSequence mEntryKeys[]; |
|
27 private CharSequence mEntries[]; |
|
28 private CharSequence mInitialValues[]; |
|
29 |
|
30 public MultiChoicePreference(Context context, AttributeSet attrs) { |
|
31 super(context, attrs); |
|
32 |
|
33 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiChoicePreference); |
|
34 mEntries = a.getTextArray(R.styleable.MultiChoicePreference_entries); |
|
35 mEntryKeys = a.getTextArray(R.styleable.MultiChoicePreference_entryKeys); |
|
36 mInitialValues = a.getTextArray(R.styleable.MultiChoicePreference_initialValues); |
|
37 a.recycle(); |
|
38 |
|
39 loadPersistedValues(); |
|
40 } |
|
41 |
|
42 public MultiChoicePreference(Context context) { |
|
43 this(context, null); |
|
44 } |
|
45 |
|
46 /** |
|
47 * Sets the human-readable entries to be shown in the list. This will be |
|
48 * shown in subsequent dialogs. |
|
49 * <p> |
|
50 * Each entry must have a corresponding index in |
|
51 * {@link #setEntryKeys(CharSequence[])} and |
|
52 * {@link #setInitialValues(CharSequence[])}. |
|
53 * |
|
54 * @param entries The entries. |
|
55 */ |
|
56 public void setEntries(CharSequence[] entries) { |
|
57 mEntries = entries.clone(); |
|
58 } |
|
59 |
|
60 /** |
|
61 * @param entriesResId The entries array as a resource. |
|
62 */ |
|
63 public void setEntries(int entriesResId) { |
|
64 setEntries(getContext().getResources().getTextArray(entriesResId)); |
|
65 } |
|
66 |
|
67 /** |
|
68 * Sets the preference keys for preferences shown in the list. |
|
69 * |
|
70 * @param entryKeys The entry keys. |
|
71 */ |
|
72 public void setEntryKeys(CharSequence[] entryKeys) { |
|
73 mEntryKeys = entryKeys.clone(); |
|
74 loadPersistedValues(); |
|
75 } |
|
76 |
|
77 /** |
|
78 * @param entryKeysResId The entryKeys array as a resource. |
|
79 */ |
|
80 public void setEntryKeys(int entryKeysResId) { |
|
81 setEntryKeys(getContext().getResources().getTextArray(entryKeysResId)); |
|
82 } |
|
83 |
|
84 /** |
|
85 * The array of initial entry values in this list. Each entryValue |
|
86 * corresponds to an entryKey. These values are used if a) the preference |
|
87 * isn't persisted, or b) the preference is persisted but hasn't yet been |
|
88 * set. |
|
89 * |
|
90 * @param initialValues The entry values |
|
91 */ |
|
92 public void setInitialValues(CharSequence[] initialValues) { |
|
93 mInitialValues = initialValues.clone(); |
|
94 loadPersistedValues(); |
|
95 } |
|
96 |
|
97 /** |
|
98 * @param initialValuesResId The initialValues array as a resource. |
|
99 */ |
|
100 public void setInitialValues(int initialValuesResId) { |
|
101 setInitialValues(getContext().getResources().getTextArray(initialValuesResId)); |
|
102 } |
|
103 |
|
104 /** |
|
105 * The list of translated strings corresponding to each preference. |
|
106 * |
|
107 * @return The array of entries. |
|
108 */ |
|
109 public CharSequence[] getEntries() { |
|
110 return mEntries.clone(); |
|
111 } |
|
112 |
|
113 /** |
|
114 * The list of keys corresponding to each preference. |
|
115 * |
|
116 * @return The array of keys. |
|
117 */ |
|
118 public CharSequence[] getEntryKeys() { |
|
119 return mEntryKeys.clone(); |
|
120 } |
|
121 |
|
122 /** |
|
123 * The list of initial values for each preference. Each string in this list |
|
124 * should be either "true" or "false". |
|
125 * |
|
126 * @return The array of initial values. |
|
127 */ |
|
128 public CharSequence[] getInitialValues() { |
|
129 return mInitialValues.clone(); |
|
130 } |
|
131 |
|
132 /** |
|
133 * The list of values for each preference. These values are updated after |
|
134 * the dialog has been displayed. |
|
135 * |
|
136 * @return The array of values. |
|
137 */ |
|
138 public boolean[] getValues() { |
|
139 return mValues.clone(); |
|
140 } |
|
141 |
|
142 @Override |
|
143 protected void onPrepareDialogBuilder(Builder builder) { |
|
144 if (mEntries == null || mEntryKeys == null || mInitialValues == null) { |
|
145 throw new IllegalStateException( |
|
146 "MultiChoicePreference requires entries, entryKeys, and initialValues arrays."); |
|
147 } |
|
148 |
|
149 if (mEntries.length != mEntryKeys.length || mEntryKeys.length != mInitialValues.length) { |
|
150 throw new IllegalStateException( |
|
151 "MultiChoicePreference entries, entryKeys, and initialValues arrays must be the same length"); |
|
152 } |
|
153 |
|
154 builder.setMultiChoiceItems(mEntries, mValues, new DialogInterface.OnMultiChoiceClickListener() { |
|
155 @Override |
|
156 public void onClick(DialogInterface dialog, int which, boolean val) { |
|
157 // mValues is automatically updated when checkboxes are clicked |
|
158 |
|
159 // enable positive button only if at least one item is checked |
|
160 boolean enabled = false; |
|
161 for (int i = 0; i < mValues.length; i++) { |
|
162 if (mValues[i]) { |
|
163 enabled = true; |
|
164 break; |
|
165 } |
|
166 } |
|
167 Button button = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE); |
|
168 if (button.isEnabled() != enabled) |
|
169 button.setEnabled(enabled); |
|
170 } |
|
171 }); |
|
172 } |
|
173 |
|
174 @Override |
|
175 protected void onDialogClosed(boolean positiveResult) { |
|
176 if (mPrevValues == null || mInitialValues == null) { |
|
177 // Initialization is done asynchronously, so these values may not |
|
178 // have been set before the dialog was closed. |
|
179 return; |
|
180 } |
|
181 |
|
182 if (!positiveResult) { |
|
183 // user cancelled; reset checkbox values to their previous state |
|
184 mValues = mPrevValues.clone(); |
|
185 return; |
|
186 } else { |
|
187 mPrevValues = mValues.clone(); |
|
188 } |
|
189 |
|
190 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
191 @Override |
|
192 public void run() { |
|
193 for (int i = 0; i < mEntryKeys.length; i++) { |
|
194 String key = mEntryKeys[i].toString(); |
|
195 persistBoolean(key, mValues[i]); |
|
196 } |
|
197 } |
|
198 }); |
|
199 } |
|
200 |
|
201 protected boolean persistBoolean(String key, boolean value) { |
|
202 if (isPersistent()) { |
|
203 if (value == getPersistedBoolean(!value)) { |
|
204 // It's already there, so the same as persisting |
|
205 return true; |
|
206 } |
|
207 |
|
208 GeckoSharedPrefs.forApp(getContext()) |
|
209 .edit().putBoolean(key, value).commit(); |
|
210 return true; |
|
211 } |
|
212 return false; |
|
213 } |
|
214 |
|
215 protected boolean getPersistedBoolean(String key, boolean defaultReturnValue) { |
|
216 if (!isPersistent()) |
|
217 return defaultReturnValue; |
|
218 |
|
219 return GeckoSharedPrefs.forApp(getContext()) |
|
220 .getBoolean(key, defaultReturnValue); |
|
221 } |
|
222 |
|
223 /** |
|
224 * Loads persistent prefs from shared preferences. If the preferences |
|
225 * aren't persistent or haven't yet been stored, they will be set to their |
|
226 * initial values. |
|
227 */ |
|
228 private void loadPersistedValues() { |
|
229 if (mEntryKeys == null || mInitialValues == null) |
|
230 return; |
|
231 |
|
232 final int entryCount = mEntryKeys.length; |
|
233 if (entryCount != mEntries.length || entryCount != mInitialValues.length) { |
|
234 throw new IllegalStateException( |
|
235 "MultiChoicePreference entryKeys and initialValues arrays must be the same length"); |
|
236 } |
|
237 |
|
238 mValues = new boolean[entryCount]; |
|
239 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
240 @Override |
|
241 public void run() { |
|
242 for (int i = 0; i < entryCount; i++) { |
|
243 String key = mEntryKeys[i].toString(); |
|
244 boolean initialValue = mInitialValues[i].equals("true"); |
|
245 mValues[i] = getPersistedBoolean(key, initialValue); |
|
246 } |
|
247 mPrevValues = mValues.clone(); |
|
248 } |
|
249 }); |
|
250 } |
|
251 } |