1 /* 2 * Copyright (C) 2019 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.car.developeroptions; 18 19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21 import android.app.KeyguardManager; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.os.UserManager; 27 import android.util.AttributeSet; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.widget.AdapterView; 31 import android.widget.ArrayAdapter; 32 import android.widget.CheckedTextView; 33 import android.widget.ImageView; 34 import android.widget.ListAdapter; 35 import android.widget.ListView; 36 37 import androidx.appcompat.app.AlertDialog; 38 import androidx.appcompat.app.AlertDialog.Builder; 39 import androidx.preference.ListPreferenceDialogFragmentCompat; 40 import androidx.preference.PreferenceViewHolder; 41 42 import com.android.settingslib.RestrictedLockUtils; 43 import com.android.settingslib.RestrictedPreferenceHelper; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 public class RestrictedListPreference extends CustomListPreference { 49 private final RestrictedPreferenceHelper mHelper; 50 private final List<RestrictedItem> mRestrictedItems = new ArrayList<>(); 51 private boolean mRequiresActiveUnlockedProfile = false; 52 private int mProfileUserId; 53 RestrictedListPreference(Context context, AttributeSet attrs)54 public RestrictedListPreference(Context context, AttributeSet attrs) { 55 super(context, attrs); 56 setWidgetLayoutResource(R.layout.restricted_icon); 57 mHelper = new RestrictedPreferenceHelper(context, this, attrs); 58 } 59 RestrictedListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)60 public RestrictedListPreference(Context context, AttributeSet attrs, 61 int defStyleAttr, int defStyleRes) { 62 super(context, attrs, defStyleAttr, defStyleRes); 63 mHelper = new RestrictedPreferenceHelper(context, this, attrs); 64 } 65 66 @Override onBindViewHolder(PreferenceViewHolder holder)67 public void onBindViewHolder(PreferenceViewHolder holder) { 68 super.onBindViewHolder(holder); 69 mHelper.onBindViewHolder(holder); 70 final View restrictedIcon = holder.findViewById(R.id.restricted_icon); 71 if (restrictedIcon != null) { 72 restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE); 73 } 74 } 75 76 @Override performClick()77 public void performClick() { 78 if (mRequiresActiveUnlockedProfile) { 79 // Check if the profile is started, first. 80 if (Utils.startQuietModeDialogIfNecessary(getContext(), UserManager.get(getContext()), 81 mProfileUserId)) { 82 return; 83 } 84 85 // Next, check if the profile is unlocked. 86 KeyguardManager manager = 87 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); 88 if (manager.isDeviceLocked(mProfileUserId)) { 89 Intent intent = manager.createConfirmDeviceCredentialIntent( 90 null, null, mProfileUserId); 91 getContext().startActivity(intent); 92 return; 93 } 94 } 95 96 if (!mHelper.performClick()) { 97 super.performClick(); 98 } 99 } 100 101 @Override setEnabled(boolean enabled)102 public void setEnabled(boolean enabled) { 103 if (enabled && isDisabledByAdmin()) { 104 mHelper.setDisabledByAdmin(null); 105 return; 106 } 107 super.setEnabled(enabled); 108 } 109 setDisabledByAdmin(EnforcedAdmin admin)110 public void setDisabledByAdmin(EnforcedAdmin admin) { 111 if (mHelper.setDisabledByAdmin(admin)) { 112 notifyChanged(); 113 } 114 } 115 isDisabledByAdmin()116 public boolean isDisabledByAdmin() { 117 return mHelper.isDisabledByAdmin(); 118 } 119 setRequiresActiveUnlockedProfile(boolean reqState)120 public void setRequiresActiveUnlockedProfile(boolean reqState) { 121 mRequiresActiveUnlockedProfile = reqState; 122 } 123 setProfileUserId(int profileUserId)124 public void setProfileUserId(int profileUserId) { 125 mProfileUserId = profileUserId; 126 } 127 isRestrictedForEntry(CharSequence entry)128 public boolean isRestrictedForEntry(CharSequence entry) { 129 if (entry == null) { 130 return false; 131 } 132 for (RestrictedItem item : mRestrictedItems) { 133 if (entry.equals(item.entry)) { 134 return true; 135 } 136 } 137 return false; 138 } 139 addRestrictedItem(RestrictedItem item)140 public void addRestrictedItem(RestrictedItem item) { 141 mRestrictedItems.add(item); 142 } 143 clearRestrictedItems()144 public void clearRestrictedItems() { 145 mRestrictedItems.clear(); 146 } 147 getRestrictedItemForEntryValue(CharSequence entryValue)148 private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) { 149 if (entryValue == null) { 150 return null; 151 } 152 for (RestrictedItem item : mRestrictedItems) { 153 if (entryValue.equals(item.entryValue)) { 154 return item; 155 } 156 } 157 return null; 158 } 159 createListAdapter(Context context)160 protected ListAdapter createListAdapter(Context context) { 161 return new RestrictedArrayAdapter(context, getEntries(), 162 getSelectedValuePos()); 163 } 164 getSelectedValuePos()165 public int getSelectedValuePos() { 166 final String selectedValue = getValue(); 167 final int selectedIndex = 168 (selectedValue == null) ? -1 : findIndexOfValue(selectedValue); 169 return selectedIndex; 170 } 171 172 @Override onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener)173 protected void onPrepareDialogBuilder(Builder builder, 174 DialogInterface.OnClickListener listener) { 175 builder.setAdapter(createListAdapter(builder.getContext()), listener); 176 } 177 178 public class RestrictedArrayAdapter extends ArrayAdapter<CharSequence> { 179 private final int mSelectedIndex; RestrictedArrayAdapter(Context context, CharSequence[] objects, int selectedIndex)180 public RestrictedArrayAdapter(Context context, CharSequence[] objects, int selectedIndex) { 181 super(context, R.layout.restricted_dialog_singlechoice, R.id.text1, objects); 182 mSelectedIndex = selectedIndex; 183 } 184 185 @Override getView(int position, View convertView, ViewGroup parent)186 public View getView(int position, View convertView, ViewGroup parent) { 187 View root = super.getView(position, convertView, parent); 188 CharSequence entry = getItem(position); 189 CheckedTextView text = (CheckedTextView) root.findViewById(R.id.text1); 190 ImageView padlock = (ImageView) root.findViewById(R.id.restricted_lock_icon); 191 if (isRestrictedForEntry(entry)) { 192 text.setEnabled(false); 193 text.setChecked(false); 194 padlock.setVisibility(View.VISIBLE); 195 } else { 196 if (mSelectedIndex != -1) { 197 text.setChecked(position == mSelectedIndex); 198 } 199 if (!text.isEnabled()) { 200 text.setEnabled(true); 201 } 202 padlock.setVisibility(View.GONE); 203 } 204 return root; 205 } 206 207 @Override hasStableIds()208 public boolean hasStableIds() { 209 return true; 210 } 211 212 @Override getItemId(int position)213 public long getItemId(int position) { 214 return position; 215 } 216 } 217 218 public static class RestrictedListPreferenceDialogFragment extends 219 CustomListPreference.CustomListPreferenceDialogFragment { 220 private int mLastCheckedPosition = AdapterView.INVALID_POSITION; 221 newInstance(String key)222 public static ListPreferenceDialogFragmentCompat newInstance(String key) { 223 final ListPreferenceDialogFragmentCompat fragment 224 = new RestrictedListPreferenceDialogFragment(); 225 final Bundle b = new Bundle(1); 226 b.putString(ARG_KEY, key); 227 fragment.setArguments(b); 228 return fragment; 229 } 230 getCustomizablePreference()231 private RestrictedListPreference getCustomizablePreference() { 232 return (RestrictedListPreference) getPreference(); 233 } 234 235 @Override getOnItemClickListener()236 protected DialogInterface.OnClickListener getOnItemClickListener() { 237 return new DialogInterface.OnClickListener() { 238 public void onClick(DialogInterface dialog, int which) { 239 final RestrictedListPreference preference = getCustomizablePreference(); 240 if (which < 0 || which >= preference.getEntryValues().length) { 241 return; 242 } 243 String entryValue = preference.getEntryValues()[which].toString(); 244 RestrictedItem item = preference.getRestrictedItemForEntryValue(entryValue); 245 if (item != null) { 246 ListView listView = ((AlertDialog) dialog).getListView(); 247 listView.setItemChecked(getLastCheckedPosition(), true); 248 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), 249 item.enforcedAdmin); 250 } else { 251 setClickedDialogEntryIndex(which); 252 } 253 254 if (getCustomizablePreference().isAutoClosePreference()) { 255 /* 256 * Clicking on an item simulates the positive button 257 * click, and dismisses the dialog. 258 */ 259 RestrictedListPreferenceDialogFragment.this.onClick(dialog, 260 DialogInterface.BUTTON_POSITIVE); 261 dialog.dismiss(); 262 } 263 } 264 }; 265 } 266 getLastCheckedPosition()267 private int getLastCheckedPosition() { 268 if (mLastCheckedPosition == AdapterView.INVALID_POSITION) { 269 mLastCheckedPosition = ((RestrictedListPreference) getCustomizablePreference()) 270 .getSelectedValuePos(); 271 } 272 return mLastCheckedPosition; 273 } 274 setCheckedPosition(int checkedPosition)275 private void setCheckedPosition(int checkedPosition) { 276 mLastCheckedPosition = checkedPosition; 277 } 278 279 @Override setClickedDialogEntryIndex(int which)280 protected void setClickedDialogEntryIndex(int which) { 281 super.setClickedDialogEntryIndex(which); 282 mLastCheckedPosition = which; 283 } 284 } 285 286 public static class RestrictedItem { 287 public final CharSequence entry; 288 public final CharSequence entryValue; 289 public final EnforcedAdmin enforcedAdmin; 290 291 public RestrictedItem(CharSequence entry, CharSequence entryValue, 292 EnforcedAdmin enforcedAdmin) { 293 this.entry = entry; 294 this.entryValue = entryValue; 295 this.enforcedAdmin = enforcedAdmin; 296 } 297 } 298 } 299