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