1 /*
2  * Copyright (C) 2013 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.content;
18 
19 import android.annotation.ArrayRes;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.util.Arrays;
25 import java.util.Objects;
26 
27 /**
28  * Applications can expose restrictions for a restricted user on a
29  * multiuser device. The administrator can configure these restrictions that will then be
30  * applied to the restricted user. Each RestrictionsEntry is one configurable restriction.
31  * <p/>
32  * Any application that chooses to expose such restrictions does so by implementing a
33  * receiver that handles the {@link Intent#ACTION_GET_RESTRICTION_ENTRIES} action.
34  * The receiver then returns a result bundle that contains an entry called "restrictions", whose
35  * value is an ArrayList<RestrictionsEntry>.
36  */
37 public class RestrictionEntry implements Parcelable {
38 
39     /**
40      * Hidden restriction type. Use this type for information that needs to be transferred
41      * across but shouldn't be presented to the user in the UI. Stores a single String value.
42      */
43     public static final int TYPE_NULL         = 0;
44 
45     /**
46      * Restriction of type "bool". Use this for storing a boolean value, typically presented as
47      * a checkbox in the UI.
48      */
49     public static final int TYPE_BOOLEAN      = 1;
50 
51     /**
52      * Restriction of type "choice". Use this for storing a string value, typically presented as
53      * a single-select list. Call {@link #setChoiceEntries(String[])} and
54      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
55      * and the corresponding values, respectively.
56      */
57     public static final int TYPE_CHOICE       = 2;
58 
59     /**
60      * Internal restriction type. Use this for storing a string value, typically presented as
61      * a single-select list. Call {@link #setChoiceEntries(String[])} and
62      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
63      * and the corresponding values, respectively.
64      * The presentation could imply that values in lower array indices are included when a
65      * particular value is chosen.
66      * @hide
67      */
68     public static final int TYPE_CHOICE_LEVEL = 3;
69 
70     /**
71      * Restriction of type "multi-select". Use this for presenting a multi-select list where more
72      * than one entry can be selected, such as for choosing specific titles to allowlist.
73      * Call {@link #setChoiceEntries(String[])} and
74      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
75      * and the corresponding values, respectively.
76      * Use {@link #getAllSelectedStrings()} and {@link #setAllSelectedStrings(String[])} to
77      * manipulate the selections.
78      */
79     public static final int TYPE_MULTI_SELECT = 4;
80 
81     /**
82      * Restriction of type "integer". Use this for storing an integer value. The range of values
83      * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
84      */
85     public static final int TYPE_INTEGER = 5;
86 
87     /**
88      * Restriction of type "string". Use this for storing a string value.
89      * @see #setSelectedString
90      * @see #getSelectedString
91      */
92     public static final int TYPE_STRING = 6;
93 
94     /**
95      * Restriction of type "bundle". Use this for storing {@link android.os.Bundle bundles} of
96      * restrictions
97      */
98     public static final int TYPE_BUNDLE = 7;
99 
100     /**
101      * Restriction of type "bundle_array". Use this for storing arrays of
102      * {@link android.os.Bundle bundles} of restrictions
103      */
104     public static final int TYPE_BUNDLE_ARRAY = 8;
105 
106     /** The type of restriction. */
107     private int mType;
108 
109     /** The unique key that identifies the restriction. */
110     private String mKey;
111 
112     /** The user-visible title of the restriction. */
113     private String mTitle;
114 
115     /** The user-visible secondary description of the restriction. */
116     private String mDescription;
117 
118     /** The user-visible set of choices used for single-select and multi-select lists. */
119     private String[] mChoiceEntries;
120 
121     /** The values corresponding to the user-visible choices. The value(s) of this entry will
122      * one or more of these, returned by {@link #getAllSelectedStrings()} and
123      * {@link #getSelectedString()}.
124      */
125     private String[] mChoiceValues;
126 
127     /* The chosen value, whose content depends on the type of the restriction. */
128     private String mCurrentValue;
129 
130     /* List of selected choices in the multi-select case. */
131     private String[] mCurrentValues;
132 
133     /**
134      * List of nested restrictions. Used by {@link #TYPE_BUNDLE bundle} and
135      * {@link #TYPE_BUNDLE_ARRAY bundle_array} restrictions.
136      */
137     private RestrictionEntry[] mRestrictions;
138 
139     /**
140      * Constructor for specifying the type and key, with no initial value;
141      *
142      * @param type the restriction type.
143      * @param key the unique key for this restriction
144      */
RestrictionEntry(int type, String key)145     public RestrictionEntry(int type, String key) {
146         mType = type;
147         mKey = key;
148     }
149 
150     /**
151      * Constructor for {@link #TYPE_CHOICE} type.
152      * @param key the unique key for this restriction
153      * @param selectedString the current value
154      */
RestrictionEntry(String key, String selectedString)155     public RestrictionEntry(String key, String selectedString) {
156         this.mKey = key;
157         this.mType = TYPE_CHOICE;
158         this.mCurrentValue = selectedString;
159     }
160 
161     /**
162      * Constructor for {@link #TYPE_BOOLEAN} type.
163      * @param key the unique key for this restriction
164      * @param selectedState whether this restriction is selected or not
165      */
RestrictionEntry(String key, boolean selectedState)166     public RestrictionEntry(String key, boolean selectedState) {
167         this.mKey = key;
168         this.mType = TYPE_BOOLEAN;
169         setSelectedState(selectedState);
170     }
171 
172     /**
173      * Constructor for {@link #TYPE_MULTI_SELECT} type.
174      * @param key the unique key for this restriction
175      * @param selectedStrings the list of values that are currently selected
176      */
RestrictionEntry(String key, String[] selectedStrings)177     public RestrictionEntry(String key, String[] selectedStrings) {
178         this.mKey = key;
179         this.mType = TYPE_MULTI_SELECT;
180         this.mCurrentValues = selectedStrings;
181     }
182 
183     /**
184      * Constructor for {@link #TYPE_INTEGER} type.
185      * @param key the unique key for this restriction
186      * @param selectedInt the integer value of the restriction
187      */
RestrictionEntry(String key, int selectedInt)188     public RestrictionEntry(String key, int selectedInt) {
189         mKey = key;
190         mType = TYPE_INTEGER;
191         setIntValue(selectedInt);
192     }
193 
194     /**
195      * Constructor for {@link #TYPE_BUNDLE}/{@link #TYPE_BUNDLE_ARRAY} type.
196      * @param key the unique key for this restriction
197      * @param restrictionEntries array of nested restriction entries. If the entry, being created
198      * represents a {@link #TYPE_BUNDLE_ARRAY bundle-array}, {@code restrictionEntries} array may
199      * only contain elements of type {@link #TYPE_BUNDLE bundle}.
200      * @param isBundleArray true if this restriction represents
201      * {@link #TYPE_BUNDLE_ARRAY bundle-array} type, otherwise the type will be set to
202      * {@link #TYPE_BUNDLE bundle}.
203      */
RestrictionEntry(String key, RestrictionEntry[] restrictionEntries, boolean isBundleArray)204     private RestrictionEntry(String key, RestrictionEntry[] restrictionEntries,
205             boolean isBundleArray) {
206         mKey = key;
207         if (isBundleArray) {
208             mType = TYPE_BUNDLE_ARRAY;
209             if (restrictionEntries != null) {
210                 for (RestrictionEntry restriction : restrictionEntries) {
211                     if (restriction.getType() != TYPE_BUNDLE) {
212                         throw new IllegalArgumentException("bundle_array restriction can only have "
213                                 + "nested restriction entries of type bundle");
214                     }
215                 }
216             }
217         } else {
218             mType = TYPE_BUNDLE;
219         }
220         setRestrictions(restrictionEntries);
221     }
222 
223     /**
224      * Creates an entry of type {@link #TYPE_BUNDLE}.
225      * @param key the unique key for this restriction
226      * @param restrictionEntries array of nested restriction entries.
227      * @return the newly created restriction
228      */
createBundleEntry(String key, RestrictionEntry[] restrictionEntries)229     public static RestrictionEntry createBundleEntry(String key,
230             RestrictionEntry[] restrictionEntries) {
231         return new RestrictionEntry(key, restrictionEntries, false);
232     }
233 
234     /**
235      * Creates an entry of type {@link #TYPE_BUNDLE_ARRAY}.
236      * @param key the unique key for this restriction
237      * @param restrictionEntries array of nested restriction entries. The array may only contain
238      * elements of type {@link #TYPE_BUNDLE bundle}.
239      * @return the newly created restriction
240      */
createBundleArrayEntry(String key, RestrictionEntry[] restrictionEntries)241     public static RestrictionEntry createBundleArrayEntry(String key,
242             RestrictionEntry[] restrictionEntries) {
243         return new RestrictionEntry(key, restrictionEntries, true);
244     }
245 
246     /**
247      * Sets the type for this restriction.
248      * @param type the type for this restriction.
249      */
setType(int type)250     public void setType(int type) {
251         this.mType = type;
252     }
253 
254     /**
255      * Returns the type for this restriction.
256      * @return the type for this restriction
257      */
getType()258     public int getType() {
259         return mType;
260     }
261 
262     /**
263      * Returns the currently selected string value.
264      * @return the currently selected value, which can be null for types that aren't for holding
265      * single string values.
266      */
getSelectedString()267     public String getSelectedString() {
268         return mCurrentValue;
269     }
270 
271     /**
272      * Returns the list of currently selected values.
273      * @return the list of current selections, if type is {@link #TYPE_MULTI_SELECT},
274      *  null otherwise.
275      */
getAllSelectedStrings()276     public String[] getAllSelectedStrings() {
277         return mCurrentValues;
278     }
279 
280     /**
281      * Returns the current selected state for an entry of type {@link #TYPE_BOOLEAN}.
282      * @return the current selected state of the entry.
283      */
getSelectedState()284     public boolean getSelectedState() {
285         return Boolean.parseBoolean(mCurrentValue);
286     }
287 
288     /**
289      * Returns the value of the entry as an integer when the type is {@link #TYPE_INTEGER}.
290      * @return the integer value of the entry.
291      */
getIntValue()292     public int getIntValue() {
293         return Integer.parseInt(mCurrentValue);
294     }
295 
296     /**
297      * Sets the integer value of the entry when the type is {@link #TYPE_INTEGER}.
298      * @param value the integer value to set.
299      */
setIntValue(int value)300     public void setIntValue(int value) {
301         mCurrentValue = Integer.toString(value);
302     }
303 
304     /**
305      * Sets the string value to use as the selected value for this restriction. This value will
306      * be persisted by the system for later use by the application.
307      * @param selectedString the string value to select.
308      */
setSelectedString(String selectedString)309     public void setSelectedString(String selectedString) {
310         mCurrentValue = selectedString;
311     }
312 
313     /**
314      * Sets the current selected state for an entry of type {@link #TYPE_BOOLEAN}. This value will
315      * be persisted by the system for later use by the application.
316      * @param state the current selected state
317      */
setSelectedState(boolean state)318     public void setSelectedState(boolean state) {
319         mCurrentValue = Boolean.toString(state);
320     }
321 
322     /**
323      * Sets the current list of selected values for an entry of type {@link #TYPE_MULTI_SELECT}.
324      * These values will be persisted by the system for later use by the application.
325      * @param allSelectedStrings the current list of selected values.
326      */
setAllSelectedStrings(String[] allSelectedStrings)327     public void setAllSelectedStrings(String[] allSelectedStrings) {
328         mCurrentValues = allSelectedStrings;
329     }
330 
331     /**
332      * Sets a list of string values that can be selected by the user. If no user-visible entries
333      * are set by a call to {@link #setChoiceEntries(String[])}, these values will be the ones
334      * shown to the user. Values will be chosen from this list as the user's selection and the
335      * selected values can be retrieved by a call to {@link #getAllSelectedStrings()}, or
336      * {@link #getSelectedString()}, depending on whether it is a multi-select type or choice type.
337      * This method is not relevant for types other than
338      * {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}.
339      * @param choiceValues an array of Strings which will be the selected values for the user's
340      * selections.
341      * @see #getChoiceValues()
342      * @see #getAllSelectedStrings()
343      */
setChoiceValues(String[] choiceValues)344     public void setChoiceValues(String[] choiceValues) {
345         mChoiceValues = choiceValues;
346     }
347 
348     /**
349      * Sets a list of string values that can be selected by the user, similar to
350      * {@link #setChoiceValues(String[])}.
351      * @param context the application context for retrieving the resources.
352      * @param stringArrayResId the resource id for a string array containing the possible values.
353      * @see #setChoiceValues(String[])
354      */
setChoiceValues(Context context, @ArrayRes int stringArrayResId)355     public void setChoiceValues(Context context, @ArrayRes int stringArrayResId) {
356         mChoiceValues = context.getResources().getStringArray(stringArrayResId);
357     }
358 
359     /**
360      * Returns array of possible restriction entries that this entry may contain.
361      */
getRestrictions()362     public RestrictionEntry[] getRestrictions() {
363         return mRestrictions;
364     }
365 
366    /**
367     * Sets an array of possible restriction entries, that this entry may contain.
368     * <p>This method is only relevant for types {@link #TYPE_BUNDLE} and
369     * {@link #TYPE_BUNDLE_ARRAY}
370     */
setRestrictions(RestrictionEntry[] restrictions)371     public void setRestrictions(RestrictionEntry[] restrictions) {
372         mRestrictions = restrictions;
373     }
374 
375     /**
376      * Returns the list of possible string values set earlier.
377      * @return the list of possible values.
378      */
getChoiceValues()379     public String[] getChoiceValues() {
380         return mChoiceValues;
381     }
382 
383     /**
384      * Sets a list of strings that will be presented as choices to the user. When the
385      * user selects one or more of these choices, the corresponding value from the possible values
386      * are stored as the selected strings. The size of this array must match the size of the array
387      * set in {@link #setChoiceValues(String[])}. This method is not relevant for types other
388      * than {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}.
389      * @param choiceEntries the list of user-visible choices.
390      * @see #setChoiceValues(String[])
391      */
setChoiceEntries(String[] choiceEntries)392     public void setChoiceEntries(String[] choiceEntries) {
393         mChoiceEntries = choiceEntries;
394     }
395 
396     /** Sets a list of strings that will be presented as choices to the user. This is similar to
397      * {@link #setChoiceEntries(String[])}.
398      * @param context the application context, used for retrieving the resources.
399      * @param stringArrayResId the resource id of a string array containing the possible entries.
400      */
setChoiceEntries(Context context, @ArrayRes int stringArrayResId)401     public void setChoiceEntries(Context context, @ArrayRes int stringArrayResId) {
402         mChoiceEntries = context.getResources().getStringArray(stringArrayResId);
403     }
404 
405     /**
406      * Returns the list of strings, set earlier, that will be presented as choices to the user.
407      * @return the list of choices presented to the user.
408      */
getChoiceEntries()409     public String[] getChoiceEntries() {
410         return mChoiceEntries;
411     }
412 
413     /**
414      * Returns the provided user-visible description of the entry, if any.
415      * @return the user-visible description, null if none was set earlier.
416      */
getDescription()417     public String getDescription() {
418         return mDescription;
419     }
420 
421     /**
422      * Sets the user-visible description of the entry, as a possible sub-text for the title.
423      * You can use this to describe the entry in more detail or to display the current state of
424      * the restriction.
425      * @param description the user-visible description string.
426      */
setDescription(String description)427     public void setDescription(String description) {
428         this.mDescription = description;
429     }
430 
431     /**
432      * This is the unique key for the restriction entry.
433      * @return the key for the restriction.
434      */
getKey()435     public String getKey() {
436         return mKey;
437     }
438 
439     /**
440      * Returns the user-visible title for the entry, if any.
441      * @return the user-visible title for the entry, null if none was set earlier.
442      */
getTitle()443     public String getTitle() {
444         return mTitle;
445     }
446 
447     /**
448      * Sets the user-visible title for the entry.
449      * @param title the user-visible title for the entry.
450      */
setTitle(String title)451     public void setTitle(String title) {
452         this.mTitle = title;
453     }
454 
455     @Override
equals(@ullable Object o)456     public boolean equals(@Nullable Object o) {
457         if (o == this) return true;
458         if (!(o instanceof RestrictionEntry)) return false;
459         final RestrictionEntry other = (RestrictionEntry) o;
460         if (mType != other.mType || !mKey.equals(other.mKey)) {
461             return false;
462         }
463         if (mCurrentValues == null && other.mCurrentValues == null
464                 && mRestrictions == null && other.mRestrictions == null
465                 && Objects.equals(mCurrentValue, other.mCurrentValue)) {
466             return true;
467         }
468         if (mCurrentValue == null && other.mCurrentValue == null
469                 && mRestrictions == null && other.mRestrictions == null
470                 && Arrays.equals(mCurrentValues, other.mCurrentValues)) {
471             return true;
472         }
473         if (mCurrentValue == null && other.mCurrentValue == null
474                 && mCurrentValue == null && other.mCurrentValue == null
475                 && Arrays.equals(mRestrictions, other.mRestrictions)) {
476             return true;
477         }
478         return false;
479     }
480 
481     @Override
hashCode()482     public int hashCode() {
483         int result = 17;
484         result = 31 * result + mKey.hashCode();
485         if (mCurrentValue != null) {
486             result = 31 * result + mCurrentValue.hashCode();
487         } else if (mCurrentValues != null) {
488             for (String value : mCurrentValues) {
489                 if (value != null) {
490                     result = 31 * result + value.hashCode();
491                 }
492             }
493         } else if (mRestrictions != null) {
494             result = 31 * result + Arrays.hashCode(mRestrictions);
495         }
496         return result;
497     }
498 
RestrictionEntry(Parcel in)499     public RestrictionEntry(Parcel in) {
500         mType = in.readInt();
501         mKey = in.readString();
502         mTitle = in.readString();
503         mDescription = in.readString();
504         mChoiceEntries = in.readStringArray();
505         mChoiceValues = in.readStringArray();
506         mCurrentValue = in.readString();
507         mCurrentValues = in.readStringArray();
508         Parcelable[] parcelables = in.readParcelableArray(null, RestrictionEntry.class);
509         if (parcelables != null) {
510             mRestrictions = new RestrictionEntry[parcelables.length];
511             for (int i = 0; i < parcelables.length; i++) {
512                 mRestrictions[i] = (RestrictionEntry) parcelables[i];
513             }
514         }
515     }
516 
517     @Override
describeContents()518     public int describeContents() {
519         return 0;
520     }
521 
522     @Override
writeToParcel(Parcel dest, int flags)523     public void writeToParcel(Parcel dest, int flags) {
524         dest.writeInt(mType);
525         dest.writeString(mKey);
526         dest.writeString(mTitle);
527         dest.writeString(mDescription);
528         dest.writeStringArray(mChoiceEntries);
529         dest.writeStringArray(mChoiceValues);
530         dest.writeString(mCurrentValue);
531         dest.writeStringArray(mCurrentValues);
532         dest.writeParcelableArray(mRestrictions, 0);
533     }
534 
535     public static final @android.annotation.NonNull Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
536         public RestrictionEntry createFromParcel(Parcel source) {
537             return new RestrictionEntry(source);
538         }
539 
540         public RestrictionEntry[] newArray(int size) {
541             return new RestrictionEntry[size];
542         }
543     };
544 
545     @Override
toString()546     public String toString() {
547         return "RestrictionEntry{" +
548                 "mType=" + mType +
549                 ", mKey='" + mKey + '\'' +
550                 ", mTitle='" + mTitle + '\'' +
551                 ", mDescription='" + mDescription + '\'' +
552                 ", mChoiceEntries=" + Arrays.toString(mChoiceEntries) +
553                 ", mChoiceValues=" + Arrays.toString(mChoiceValues) +
554                 ", mCurrentValue='" + mCurrentValue + '\'' +
555                 ", mCurrentValues=" + Arrays.toString(mCurrentValues) +
556                 ", mRestrictions=" + Arrays.toString(mRestrictions) +
557                 '}';
558     }
559 }
560