1 /*
2  * Copyright (C) 2015 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.system;
18 
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ServiceInfo;
24 import android.os.Bundle;
25 import android.provider.Settings;
26 import android.support.annotation.NonNull;
27 import android.support.v14.preference.SwitchPreference;
28 import android.support.v17.leanback.app.GuidedStepFragment;
29 import android.support.v17.leanback.widget.GuidanceStylist;
30 import android.support.v17.leanback.widget.GuidedAction;
31 import android.support.v17.preference.LeanbackPreferenceFragment;
32 import android.support.v7.preference.CheckBoxPreference;
33 import android.support.v7.preference.Preference;
34 import android.support.v7.preference.PreferenceCategory;
35 import android.support.v7.preference.PreferenceGroup;
36 import android.support.v7.preference.PreferenceScreen;
37 import android.text.TextUtils;
38 import android.view.accessibility.AccessibilityManager;
39 
40 import com.android.settingslib.accessibility.AccessibilityUtils;
41 import com.android.tv.settings.R;
42 
43 import java.util.List;
44 import java.util.Set;
45 
46 public class AccessibilityFragment extends LeanbackPreferenceFragment {
47     private static final String TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE =
48             "toggle_high_text_contrast_preference";
49 
50     private PreferenceGroup mServicesPref;
51 
newInstance()52     public static AccessibilityFragment newInstance() {
53         return new AccessibilityFragment();
54     }
55 
56     @Override
onResume()57     public void onResume() {
58         super.onResume();
59         if (mServicesPref != null) {
60             refreshServices(mServicesPref);
61         }
62     }
63 
64     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)65     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
66         final Context themedContext = getPreferenceManager().getContext();
67         final PreferenceScreen screen =
68                 getPreferenceManager().createPreferenceScreen(themedContext);
69         screen.setTitle(R.string.system_accessibility);
70 
71         final Preference captionsPreference = new Preference(themedContext);
72         captionsPreference.setTitle(R.string.accessibility_captions);
73         captionsPreference.setIntent(new Intent(Intent.ACTION_MAIN).setComponent(
74                 new ComponentName(getActivity(), CaptionSetupActivity.class)));
75         screen.addPreference(captionsPreference);
76 
77         final SwitchPreference highContrastPreference = new SwitchPreference(themedContext);
78         highContrastPreference.setKey(TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE);
79         highContrastPreference.setPersistent(false);
80         highContrastPreference.setTitle(
81                 R.string.accessibility_toggle_high_text_contrast_preference_title);
82         highContrastPreference.setSummary(R.string.experimental_preference);
83         highContrastPreference.setChecked(Settings.Secure.getInt(getContext().getContentResolver(),
84                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1);
85         screen.addPreference(highContrastPreference);
86 
87         mServicesPref = new PreferenceCategory(themedContext);
88         mServicesPref.setTitle(R.string.system_services);
89         screen.addPreference(mServicesPref);
90         refreshServices(mServicesPref);
91 
92         final Preference ttsPref = new Preference(themedContext);
93         ttsPref.setTitle(R.string.system_accessibility_tts_output);
94         ttsPref.setFragment(TextToSpeechFragment.class.getName());
95         screen.addPreference(ttsPref);
96 
97         setPreferenceScreen(screen);
98     }
99 
100     @Override
onPreferenceTreeClick(Preference preference)101     public boolean onPreferenceTreeClick(Preference preference) {
102         if (TextUtils.equals(preference.getKey(), TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE)) {
103             Settings.Secure.putInt(getActivity().getContentResolver(),
104                     Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
105                     (((SwitchPreference) preference).isChecked() ? 1 : 0));
106             return true;
107         } else {
108             return super.onPreferenceTreeClick(preference);
109         }
110     }
111 
refreshServices(PreferenceGroup group)112     private void refreshServices(PreferenceGroup group) {
113         final List<AccessibilityServiceInfo> installedServiceInfos = AccessibilityManager
114                 .getInstance(getActivity()).getInstalledAccessibilityServiceList();
115         final Set<ComponentName> enabledServices =
116                 AccessibilityUtils.getEnabledServicesFromSettings(getActivity());
117         final boolean accessibilityEnabled = Settings.Secure.getInt(
118                 getActivity().getContentResolver(),
119                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
120 
121         for (final AccessibilityServiceInfo accInfo : installedServiceInfos) {
122             final ServiceInfo serviceInfo = accInfo.getResolveInfo().serviceInfo;
123             final ComponentName componentName = new ComponentName(serviceInfo.packageName,
124                     serviceInfo.name);
125 
126             final boolean serviceEnabled = accessibilityEnabled
127                     && enabledServices.contains(componentName);
128 
129             final String title = accInfo.getResolveInfo()
130                     .loadLabel(getActivity().getPackageManager()).toString();
131 
132             final String key = "ServicePref:" + componentName.flattenToString();
133             Preference servicePref = findPreference(key);
134             if (servicePref == null) {
135                 servicePref = new Preference(group.getContext());
136                 servicePref.setKey(key);
137             }
138             servicePref.setTitle(title);
139             servicePref.setSummary(serviceEnabled ? R.string.settings_on : R.string.settings_off);
140             servicePref.setFragment(AccessibilityServiceFragment.class.getName());
141             AccessibilityServiceFragment.prepareArgs(servicePref.getExtras(),
142                     serviceInfo.packageName,
143                     serviceInfo.name,
144                     accInfo.getSettingsActivityName(),
145                     title);
146             group.addPreference(servicePref);
147         }
148     }
149 
150     public static class AccessibilityServiceFragment extends LeanbackPreferenceFragment {
151         private static final String ARG_PACKAGE_NAME = "packageName";
152         private static final String ARG_SERVICE_NAME = "serviceName";
153         private static final String ARG_SETTINGS_ACTIVITY_NAME = "settingsActivityName";
154         private static final String ARG_LABEL = "label";
155 
156         private CheckBoxPreference mEnablePref;
157 
prepareArgs(@onNull Bundle args, String packageName, String serviceName, String activityName, String label)158         public static void prepareArgs(@NonNull Bundle args, String packageName, String serviceName,
159                 String activityName, String label) {
160             args.putString(ARG_PACKAGE_NAME, packageName);
161             args.putString(ARG_SERVICE_NAME, serviceName);
162             args.putString(ARG_SETTINGS_ACTIVITY_NAME, activityName);
163             args.putString(ARG_LABEL, label);
164         }
165 
166         @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)167         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
168             final Context themedContext = getPreferenceManager().getContext();
169             final PreferenceScreen screen =
170                     getPreferenceManager().createPreferenceScreen(themedContext);
171             screen.setTitle(getArguments().getString(ARG_LABEL));
172 
173             mEnablePref = new CheckBoxPreference(themedContext);
174             mEnablePref.setTitle(R.string.system_accessibility_status);
175             mEnablePref.setFragment(EnableDisableConfirmationFragment.class.getName());
176             screen.addPreference(mEnablePref);
177 
178             final Preference settingsPref = new Preference(themedContext);
179             settingsPref.setTitle(R.string.system_accessibility_config);
180             final String activityName = getArguments().getString(ARG_SETTINGS_ACTIVITY_NAME);
181             if (!TextUtils.isEmpty(activityName)) {
182                 final String packageName = getArguments().getString(ARG_PACKAGE_NAME);
183                 settingsPref.setIntent(new Intent(Intent.ACTION_MAIN)
184                         .setComponent(new ComponentName(packageName, activityName)));
185             } else {
186                 settingsPref.setEnabled(false);
187             }
188             screen.addPreference(settingsPref);
189 
190             setPreferenceScreen(screen);
191         }
192 
193         @Override
onResume()194         public void onResume() {
195             super.onResume();
196 
197             final String packageName = getArguments().getString(ARG_PACKAGE_NAME);
198             final String serviceName = getArguments().getString(ARG_SERVICE_NAME);
199 
200             final ComponentName serviceComponent = new ComponentName(packageName, serviceName);
201             final Set<ComponentName> enabledServices =
202                     AccessibilityUtils.getEnabledServicesFromSettings(getActivity());
203             final boolean enabled = enabledServices.contains(serviceComponent);
204 
205             mEnablePref.setChecked(enabled);
206             EnableDisableConfirmationFragment.prepareArgs(mEnablePref.getExtras(),
207                     new ComponentName(packageName, serviceName),
208                     getArguments().getString(ARG_LABEL), !enabled);
209         }
210 
211         public static class EnableDisableConfirmationFragment extends GuidedStepFragment {
212             private static final String ARG_LABEL = "label";
213             private static final String ARG_COMPONENT = "component";
214             private static final String ARG_ENABLING = "enabling";
215 
prepareArgs(@onNull Bundle args, ComponentName cn, String label, boolean enabling)216             public static void prepareArgs(@NonNull Bundle args, ComponentName cn, String label,
217                     boolean enabling) {
218                 args.putParcelable(ARG_COMPONENT, cn);
219                 args.putString(ARG_LABEL, label);
220                 args.putBoolean(ARG_ENABLING, enabling);
221             }
222 
223             @NonNull
224             @Override
onCreateGuidance(Bundle savedInstanceState)225             public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
226                 final String label = getArguments().getString(ARG_LABEL);
227                 if (getArguments().getBoolean(ARG_ENABLING)) {
228                     return new GuidanceStylist.Guidance(
229                             getString(R.string.system_accessibility_service_on_confirm_title,
230                                     label),
231                             getString(R.string.system_accessibility_service_on_confirm_desc,
232                                     label),
233                             null,
234                             getActivity().getDrawable(R.drawable.ic_accessibility_new_132dp)
235                     );
236                 } else {
237                     return new GuidanceStylist.Guidance(
238                             getString(R.string.system_accessibility_service_off_confirm_title,
239                                     label),
240                             getString(R.string.system_accessibility_service_off_confirm_desc,
241                                     label),
242                             null,
243                             getActivity().getDrawable(R.drawable.ic_accessibility_new_132dp)
244                     );
245                 }
246             }
247 
248             @Override
onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)249             public void onCreateActions(@NonNull List<GuidedAction> actions,
250                     Bundle savedInstanceState) {
251                 final Context context = getActivity();
252                 actions.add(new GuidedAction.Builder(context)
253                         .clickAction(GuidedAction.ACTION_ID_OK).build());
254                 actions.add(new GuidedAction.Builder(context)
255                         .clickAction(GuidedAction.ACTION_ID_CANCEL).build());
256             }
257 
258             @Override
onGuidedActionClicked(GuidedAction action)259             public void onGuidedActionClicked(GuidedAction action) {
260                 if (action.getId() == GuidedAction.ACTION_ID_OK) {
261                     final ComponentName component = getArguments().getParcelable(ARG_COMPONENT);
262                     AccessibilityUtils.setAccessibilityServiceState(getActivity(),
263                             component, getArguments().getBoolean(ARG_ENABLING));
264                     getFragmentManager().popBackStack();
265                 } else if (action.getId() == GuidedAction.ACTION_ID_CANCEL) {
266                     getFragmentManager().popBackStack();
267                 } else {
268                     super.onGuidedActionClicked(action);
269                 }
270             }
271         }
272     }
273 }
274