• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.settings.accessibility;
17 
18 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
19 import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
20 
21 import android.accessibilityservice.AccessibilityServiceInfo;
22 import android.app.Dialog;
23 import android.app.settings.SettingsEnums;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ResolveInfo;
29 import android.graphics.drawable.Drawable;
30 import android.os.Binder;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.os.UserHandle;
34 import android.provider.Settings;
35 import android.text.TextUtils;
36 import android.view.accessibility.AccessibilityManager;
37 import android.view.View;
38 
39 import androidx.fragment.app.Fragment;
40 import androidx.fragment.app.FragmentActivity;
41 
42 import com.android.internal.accessibility.AccessibilityShortcutController;
43 import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo;
44 import com.android.settings.R;
45 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
46 import com.android.settings.widget.RadioButtonPickerFragment;
47 import com.android.settings.widget.RadioButtonPreference;
48 import com.android.settingslib.accessibility.AccessibilityUtils;
49 import com.android.settingslib.widget.CandidateInfo;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Map;
54 
55 /**
56  * Fragment for picking accessibility shortcut service
57  */
58 public class ShortcutServicePickerFragment extends RadioButtonPickerFragment {
59 
60     @Override
getMetricsCategory()61     public int getMetricsCategory() {
62         return SettingsEnums.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE;
63     }
64 
65     @Override
getPreferenceScreenResId()66     protected int getPreferenceScreenResId() {
67         return R.xml.accessibility_shortcut_service_settings;
68     }
69 
70     @Override
getCandidates()71     protected List<? extends CandidateInfo> getCandidates() {
72         final Context context = getContext();
73         final AccessibilityManager accessibilityManager = context
74                 .getSystemService(AccessibilityManager.class);
75         final List<AccessibilityServiceInfo> installedServices =
76                 accessibilityManager.getInstalledAccessibilityServiceList();
77         final int numInstalledServices = installedServices.size();
78 
79         final List<CandidateInfo> candidates = new ArrayList<>(numInstalledServices);
80         Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureInfoMap =
81                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
82         for (ComponentName componentName : frameworkFeatureInfoMap.keySet()) {
83             final int iconId;
84             if (componentName.equals(COLOR_INVERSION_COMPONENT_NAME)) {
85                 iconId = R.drawable.ic_color_inversion;
86             } else if (componentName.equals(DALTONIZER_COMPONENT_NAME)) {
87                 iconId = R.drawable.ic_daltonizer;
88             } else {
89                 iconId = R.drawable.empty_icon;
90             }
91             candidates.add(new FrameworkCandidateInfo(frameworkFeatureInfoMap.get(componentName),
92                     iconId, componentName.flattenToString()));
93         }
94         for (int i = 0; i < numInstalledServices; i++) {
95             candidates.add(new ServiceCandidateInfo(installedServices.get(i)));
96         }
97 
98         return candidates;
99     }
100 
101     @Override
getDefaultKey()102     protected String getDefaultKey() {
103         String shortcutServiceString = AccessibilityUtils
104                 .getShortcutTargetServiceComponentNameString(getContext(), UserHandle.myUserId());
105         if (shortcutServiceString != null) {
106             ComponentName shortcutName = ComponentName.unflattenFromString(shortcutServiceString);
107             if (shortcutName != null) {
108                 return shortcutName.flattenToString();
109             }
110         }
111         return null;
112     }
113 
114     @Override
setDefaultKey(String key)115     protected boolean setDefaultKey(String key) {
116         Settings.Secure.putString(getContext().getContentResolver(),
117                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, key);
118         return true;
119     }
120 
121     @Override
onRadioButtonClicked(RadioButtonPreference selected)122     public void onRadioButtonClicked(RadioButtonPreference selected) {
123         final String selectedKey = selected.getKey();
124 
125         if (TextUtils.isEmpty(selectedKey)) {
126             super.onRadioButtonClicked(selected);
127         } else {
128             final ComponentName selectedComponent = ComponentName.unflattenFromString(selectedKey);
129             if (AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
130                     .containsKey(selectedComponent)) {
131                 // This is a framework feature. It doesn't need to be confirmed.
132                 onRadioButtonConfirmed(selectedKey);
133             } else {
134                 final FragmentActivity activity = getActivity();
135                 if (activity != null) {
136                     ConfirmationDialogFragment.newInstance(this, selectedKey)
137                             .show(activity.getSupportFragmentManager(),
138                                     ConfirmationDialogFragment.TAG);
139                 }
140             }
141         }
142     }
143 
onServiceConfirmed(String serviceKey)144     private void onServiceConfirmed(String serviceKey) {
145         onRadioButtonConfirmed(serviceKey);
146     }
147 
148     public static class ConfirmationDialogFragment extends InstrumentedDialogFragment
149             implements View.OnClickListener {
150         private static final String EXTRA_KEY = "extra_key";
151         private static final String TAG = "ConfirmationDialogFragment";
152         private IBinder mToken;
153 
newInstance(ShortcutServicePickerFragment parent, String key)154         public static ConfirmationDialogFragment newInstance(ShortcutServicePickerFragment parent,
155                 String key) {
156             final ConfirmationDialogFragment fragment = new ConfirmationDialogFragment();
157             final Bundle argument = new Bundle();
158             argument.putString(EXTRA_KEY, key);
159             fragment.setArguments(argument);
160             fragment.setTargetFragment(parent, 0);
161             fragment.mToken = new Binder();
162             return fragment;
163         }
164 
165         @Override
getMetricsCategory()166         public int getMetricsCategory() {
167             return SettingsEnums.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE;
168         }
169 
170         @Override
onCreateDialog(Bundle savedInstanceState)171         public Dialog onCreateDialog(Bundle savedInstanceState) {
172             final Bundle bundle = getArguments();
173             final String key = bundle.getString(EXTRA_KEY);
174             final ComponentName serviceComponentName = ComponentName.unflattenFromString(key);
175             final AccessibilityManager accessibilityManager = getActivity()
176                     .getSystemService(AccessibilityManager.class);
177             AccessibilityServiceInfo info = accessibilityManager
178                     .getInstalledServiceInfoWithComponentName(serviceComponentName);
179             return AccessibilityServiceWarning.createCapabilitiesDialog(getActivity(), info, this);
180         }
181 
182         @Override
onClick(View view)183         public void onClick(View view) {
184             final Fragment fragment = getTargetFragment();
185             if ((view.getId() == R.id.permission_enable_allow_button)
186                 && (fragment instanceof ShortcutServicePickerFragment)) {
187                 final Bundle bundle = getArguments();
188                 ((ShortcutServicePickerFragment) fragment).onServiceConfirmed(
189                         bundle.getString(EXTRA_KEY));
190             }
191             dismiss();
192         }
193     }
194 
195     private class FrameworkCandidateInfo extends CandidateInfo {
196         final ToggleableFrameworkFeatureInfo mToggleableFrameworkFeatureInfo;
197         final int mIconResId;
198         final String mKey;
199 
FrameworkCandidateInfo( ToggleableFrameworkFeatureInfo frameworkFeatureInfo, int iconResId, String key)200         public FrameworkCandidateInfo(
201                 ToggleableFrameworkFeatureInfo frameworkFeatureInfo, int iconResId, String key) {
202             super(true /* enabled */);
203             mToggleableFrameworkFeatureInfo = frameworkFeatureInfo;
204             mIconResId = iconResId;
205             mKey = key;
206         }
207 
208         @Override
loadLabel()209         public CharSequence loadLabel() {
210             return mToggleableFrameworkFeatureInfo.getLabel(getContext());
211         }
212 
213         @Override
loadIcon()214         public Drawable loadIcon() {
215             return getContext().getDrawable(mIconResId);
216         }
217 
218         @Override
getKey()219         public String getKey() {
220             return mKey;
221         }
222     }
223 
224     private class ServiceCandidateInfo extends CandidateInfo {
225         final AccessibilityServiceInfo mServiceInfo;
226 
ServiceCandidateInfo(AccessibilityServiceInfo serviceInfo)227         public ServiceCandidateInfo(AccessibilityServiceInfo serviceInfo) {
228             super(true /* enabled */);
229             mServiceInfo = serviceInfo;
230         }
231 
232         @Override
loadLabel()233         public CharSequence loadLabel() {
234             final PackageManager pmw = getContext().getPackageManager();
235             final CharSequence label =
236                     mServiceInfo.getResolveInfo().serviceInfo.loadLabel(pmw);
237             if (label != null) {
238                 return label;
239             }
240 
241             final ComponentName componentName = mServiceInfo.getComponentName();
242             if (componentName != null) {
243                 try {
244                     final ApplicationInfo appInfo = pmw.getApplicationInfoAsUser(
245                             componentName.getPackageName(), 0, UserHandle.myUserId());
246                     return appInfo.loadLabel(pmw);
247                 } catch (PackageManager.NameNotFoundException e) {
248                     return null;
249                 }
250             }
251             return null;
252         }
253 
254         @Override
loadIcon()255         public Drawable loadIcon() {
256             final ResolveInfo resolveInfo = mServiceInfo.getResolveInfo();
257             return (resolveInfo.getIconResource() == 0)
258                     ? getContext().getDrawable(R.drawable.ic_accessibility_generic)
259                     : resolveInfo.loadIcon(getContext().getPackageManager());
260         }
261 
262         @Override
getKey()263         public String getKey() {
264             return mServiceInfo.getComponentName().flattenToString();
265         }
266     }
267 }
268