1 /*
2  * Copyright (C) 2017 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.accessibility;
18 
19 import static android.content.Context.ACCESSIBILITY_SERVICE;
20 
21 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted;
22 
23 import android.accessibilityservice.AccessibilityServiceInfo;
24 import android.app.admin.DevicePolicyManager;
25 import android.app.tvsettings.TvSettingsEnums;
26 import android.content.ComponentName;
27 import android.content.pm.ServiceInfo;
28 import android.os.Bundle;
29 import android.os.UserHandle;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 import android.view.accessibility.AccessibilityManager;
33 
34 import androidx.annotation.Keep;
35 import androidx.preference.Preference;
36 import androidx.preference.PreferenceGroup;
37 import androidx.preference.SwitchPreference;
38 import androidx.preference.TwoStatePreference;
39 
40 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
41 import com.android.settingslib.RestrictedLockUtilsInternal;
42 import com.android.settingslib.RestrictedPreference;
43 import com.android.settingslib.accessibility.AccessibilityUtils;
44 import com.android.tv.settings.R;
45 import com.android.tv.settings.SettingsPreferenceFragment;
46 
47 import java.util.List;
48 import java.util.Set;
49 
50 /**
51  * Fragment for Accessibility settings
52  */
53 @Keep
54 public class AccessibilityFragment extends SettingsPreferenceFragment {
55     private static final String TOGGLE_HIGH_TEXT_CONTRAST_KEY = "toggle_high_text_contrast";
56     private static final String ACCESSIBILITY_SERVICES_KEY = "system_accessibility_services";
57 
58     private PreferenceGroup mServicesPref;
59     private AccessibilityManager.AccessibilityStateChangeListener
60             mAccessibilityStateChangeListener = enabled -> refreshServices(mServicesPref);
61 
62     /**
63      * Create a new instance of the fragment
64      * @return New fragment instance
65      */
newInstance()66     public static AccessibilityFragment newInstance() {
67         return new AccessibilityFragment();
68     }
69 
70     @Override
onResume()71     public void onResume() {
72         super.onResume();
73         if (mServicesPref != null) {
74             refreshServices(mServicesPref);
75         }
76     }
77 
78     @Override
onStop()79     public void onStop() {
80         super.onStop();
81         AccessibilityManager am = (AccessibilityManager)
82                 getContext().getSystemService(ACCESSIBILITY_SERVICE);
83         if (am != null && mServicesPref != null) {
84             am.removeAccessibilityStateChangeListener(mAccessibilityStateChangeListener);
85         }
86     }
87 
88     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)89     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
90         setPreferencesFromResource(R.xml.accessibility, null);
91 
92         final TwoStatePreference highContrastPreference =
93                 (TwoStatePreference) findPreference(TOGGLE_HIGH_TEXT_CONTRAST_KEY);
94         highContrastPreference.setChecked(Settings.Secure.getInt(getContext().getContentResolver(),
95                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1);
96 
97         mServicesPref = (PreferenceGroup) findPreference(ACCESSIBILITY_SERVICES_KEY);
98         if (mServicesPref != null) {
99             refreshServices(mServicesPref);
100             AccessibilityManager am = (AccessibilityManager)
101                     getContext().getSystemService(ACCESSIBILITY_SERVICE);
102             if (am != null) {
103                 am.addAccessibilityStateChangeListener(mAccessibilityStateChangeListener);
104             }
105         }
106     }
107 
108     @Override
onPreferenceTreeClick(Preference preference)109     public boolean onPreferenceTreeClick(Preference preference) {
110         if (TextUtils.equals(preference.getKey(), TOGGLE_HIGH_TEXT_CONTRAST_KEY)) {
111             logToggleInteracted(
112                     TvSettingsEnums.SYSTEM_A11Y_HIGH_CONTRAST_TEXT,
113                     ((SwitchPreference) preference).isChecked());
114             Settings.Secure.putInt(getActivity().getContentResolver(),
115                     Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
116                     (((SwitchPreference) preference).isChecked() ? 1 : 0));
117             return true;
118         } else {
119             return super.onPreferenceTreeClick(preference);
120         }
121     }
122 
refreshServices(PreferenceGroup group)123     private void refreshServices(PreferenceGroup group) {
124         DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
125         final List<AccessibilityServiceInfo> installedServiceInfos =
126                 getActivity().getSystemService(AccessibilityManager.class)
127                         .getInstalledAccessibilityServiceList();
128         final Set<ComponentName> enabledServices =
129                 AccessibilityUtils.getEnabledServicesFromSettings(getActivity());
130         final List<String> permittedServices = dpm.getPermittedAccessibilityServices(
131                 UserHandle.myUserId());
132 
133         final boolean accessibilityEnabled = Settings.Secure.getInt(
134                 getActivity().getContentResolver(),
135                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
136 
137         for (final AccessibilityServiceInfo accInfo : installedServiceInfos) {
138             final ServiceInfo serviceInfo = accInfo.getResolveInfo().serviceInfo;
139             final ComponentName componentName = new ComponentName(serviceInfo.packageName,
140                     serviceInfo.name);
141 
142             final boolean serviceEnabled = accessibilityEnabled
143                     && enabledServices.contains(componentName);
144             // permittedServices null means all accessibility services are allowed.
145             final boolean serviceAllowed = permittedServices == null
146                     || permittedServices.contains(serviceInfo.packageName);
147 
148             final String title = accInfo.getResolveInfo()
149                     .loadLabel(getActivity().getPackageManager()).toString();
150 
151             final String key = "ServicePref:" + componentName.flattenToString();
152             RestrictedPreference servicePref = findPreference(key);
153             if (servicePref == null) {
154                 servicePref = new RestrictedPreference(group.getContext());
155                 servicePref.setKey(key);
156             }
157             servicePref.setTitle(title);
158             servicePref.setSummary(serviceEnabled ? R.string.settings_on : R.string.settings_off);
159             AccessibilityServiceFragment.prepareArgs(servicePref.getExtras(),
160                     serviceInfo.packageName,
161                     serviceInfo.name,
162                     accInfo.getSettingsActivityName(),
163                     title);
164 
165             if (serviceAllowed || serviceEnabled) {
166                 servicePref.setEnabled(true);
167                 servicePref.setFragment(AccessibilityServiceFragment.class.getName());
168             } else {
169                 // Disable accessibility service that are not permitted.
170                 final EnforcedAdmin admin =
171                         RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
172                                 getContext(), serviceInfo.packageName, UserHandle.myUserId());
173                 if (admin != null) {
174                     servicePref.setDisabledByAdmin(admin);
175                 } else {
176                     servicePref.setEnabled(false);
177                 }
178                 servicePref.setFragment(null);
179             }
180 
181             group.addPreference(servicePref);
182         }
183     }
184 
185     @Override
getPageId()186     protected int getPageId() {
187         return TvSettingsEnums.SYSTEM_A11Y;
188     }
189 }
190