• 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 android.content.DialogInterface.BUTTON_POSITIVE;
19 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
20 import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
21 
22 import android.accessibilityservice.AccessibilityServiceInfo;
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.app.Fragment;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import android.graphics.drawable.Drawable;
33 import android.os.Binder;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.text.TextUtils;
39 import android.view.accessibility.AccessibilityManager;
40 
41 import com.android.internal.accessibility.AccessibilityShortcutController;
42 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
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 import com.android.settingslib.wrapper.PackageManagerWrapper;
51 
52 import java.util.ArrayList;
53 import java.util.List;
54 import java.util.Map;
55 
56 /**
57  * Fragment for picking accessibility shortcut service
58  */
59 public class ShortcutServicePickerFragment extends RadioButtonPickerFragment {
60 
61     @Override
getMetricsCategory()62     public int getMetricsCategory() {
63         return MetricsEvent.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE;
64     }
65 
66     @Override
getPreferenceScreenResId()67     protected int getPreferenceScreenResId() {
68         return R.xml.accessibility_shortcut_service_settings;
69     }
70 
71     @Override
getCandidates()72     protected List<? extends CandidateInfo> getCandidates() {
73         final Context context = getContext();
74         final AccessibilityManager accessibilityManager = context
75                 .getSystemService(AccessibilityManager.class);
76         final List<AccessibilityServiceInfo> installedServices =
77                 accessibilityManager.getInstalledAccessibilityServiceList();
78         final int numInstalledServices = installedServices.size();
79 
80         final List<CandidateInfo> candidates = new ArrayList<>(numInstalledServices);
81         Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureInfoMap =
82                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
83         for (ComponentName componentName : frameworkFeatureInfoMap.keySet()) {
84             final int iconId;
85             if (componentName.equals(COLOR_INVERSION_COMPONENT_NAME)) {
86                 iconId = R.drawable.ic_color_inversion;
87             } else if (componentName.equals(DALTONIZER_COMPONENT_NAME)) {
88                 iconId = R.drawable.ic_daltonizer;
89             } else {
90                 iconId = R.drawable.empty_icon;
91             }
92             candidates.add(new FrameworkCandidateInfo(frameworkFeatureInfoMap.get(componentName),
93                     iconId, componentName.flattenToString()));
94         }
95         for (int i = 0; i < numInstalledServices; i++) {
96             candidates.add(new ServiceCandidateInfo(installedServices.get(i)));
97         }
98 
99         return candidates;
100     }
101 
102     @Override
getDefaultKey()103     protected String getDefaultKey() {
104         String shortcutServiceString = AccessibilityUtils
105                 .getShortcutTargetServiceComponentNameString(getContext(), UserHandle.myUserId());
106         if (shortcutServiceString != null) {
107             ComponentName shortcutName = ComponentName.unflattenFromString(shortcutServiceString);
108             if (shortcutName != null) {
109                 return shortcutName.flattenToString();
110             }
111         }
112         return null;
113     }
114 
115     @Override
setDefaultKey(String key)116     protected boolean setDefaultKey(String key) {
117         Settings.Secure.putString(getContext().getContentResolver(),
118                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, key);
119         return true;
120     }
121 
122     @Override
onRadioButtonClicked(RadioButtonPreference selected)123     public void onRadioButtonClicked(RadioButtonPreference selected) {
124         final String selectedKey = selected.getKey();
125 
126         if (TextUtils.isEmpty(selectedKey)) {
127             super.onRadioButtonClicked(selected);
128         } else {
129             final ComponentName selectedComponent = ComponentName.unflattenFromString(selectedKey);
130             if (AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
131                     .containsKey(selectedComponent)) {
132                 // This is a framework feature. It doesn't need to be confirmed.
133                 onRadioButtonConfirmed(selectedKey);
134             } else {
135                 final Activity activity = getActivity();
136                 if (activity != null) {
137                     ConfirmationDialogFragment.newInstance(this, selectedKey)
138                             .show(activity.getFragmentManager(), 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 DialogInterface.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 MetricsEvent.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(DialogInterface dialog, int which)183         public void onClick(DialogInterface dialog, int which) {
184             final Fragment fragment = getTargetFragment();
185             if ((which == BUTTON_POSITIVE) && (fragment instanceof ShortcutServicePickerFragment)) {
186                 final Bundle bundle = getArguments();
187                 ((ShortcutServicePickerFragment) fragment).onServiceConfirmed(
188                         bundle.getString(EXTRA_KEY));
189             }
190         }
191     }
192 
193     private class FrameworkCandidateInfo extends CandidateInfo {
194         final ToggleableFrameworkFeatureInfo mToggleableFrameworkFeatureInfo;
195         final int mIconResId;
196         final String mKey;
197 
FrameworkCandidateInfo( ToggleableFrameworkFeatureInfo frameworkFeatureInfo, int iconResId, String key)198         public FrameworkCandidateInfo(
199                 ToggleableFrameworkFeatureInfo frameworkFeatureInfo, int iconResId, String key) {
200             super(true /* enabled */);
201             mToggleableFrameworkFeatureInfo = frameworkFeatureInfo;
202             mIconResId = iconResId;
203             mKey = key;
204         }
205 
206         @Override
loadLabel()207         public CharSequence loadLabel() {
208             return mToggleableFrameworkFeatureInfo.getLabel(getContext());
209         }
210 
211         @Override
loadIcon()212         public Drawable loadIcon() {
213             return getContext().getDrawable(mIconResId);
214         }
215 
216         @Override
getKey()217         public String getKey() {
218             return mKey;
219         }
220     }
221 
222     private class ServiceCandidateInfo extends CandidateInfo {
223         final AccessibilityServiceInfo mServiceInfo;
224 
ServiceCandidateInfo(AccessibilityServiceInfo serviceInfo)225         public ServiceCandidateInfo(AccessibilityServiceInfo serviceInfo) {
226             super(true /* enabled */);
227             mServiceInfo = serviceInfo;
228         }
229 
230         @Override
loadLabel()231         public CharSequence loadLabel() {
232             final PackageManagerWrapper pmw =
233                     new PackageManagerWrapper(getContext().getPackageManager());
234             final CharSequence label =
235                     mServiceInfo.getResolveInfo().serviceInfo.loadLabel(pmw.getPackageManager());
236             if (label != null) {
237                 return label;
238             }
239 
240             final ComponentName componentName = mServiceInfo.getComponentName();
241             if (componentName != null) {
242                 try {
243                     final ApplicationInfo appInfo = pmw.getApplicationInfoAsUser(
244                             componentName.getPackageName(), 0, UserHandle.myUserId());
245                     return appInfo.loadLabel(pmw.getPackageManager());
246                 } catch (PackageManager.NameNotFoundException e) {
247                     return null;
248                 }
249             }
250             return null;
251         }
252 
253         @Override
loadIcon()254         public Drawable loadIcon() {
255             final ResolveInfo resolveInfo = mServiceInfo.getResolveInfo();
256             return (resolveInfo.getIconResource() == 0)
257                     ? getContext().getDrawable(R.mipmap.ic_accessibility_generic)
258                     : resolveInfo.loadIcon(getContext().getPackageManager());
259         }
260 
261         @Override
getKey()262         public String getKey() {
263             return mServiceInfo.getComponentName().flattenToString();
264         }
265     }
266 }
267