1 /*
2  * Copyright (C) 2024 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.settings.accessibility;
18 
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.graphics.Color;
25 import android.graphics.drawable.Drawable;
26 import android.os.Bundle;
27 import android.util.Log;
28 
29 import androidx.core.content.ContextCompat;
30 
31 import com.android.settings.R;
32 import com.android.settings.Utils;
33 import com.android.settings.overlay.FeatureFactory;
34 import com.android.settingslib.RestrictedPreference;
35 import com.android.settingslib.utils.ThreadUtils;
36 
37 import com.google.common.util.concurrent.ListenableFuture;
38 
39 import java.util.concurrent.ExecutionException;
40 
41 /**
42  * Preference item for showing an accessibility service in a preference list
43  */
44 public class AccessibilityServicePreference extends RestrictedPreference {
45     private static final String LOG_TAG = AccessibilityServicePreference.class.getSimpleName();
46     // Index of the first preference in a preference category.
47     private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
48     private final PackageManager mPm;
49     private final AccessibilityServiceInfo mA11yServiceInfo;
50     private final ComponentName mComponentName;
51     private final boolean mServiceEnabled;
52     private final ListenableFuture mExtraArgumentsFuture;
53 
AccessibilityServicePreference(Context context, String packageName, int uid, AccessibilityServiceInfo a11yServiceInfo, boolean serviceEnabled)54     public AccessibilityServicePreference(Context context, String packageName, int uid,
55             AccessibilityServiceInfo a11yServiceInfo, boolean serviceEnabled) {
56         super(context, packageName, uid);
57         mPm = context.getPackageManager();
58         mA11yServiceInfo = a11yServiceInfo;
59         mServiceEnabled = serviceEnabled;
60         mComponentName = new ComponentName(packageName,
61                 mA11yServiceInfo.getResolveInfo().serviceInfo.name);
62         // setup basic info for a preference
63         setKey(mComponentName.flattenToString());
64         setTitle(mA11yServiceInfo.getResolveInfo().loadLabel(mPm));
65         setSummary(AccessibilitySettings.getServiceSummary(
66                 getContext(), mA11yServiceInfo, mServiceEnabled));
67         setFragment(RestrictedPreferenceHelper.getAccessibilityServiceFragmentTypeName(
68                 mA11yServiceInfo));
69         setIconSize(ICON_SIZE_MEDIUM);
70         setIconSpaceReserved(true);
71         setPersistent(false); // Disable SharedPreferences.
72         setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
73 
74         // kick off image loading tasks
75         ThreadUtils.postOnBackgroundThread(() -> {
76             final Drawable icon = getA11yServiceIcon();
77             ThreadUtils.getUiThreadHandler().post(() -> this.setIcon(icon));
78         });
79 
80         final Bundle extras = getExtras();
81         extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, mComponentName);
82 
83         mExtraArgumentsFuture = ThreadUtils.postOnBackgroundThread(this::setupDataForOpenFragment);
84     }
85 
86     @Override
performClick()87     public void performClick() {
88         try {
89             mExtraArgumentsFuture.get();
90         } catch (InterruptedException | ExecutionException e) {
91             Log.e(LOG_TAG,
92                     "Unable to finish grabbing necessary arguments to open the fragment: "
93                             + "componentName: " + mComponentName);
94         }
95         super.performClick();
96     }
97 
getA11yServiceIcon()98     private Drawable getA11yServiceIcon() {
99         ResolveInfo resolveInfo = mA11yServiceInfo.getResolveInfo();
100         Drawable serviceIcon;
101         if (resolveInfo.getIconResource() == 0) {
102             serviceIcon = ContextCompat.getDrawable(getContext(),
103                     R.drawable.ic_accessibility_generic);
104         } else {
105             serviceIcon = resolveInfo.loadIcon(mPm);
106         }
107         return Utils.getAdaptiveIcon(getContext(), serviceIcon, Color.WHITE);
108     }
109 
setupDataForOpenFragment()110     private void setupDataForOpenFragment() {
111         final String prefKey = getKey();
112         final int imageRes = mA11yServiceInfo.getAnimatedImageRes();
113         final CharSequence intro = mA11yServiceInfo.loadIntro(mPm);
114         final CharSequence description = AccessibilitySettings.getServiceDescription(
115                 getContext(), mA11yServiceInfo, mServiceEnabled);
116         final String htmlDescription = mA11yServiceInfo.loadHtmlDescription(mPm);
117         final String settingsClassName = mA11yServiceInfo.getSettingsActivityName();
118         final String tileServiceClassName = mA11yServiceInfo.getTileServiceName();
119         final ResolveInfo resolveInfo = mA11yServiceInfo.getResolveInfo();
120         final int metricsCategory = FeatureFactory.getFeatureFactory()
121                 .getAccessibilityMetricsFeatureProvider()
122                 .getDownloadedFeatureMetricsCategory(mComponentName);
123         ThreadUtils.getUiThreadHandler().post(() -> {
124             RestrictedPreferenceHelper.putBasicExtras(
125                     this, prefKey, getTitle(), intro, description, imageRes,
126                     htmlDescription, mComponentName, metricsCategory);
127             RestrictedPreferenceHelper.putServiceExtras(this, resolveInfo, mServiceEnabled);
128             RestrictedPreferenceHelper.putSettingsExtras(this, getPackageName(), settingsClassName);
129             RestrictedPreferenceHelper.putTileServiceExtras(
130                     this, getPackageName(), tileServiceClassName);
131         });
132     }
133 }
134