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