1 /*
2  * Copyright (C) 2021 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 com.android.tv.settings;
18 
19 import android.content.Context;
20 import android.os.UserHandle;
21 import android.os.UserManager;
22 import android.text.TextUtils;
23 
24 import androidx.preference.Preference;
25 import androidx.preference.PreferenceGroup;
26 
27 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
28 import com.android.settingslib.RestrictedLockUtilsInternal;
29 import com.android.settingslib.RestrictedPreference;
30 
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.function.Consumer;
34 
35 /**
36  * Adapter that wraps a regular Preference, and restricts the preference if user restrictions apply.
37  *
38  * <p>
39  * If the user is restricted by an admin, the preference is replaced by a RestrictedPreference.
40  * Some state from the original preference is copied, such as title, summary, and visibility.
41  * The restricted preference shows an explanatory message dialog on click, and does not show a
42  * preview panel.
43  *
44  * <p>
45  * If a base user restriction applies, the original preference is disabled.
46  *
47  * <p>
48  * Updates to the preference should be made through {@link #updatePreference(Consumer)}, so that the
49  * restricted preference can be updated after changes, or the original preference can be kept
50  * disabled. Otherwise, {@link #updatePreference()} can be called after direct edits to
51  * the underlying preference.
52  *
53  * @param <T> the type of the wrapped preference
54  */
55 public class RestrictedPreferenceAdapter<T extends Preference> {
56     private final Context mContext;
57     private final T mOriginalPreference;
58     private final List<String> mUserRestrictions;
59     private boolean mRestricted;
60     private EnforcedAdmin mEnforcingAdmin;
61     private RestrictedPreference mRestrictedPreference;
62     private boolean mIsApSaved;
63 
RestrictedPreferenceAdapter(Context context, T originalPreference, List<String> userRestrictions)64     public RestrictedPreferenceAdapter(Context context, T originalPreference,
65             List<String> userRestrictions) {
66         this.mContext = context;
67         this.mOriginalPreference = originalPreference;
68         this.mUserRestrictions = userRestrictions;
69 
70         this.mRestricted = isRestricted();
71         this.mEnforcingAdmin = isRestrictedByAdmin();
72 
73         if (mEnforcingAdmin != null) {
74             mRestrictedPreference = new RestrictedPreference(mContext);
75         } else {
76             mRestrictedPreference = null;
77         }
78     }
79 
RestrictedPreferenceAdapter(Context context, T originalPreference, String userRestriction)80     public RestrictedPreferenceAdapter(Context context, T originalPreference,
81             String userRestriction) {
82         this(context, originalPreference, Collections.singletonList(userRestriction));
83     }
84 
85     /**
86      * Create a RestrictedPreferenceAdapter from a preference.
87      *
88      * @param <T> the type of the preference
89      * @param preference the preference to adapt
90      * @param userRestriction the user restriction to enforce for this preference
91      */
adapt(T preference, String userRestriction)92     public static <T extends Preference> RestrictedPreferenceAdapter<T> adapt(T preference,
93             String userRestriction) {
94         RestrictedPreferenceAdapter<T> adapter = new RestrictedPreferenceAdapter<T>(
95                 preference.getContext(), preference, userRestriction);
96         adapter.setup();
97         return adapter;
98     }
99 
setup()100     private void setup() {
101         if (mRestricted) {
102             mOriginalPreference.setEnabled(false);
103 
104             if (mEnforcingAdmin != null) {
105                 updateRestrictedPreference();
106                 replacePreference();
107             }
108         }
109     }
110 
111     /**
112      * Returns {@code true} if a restriction applies to this preference, {@code false} otherwise.
113      */
isRestricted()114     public boolean isRestricted() {
115         if (mUserRestrictions == null) {
116             return false;
117         }
118 
119         UserManager userManager = UserManager.get(mContext);
120         for (String userRestriction : mUserRestrictions) {
121             if (userManager.hasUserRestriction(userRestriction)) {
122                 return true;
123             }
124         }
125 
126         return false;
127     }
128 
129     /**
130      * Returns {@code true} if given restriction applies to this preference,
131      * {@code false} otherwise.
132      */
isRestricted(String restriction)133     public boolean isRestricted(String restriction) {
134         UserManager userManager = UserManager.get(mContext);
135         return userManager.hasUserRestriction(restriction);
136     }
137 
isRestrictedByAdmin()138     private EnforcedAdmin isRestrictedByAdmin() {
139         if (mUserRestrictions == null) {
140             return null;
141         }
142 
143         for (String userRestriction : mUserRestrictions) {
144             EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
145                     userRestriction, UserHandle.myUserId());
146             if (admin != null) {
147                 return admin;
148             }
149         }
150         return null;
151     }
152 
replacePreference()153     private void replacePreference() {
154         if (mRestrictedPreference == null) {
155             return;
156         }
157 
158         final String key = mOriginalPreference.getKey();
159         if (TextUtils.isEmpty(key)) {
160             throw new IllegalArgumentException("Can't replace a preference without a key");
161         }
162         final PreferenceGroup screen = mOriginalPreference.getParent();
163         final int order = mOriginalPreference.getOrder();
164         mRestrictedPreference.setOrder(order);
165         screen.removePreference(mOriginalPreference);
166         screen.addPreference(mRestrictedPreference);
167     }
168 
169     /** Set access point saved or not. */
setApSaved(boolean saved)170     public void setApSaved(boolean saved) {
171         mIsApSaved = saved;
172     }
173 
174     /**
175      * Returns the preference to be inserted into the preference screen.
176      *
177      * If the preference is unrestricted, this returns the underlying original preference.
178      * If the preference is restricted, this returns the RestrictedPreference that replaces
179      * the original preference.
180      */
getPreference()181     public Preference getPreference() {
182         if (mRestrictedPreference != null && (isRestricted(UserManager.DISALLOW_CONFIG_WIFI)
183                 || (isRestricted(UserManager.DISALLOW_ADD_WIFI_CONFIG) && !mIsApSaved))) {
184             return mRestrictedPreference;
185         }
186 
187         return mOriginalPreference;
188     }
189 
190     /** Returns the original preference. */
getOriginalPreference()191     public Preference getOriginalPreference() {
192         return mOriginalPreference;
193     }
194 
195     /**
196      * Update the preference.
197      * The update function will be called with the adapted preference, changes will be reflected
198      * on the restricted preference.
199      *
200      * @param updateFn function that is passed the original preference to make updates to
201      */
updatePreference(Consumer<T> updateFn)202     public void updatePreference(Consumer<T> updateFn) {
203         updateFn.accept(mOriginalPreference);
204         updatePreference();
205     }
206 
207     /**
208      * Update the preference.
209      * This copies some attributes from the original preference to the restricted preference.
210      * Call this after making direct changes to the original preference.
211      */
updatePreference()212     public void updatePreference() {
213         mRestricted = isRestricted();
214         mEnforcingAdmin = isRestrictedByAdmin();
215         if (mEnforcingAdmin != null) {
216             if (mRestrictedPreference == null) {
217                 mRestrictedPreference = new RestrictedPreference(mContext);
218             }
219         } else {
220             mRestrictedPreference = null;
221         }
222         if (mRestricted && isRestricted(UserManager.DISALLOW_CONFIG_WIFI)) {
223             mOriginalPreference.setEnabled(false);
224         } else {
225             mOriginalPreference.setEnabled(true);
226         }
227         updateRestrictedPreference();
228     }
229 
updateRestrictedPreference()230     private void updateRestrictedPreference() {
231         if (mRestrictedPreference == null) {
232             return;
233         }
234         mRestrictedPreference.setKey(mOriginalPreference.getKey());
235         mRestrictedPreference.setOrder(mOriginalPreference.getOrder());
236         mRestrictedPreference.setTitle(mOriginalPreference.getTitle());
237         mRestrictedPreference.setSummary(mOriginalPreference.getSummary());
238         mRestrictedPreference.setIcon(mOriginalPreference.getIcon());
239         mRestrictedPreference.setVisible(mOriginalPreference.isVisible());
240         mRestrictedPreference.setDisabledByAdmin(isRestrictedByAdmin());
241     }
242 }
243