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