1 /*
2  * Copyright (C) 2021 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.car.settings.accessibility;
18 
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.ServiceInfo;
23 import android.os.UserHandle;
24 import android.view.accessibility.AccessibilityManager;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.car.settings.R;
29 import com.android.internal.accessibility.util.AccessibilityUtils;
30 
31 import java.util.List;
32 
33 /**
34  * Hosts utility methods and constants for screen reader related settings.
35  */
36 public class ScreenReaderUtils {
37 
38     /**
39      * Returns whether the system screen reader is installed on the device.
40      */
isScreenReaderInstalled(Context context)41     static boolean isScreenReaderInstalled(Context context) {
42         return getScreenReaderServiceInfo(context) != null;
43     }
44 
45     /**
46      * Returns whether the default screen reader is currently enabled on the device.
47      */
isScreenReaderEnabled(Context context)48     static boolean isScreenReaderEnabled(Context context) {
49         ComponentName screenReaderComponent = getScreenReaderComponentName(context);
50         if (screenReaderComponent == null) {
51             return false;
52         }
53         return AccessibilityUtils.getEnabledServicesFromSettings(context,
54                 UserHandle.myUserId()).contains(screenReaderComponent);
55     }
56 
57     /**
58      * Returns the name of the system default screen reader if it is installed. Returns an empty
59      * string if not.
60      */
61     @Nullable
getScreenReaderName(Context context)62     static CharSequence getScreenReaderName(Context context) {
63         AccessibilityServiceInfo serviceInfo = getScreenReaderServiceInfo(context);
64         if (serviceInfo == null) {
65             return "";
66         }
67 
68         return serviceInfo.getResolveInfo().loadLabel(context.getPackageManager());
69     }
70 
71     /**
72      * Returns the description of the system default screen reader if it is installed. Returns an
73      * empty string if not.
74      */
getScreenReaderDescription(Context context)75     static CharSequence getScreenReaderDescription(Context context) {
76         AccessibilityServiceInfo serviceInfo = getScreenReaderServiceInfo(context);
77         if (serviceInfo == null) {
78             return "";
79         }
80 
81         String description = serviceInfo.loadDescription(context.getPackageManager());
82         return description != null ? description : "";
83     }
84 
85     /**
86      * Returns the settings activity ComponentName of the system default screen reader if the screen
87      * reader is installed and a settings activity exists. Returns {@code null} otherwise.
88      */
89     @Nullable
getScreenReaderSettingsActivity(Context context)90     static ComponentName getScreenReaderSettingsActivity(Context context) {
91         AccessibilityServiceInfo serviceInfo = getScreenReaderServiceInfo(context);
92         if (serviceInfo == null) {
93             return null;
94         }
95 
96         String settingsActivity = serviceInfo.getSettingsActivityName();
97         if (settingsActivity == null || settingsActivity.isEmpty()) {
98             return null;
99         }
100 
101         return new ComponentName(getScreenReaderComponentName(context).getPackageName(),
102                 settingsActivity);
103     }
104 
105     /**
106      * Sets the screen reader enabled state. This should only be called if the screen reader is
107      * installed.
108      */
setScreenReaderEnabled(Context context, boolean enabled)109     static void setScreenReaderEnabled(Context context, boolean enabled) {
110         ComponentName screenReaderComponent = getScreenReaderComponentName(context);
111         if (screenReaderComponent == null) {
112             return;
113         }
114 
115         AccessibilityUtils.setAccessibilityServiceState(context,
116                 getScreenReaderComponentName(context), enabled,
117                 UserHandle.myUserId());
118     }
119 
getScreenReaderComponentName(Context context)120     private static ComponentName getScreenReaderComponentName(Context context) {
121         return ComponentName.unflattenFromString(
122                 context.getString(R.string.config_default_screen_reader));
123     }
124 
125     @Nullable
getScreenReaderServiceInfo(Context context)126     private static AccessibilityServiceInfo getScreenReaderServiceInfo(Context context) {
127         AccessibilityManager accessibilityManager = context.getSystemService(
128                 AccessibilityManager.class);
129         if (accessibilityManager == null) {
130             return null;
131         }
132 
133         List<AccessibilityServiceInfo> accessibilityServices =
134                 accessibilityManager.getInstalledAccessibilityServiceList();
135         if (accessibilityServices == null) {
136             return null;
137         }
138 
139         ComponentName screenReaderComponent = getScreenReaderComponentName(context);
140         if (screenReaderComponent == null) {
141             return null;
142         }
143 
144         for (AccessibilityServiceInfo info : accessibilityServices) {
145             ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
146             if (screenReaderComponent.getPackageName().equals(serviceInfo.packageName)
147                     && screenReaderComponent.getClassName().equals(serviceInfo.name)) {
148                 return info;
149             }
150         }
151 
152         return null;
153     }
154 
155 }
156