1 /*
2  * Copyright (C) 2012 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.content.Context;
22 import android.content.DialogInterface;
23 import android.content.res.TypedArray;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.AttributeSet;
27 
28 import java.util.Arrays;
29 
30 /**
31  * @hide
32  * A {@link Preference} that displays a list of entries as
33  * a dialog which allow the user to toggle each individually on and off.
34  *
35  * @attr ref android.R.styleable#ListPreference_entries
36  * @attr ref android.R.styleable#ListPreference_entryValues
37  *
38  * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
39  *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
40  *      Preference Library</a> for consistent behavior across all devices. For more information on
41  *      using the AndroidX Preference Library see
42  *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
43  */
44 @Deprecated
45 public class MultiCheckPreference extends DialogPreference {
46     private CharSequence[] mEntries;
47     private String[] mEntryValues;
48     private boolean[] mSetValues;
49     private boolean[] mOrigValues;
50     private String mSummary;
51 
MultiCheckPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)52     public MultiCheckPreference(
53             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
54         super(context, attrs, defStyleAttr, defStyleRes);
55 
56         TypedArray a = context.obtainStyledAttributes(
57                 attrs, com.android.internal.R.styleable.ListPreference, defStyleAttr, defStyleRes);
58         mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries);
59         if (mEntries != null) {
60             setEntries(mEntries);
61         }
62         setEntryValuesCS(a.getTextArray(
63                 com.android.internal.R.styleable.ListPreference_entryValues));
64         a.recycle();
65 
66         /* Retrieve the Preference summary attribute since it's private
67          * in the Preference class.
68          */
69         a = context.obtainStyledAttributes(attrs,
70                 com.android.internal.R.styleable.Preference, 0, 0);
71         mSummary = a.getString(com.android.internal.R.styleable.Preference_summary);
72         a.recycle();
73     }
74 
MultiCheckPreference(Context context, AttributeSet attrs, int defStyleAttr)75     public MultiCheckPreference(Context context, AttributeSet attrs, int defStyleAttr) {
76         this(context, attrs, defStyleAttr, 0);
77     }
78 
MultiCheckPreference(Context context, AttributeSet attrs)79     public MultiCheckPreference(Context context, AttributeSet attrs) {
80         this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
81     }
82 
MultiCheckPreference(Context context)83     public MultiCheckPreference(Context context) {
84         this(context, null);
85     }
86 
87     /**
88      * Sets the human-readable entries to be shown in the list. This will be
89      * shown in subsequent dialogs.
90      * <p>
91      * Each entry must have a corresponding index in
92      * {@link #setEntryValues(CharSequence[])}.
93      *
94      * @param entries The entries.
95      * @see #setEntryValues(CharSequence[])
96      */
setEntries(CharSequence[] entries)97     public void setEntries(CharSequence[] entries) {
98         mEntries = entries;
99         mSetValues = new boolean[entries.length];
100         mOrigValues = new boolean[entries.length];
101     }
102 
103     /**
104      * @see #setEntries(CharSequence[])
105      * @param entriesResId The entries array as a resource.
106      */
setEntries(@rrayRes int entriesResId)107     public void setEntries(@ArrayRes int entriesResId) {
108         setEntries(getContext().getResources().getTextArray(entriesResId));
109     }
110 
111     /**
112      * The list of entries to be shown in the list in subsequent dialogs.
113      *
114      * @return The list as an array.
115      */
getEntries()116     public CharSequence[] getEntries() {
117         return mEntries;
118     }
119 
120     /**
121      * The array to find the value to save for a preference when an entry from
122      * entries is selected. If a user clicks on the second item in entries, the
123      * second item in this array will be saved to the preference.
124      *
125      * @param entryValues The array to be used as values to save for the preference.
126      */
setEntryValues(String[] entryValues)127     public void setEntryValues(String[] entryValues) {
128         mEntryValues = entryValues;
129         Arrays.fill(mSetValues, false);
130         Arrays.fill(mOrigValues, false);
131     }
132 
133     /**
134      * @see #setEntryValues(CharSequence[])
135      * @param entryValuesResId The entry values array as a resource.
136      */
setEntryValues(@rrayRes int entryValuesResId)137     public void setEntryValues(@ArrayRes int entryValuesResId) {
138         setEntryValuesCS(getContext().getResources().getTextArray(entryValuesResId));
139     }
140 
setEntryValuesCS(CharSequence[] values)141     private void setEntryValuesCS(CharSequence[] values) {
142         setValues(null);
143         if (values != null) {
144             mEntryValues = new String[values.length];
145             for (int i=0; i<values.length; i++) {
146                 mEntryValues[i] = values[i].toString();
147             }
148         }
149     }
150 
151     /**
152      * Returns the array of values to be saved for the preference.
153      *
154      * @return The array of values.
155      */
getEntryValues()156     public String[] getEntryValues() {
157         return mEntryValues;
158     }
159 
160     /**
161      * Get the boolean state of a given value.
162      */
getValue(int index)163     public boolean getValue(int index) {
164         return mSetValues[index];
165     }
166 
167     /**
168      * Set the boolean state of a given value.
169      */
setValue(int index, boolean state)170     public void setValue(int index, boolean state) {
171         mSetValues[index] = state;
172     }
173 
174     /**
175      * Sets the current values.
176      */
setValues(boolean[] values)177     public void setValues(boolean[] values) {
178         if (mSetValues != null) {
179             Arrays.fill(mSetValues, false);
180             Arrays.fill(mOrigValues, false);
181             if (values != null) {
182                 System.arraycopy(values, 0, mSetValues, 0,
183                         values.length < mSetValues.length ? values.length : mSetValues.length);
184             }
185         }
186     }
187 
188     /**
189      * Returns the summary of this ListPreference. If the summary
190      * has a {@linkplain java.lang.String#format String formatting}
191      * marker in it (i.e. "%s" or "%1$s"), then the current entry
192      * value will be substituted in its place.
193      *
194      * @return the summary with appropriate string substitution
195      */
196     @Override
getSummary()197     public CharSequence getSummary() {
198         if (mSummary == null) {
199             return super.getSummary();
200         } else {
201             return mSummary;
202         }
203     }
204 
205     /**
206      * Sets the summary for this Preference with a CharSequence.
207      * If the summary has a
208      * {@linkplain java.lang.String#format String formatting}
209      * marker in it (i.e. "%s" or "%1$s"), then the current entry
210      * value will be substituted in its place when it's retrieved.
211      *
212      * @param summary The summary for the preference.
213      */
214     @Override
setSummary(CharSequence summary)215     public void setSummary(CharSequence summary) {
216         super.setSummary(summary);
217         if (summary == null && mSummary != null) {
218             mSummary = null;
219         } else if (summary != null && !summary.equals(mSummary)) {
220             mSummary = summary.toString();
221         }
222     }
223 
224     /**
225      * Returns the currently selected values.
226      */
getValues()227     public boolean[] getValues() {
228         return mSetValues;
229     }
230 
231     /**
232      * Returns the index of the given value (in the entry values array).
233      *
234      * @param value The value whose index should be returned.
235      * @return The index of the value, or -1 if not found.
236      */
findIndexOfValue(String value)237     public int findIndexOfValue(String value) {
238         if (value != null && mEntryValues != null) {
239             for (int i = mEntryValues.length - 1; i >= 0; i--) {
240                 if (mEntryValues[i].equals(value)) {
241                     return i;
242                 }
243             }
244         }
245         return -1;
246     }
247 
248     @Override
onPrepareDialogBuilder(Builder builder)249     protected void onPrepareDialogBuilder(Builder builder) {
250         super.onPrepareDialogBuilder(builder);
251 
252         if (mEntries == null || mEntryValues == null) {
253             throw new IllegalStateException(
254                     "ListPreference requires an entries array and an entryValues array.");
255         }
256 
257         mOrigValues = Arrays.copyOf(mSetValues, mSetValues.length);
258         builder.setMultiChoiceItems(mEntries, mSetValues,
259                 new DialogInterface.OnMultiChoiceClickListener() {
260                     @Override
261                     public void onClick(DialogInterface dialog, int which, boolean isChecked) {
262                         mSetValues[which] = isChecked;
263                     }
264         });
265     }
266 
267     @Override
onDialogClosed(boolean positiveResult)268     protected void onDialogClosed(boolean positiveResult) {
269         super.onDialogClosed(positiveResult);
270 
271         if (positiveResult) {
272             if (callChangeListener(getValues())) {
273                 return;
274             }
275         }
276         System.arraycopy(mOrigValues, 0, mSetValues, 0, mSetValues.length);
277     }
278 
279     @Override
onGetDefaultValue(TypedArray a, int index)280     protected Object onGetDefaultValue(TypedArray a, int index) {
281         return a.getString(index);
282     }
283 
284     @Override
onSetInitialValue(boolean restoreValue, Object defaultValue)285     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
286     }
287 
288     @Override
onSaveInstanceState()289     protected Parcelable onSaveInstanceState() {
290         final Parcelable superState = super.onSaveInstanceState();
291         if (isPersistent()) {
292             // No need to save instance state since it's persistent
293             return superState;
294         }
295 
296         final SavedState myState = new SavedState(superState);
297         myState.values = getValues();
298         return myState;
299     }
300 
301     @Override
onRestoreInstanceState(Parcelable state)302     protected void onRestoreInstanceState(Parcelable state) {
303         if (state == null || !state.getClass().equals(SavedState.class)) {
304             // Didn't save state for us in onSaveInstanceState
305             super.onRestoreInstanceState(state);
306             return;
307         }
308 
309         SavedState myState = (SavedState) state;
310         super.onRestoreInstanceState(myState.getSuperState());
311         setValues(myState.values);
312     }
313 
314     private static class SavedState extends BaseSavedState {
315         boolean[] values;
316 
SavedState(Parcel source)317         public SavedState(Parcel source) {
318             super(source);
319             values = source.createBooleanArray();
320         }
321 
322         @Override
writeToParcel(Parcel dest, int flags)323         public void writeToParcel(Parcel dest, int flags) {
324             super.writeToParcel(dest, flags);
325             dest.writeBooleanArray(values);
326         }
327 
SavedState(Parcelable superState)328         public SavedState(Parcelable superState) {
329             super(superState);
330         }
331 
332         public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR =
333                 new Parcelable.Creator<SavedState>() {
334             public SavedState createFromParcel(Parcel in) {
335                 return new SavedState(in);
336             }
337 
338             public SavedState[] newArray(int size) {
339                 return new SavedState[size];
340             }
341         };
342     }
343 
344 }
345