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.AccessibilityShortcutInfo;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
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 activity in a preference list
43  */
44 public class AccessibilityActivityPreference extends RestrictedPreference {
45     private static final String LOG_TAG = AccessibilityActivityPreference.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 static final String TARGET_FRAGMENT =
49             LaunchAccessibilityActivityPreferenceFragment.class.getName();
50     private final AccessibilityShortcutInfo mA11yShortcutInfo;
51     private final PackageManager mPm;
52     private final ComponentName mComponentName;
53     private final CharSequence mLabel;
54     private final ListenableFuture mExtraArgumentsFuture;
55 
AccessibilityActivityPreference(Context context, String packageName, int uid, AccessibilityShortcutInfo a11yShortcutInfo)56     public AccessibilityActivityPreference(Context context, String packageName, int uid,
57             AccessibilityShortcutInfo a11yShortcutInfo) {
58         super(context, packageName, uid);
59         mPm = context.getPackageManager();
60         mA11yShortcutInfo = a11yShortcutInfo;
61         mComponentName = a11yShortcutInfo.getComponentName();
62         mLabel = a11yShortcutInfo.getActivityInfo().loadLabel(mPm);
63         // setup basic info for a preference
64         setKey(mComponentName.flattenToString());
65         setTitle(mLabel);
66         setSummary(a11yShortcutInfo.loadSummary(mPm));
67         setFragment(TARGET_FRAGMENT);
68         setIconSize(ICON_SIZE_MEDIUM);
69         setIconSpaceReserved(true);
70         setPersistent(false); // Disable SharedPreferences.
71         setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
72 
73         // kick off image loading tasks
74         ThreadUtils.postOnBackgroundThread(() -> {
75             final Drawable icon = getA11yActivityIcon();
76             ThreadUtils.getUiThreadHandler().post(() -> this.setIcon(icon));
77         });
78 
79         final Bundle extras = getExtras();
80         extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, mComponentName);
81 
82         mExtraArgumentsFuture = ThreadUtils.postOnBackgroundThread(this::setupDataForOpenFragment);
83     }
84 
85     @Override
performClick()86     public void performClick() {
87         try {
88             mExtraArgumentsFuture.get();
89         } catch (InterruptedException | ExecutionException e) {
90             Log.e(LOG_TAG,
91                     "Unable to finish grabbing necessary arguments to open the fragment: "
92                             + "componentName: " + mComponentName);
93         }
94         super.performClick();
95     }
96 
97     /**
98      * Returns the label of the Accessibility Activity
99      */
getLabel()100     public CharSequence getLabel() {
101         return mLabel;
102     }
103 
getA11yActivityIcon()104     private Drawable getA11yActivityIcon() {
105         ActivityInfo activityInfo = mA11yShortcutInfo.getActivityInfo();
106         Drawable serviceIcon;
107         if (activityInfo.getIconResource() == 0) {
108             serviceIcon = ContextCompat.getDrawable(getContext(),
109                     R.drawable.ic_accessibility_generic);
110         } else {
111             serviceIcon = activityInfo.loadIcon(mPm);
112         }
113         return Utils.getAdaptiveIcon(getContext(), serviceIcon, Color.WHITE);
114     }
115 
setupDataForOpenFragment()116     private void setupDataForOpenFragment() {
117         final String prefKey = getKey();
118         final int imageRes = mA11yShortcutInfo.getAnimatedImageRes();
119         final CharSequence intro = mA11yShortcutInfo.loadIntro(mPm);
120         final CharSequence description = mA11yShortcutInfo.loadDescription(mPm);
121         final String htmlDescription = mA11yShortcutInfo.loadHtmlDescription(mPm);
122         final String settingsClassName = mA11yShortcutInfo.getSettingsActivityName();
123         final String tileServiceClassName = mA11yShortcutInfo.getTileServiceName();
124         final int metricsCategory = FeatureFactory.getFeatureFactory()
125                 .getAccessibilityMetricsFeatureProvider()
126                 .getDownloadedFeatureMetricsCategory(mComponentName);
127 
128         ThreadUtils.getUiThreadHandler().post(() -> {
129             RestrictedPreferenceHelper.putBasicExtras(
130                     this, prefKey, getTitle(), intro, description, imageRes,
131                     htmlDescription, mComponentName, metricsCategory);
132             RestrictedPreferenceHelper.putSettingsExtras(this, getPackageName(), settingsClassName);
133             RestrictedPreferenceHelper.putTileServiceExtras(
134                     this, getPackageName(), tileServiceClassName);
135         });
136     }
137 }
138