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 final boolean mRestricted; 60 private final EnforcedAdmin mEnforcingAdmin; 61 private final RestrictedPreference mRestrictedPreference; 62 RestrictedPreferenceAdapter(Context context, T originalPreference, List<String> userRestrictions)63 public RestrictedPreferenceAdapter(Context context, T originalPreference, 64 List<String> userRestrictions) { 65 this.mContext = context; 66 this.mOriginalPreference = originalPreference; 67 this.mUserRestrictions = userRestrictions; 68 69 this.mRestricted = isRestricted(); 70 this.mEnforcingAdmin = isRestrictedByAdmin(); 71 72 if (mEnforcingAdmin != null) { 73 mRestrictedPreference = new RestrictedPreference(mContext); 74 } else { 75 mRestrictedPreference = null; 76 } 77 } 78 RestrictedPreferenceAdapter(Context context, T originalPreference, String userRestriction)79 public RestrictedPreferenceAdapter(Context context, T originalPreference, 80 String userRestriction) { 81 this(context, originalPreference, Collections.singletonList(userRestriction)); 82 } 83 84 /** 85 * Create a RestrictedPreferenceAdapter from a preference. 86 * 87 * @param <T> the type of the preference 88 * @param preference the preference to adapt 89 * @param userRestriction the user restriction to enforce for this preference 90 */ adapt(T preference, String userRestriction)91 public static <T extends Preference> RestrictedPreferenceAdapter<T> adapt(T preference, 92 String userRestriction) { 93 RestrictedPreferenceAdapter<T> adapter = new RestrictedPreferenceAdapter<T>( 94 preference.getContext(), preference, userRestriction); 95 adapter.setup(); 96 return adapter; 97 } 98 setup()99 private void setup() { 100 if (mRestricted) { 101 mOriginalPreference.setEnabled(false); 102 103 if (mEnforcingAdmin != null) { 104 updateRestrictedPreference(); 105 replacePreference(); 106 } 107 } 108 } 109 110 /** 111 * Returns {@code true} if a restriction applies to this preference, {@code false} otherwise. 112 */ isRestricted()113 public boolean isRestricted() { 114 if (mUserRestrictions == null) { 115 return false; 116 } 117 118 UserManager userManager = UserManager.get(mContext); 119 for (String userRestriction : mUserRestrictions) { 120 if (userManager.hasUserRestriction(userRestriction)) { 121 return true; 122 } 123 } 124 125 return false; 126 } 127 isRestrictedByAdmin()128 private EnforcedAdmin isRestrictedByAdmin() { 129 if (mUserRestrictions == null) { 130 return null; 131 } 132 133 for (String userRestriction : mUserRestrictions) { 134 EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext, 135 userRestriction, UserHandle.myUserId()); 136 if (admin != null) { 137 return admin; 138 } 139 } 140 return null; 141 } 142 replacePreference()143 private void replacePreference() { 144 if (mRestrictedPreference == null) { 145 return; 146 } 147 148 final String key = mOriginalPreference.getKey(); 149 if (TextUtils.isEmpty(key)) { 150 throw new IllegalArgumentException("Can't replace a preference without a key"); 151 } 152 final PreferenceGroup screen = mOriginalPreference.getParent(); 153 final int order = mOriginalPreference.getOrder(); 154 mRestrictedPreference.setOrder(order); 155 screen.removePreference(mOriginalPreference); 156 screen.addPreference(mRestrictedPreference); 157 } 158 159 /** 160 * Returns the preference to be inserted into the preference screen. 161 * 162 * If the preference is unrestricted, this returns the underlying original preference. 163 * If the preference is restricted, this returns the RestrictedPreference that replaces 164 * the original preference. 165 */ getPreference()166 public Preference getPreference() { 167 if (mRestrictedPreference != null) { 168 return mRestrictedPreference; 169 } 170 171 return mOriginalPreference; 172 } 173 174 /** Returns the original preference. */ getOriginalPreference()175 public Preference getOriginalPreference() { 176 return mOriginalPreference; 177 } 178 179 /** 180 * Update the preference. 181 * The update function will be called with the adapted preference, changes will be reflected 182 * on the restricted preference. 183 * 184 * @param updateFn function that is passed the original preference to make updates to 185 */ updatePreference(Consumer<T> updateFn)186 public void updatePreference(Consumer<T> updateFn) { 187 updateFn.accept(mOriginalPreference); 188 updatePreference(); 189 } 190 191 /** 192 * Update the preference. 193 * This copies some attributes from the original preference to the restricted preference. 194 * Call this after making direct changes to the original preference. 195 */ updatePreference()196 public void updatePreference() { 197 if (mRestricted) { 198 mOriginalPreference.setEnabled(false); 199 } 200 updateRestrictedPreference(); 201 } 202 updateRestrictedPreference()203 private void updateRestrictedPreference() { 204 if (mRestrictedPreference == null) { 205 return; 206 } 207 208 mRestrictedPreference.setKey(mOriginalPreference.getKey()); 209 mRestrictedPreference.setTitle(mOriginalPreference.getTitle()); 210 mRestrictedPreference.setSummary(mOriginalPreference.getSummary()); 211 mRestrictedPreference.setIcon(mOriginalPreference.getIcon()); 212 mRestrictedPreference.setVisible(mOriginalPreference.isVisible()); 213 mRestrictedPreference.setDisabledByAdmin(isRestrictedByAdmin()); 214 } 215 } 216