Home | History | Annotate | Download | only in accessibility
      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
     62     public int getMetricsCategory() {
     63         return MetricsEvent.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE;
     64     }
     65 
     66     @Override
     67     protected int getPreferenceScreenResId() {
     68         return R.xml.accessibility_shortcut_service_settings;
     69     }
     70 
     71     @Override
     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
    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
    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
    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 
    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 
    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
    166         public int getMetricsCategory() {
    167             return MetricsEvent.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE;
    168         }
    169 
    170         @Override
    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
    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 
    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
    207         public CharSequence loadLabel() {
    208             return mToggleableFrameworkFeatureInfo.getLabel(getContext());
    209         }
    210 
    211         @Override
    212         public Drawable loadIcon() {
    213             return getContext().getDrawable(mIconResId);
    214         }
    215 
    216         @Override
    217         public String getKey() {
    218             return mKey;
    219         }
    220     }
    221 
    222     private class ServiceCandidateInfo extends CandidateInfo {
    223         final AccessibilityServiceInfo mServiceInfo;
    224 
    225         public ServiceCandidateInfo(AccessibilityServiceInfo serviceInfo) {
    226             super(true /* enabled */);
    227             mServiceInfo = serviceInfo;
    228         }
    229 
    230         @Override
    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
    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
    262         public String getKey() {
    263             return mServiceInfo.getComponentName().flattenToString();
    264         }
    265     }
    266 }
    267