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 
17 package com.android.launcher3.compat;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.text.TextUtils;
22 import android.util.Log;
23 import android.view.View;
24 import android.view.accessibility.AccessibilityEvent;
25 import android.view.accessibility.AccessibilityManager;
26 
27 import androidx.annotation.Nullable;
28 
29 import com.android.launcher3.Utilities;
30 import com.android.launcher3.testing.TestProtocol;
31 
32 public class AccessibilityManagerCompat {
33 
isAccessibilityEnabled(Context context)34     public static boolean isAccessibilityEnabled(Context context) {
35         return getManager(context).isEnabled();
36     }
37 
isObservedEventType(Context context, int eventType)38     public static boolean isObservedEventType(Context context, int eventType) {
39         // TODO: Use new API once available
40         return isAccessibilityEnabled(context);
41     }
42 
43     /**
44      * @param target The view the accessibility event is initialized on.
45      *               If null, this method has no effect.
46      * @param type   See TYPE_ constants defined in {@link AccessibilityEvent}.
47      * @param text   Optional text to add to the event, which will be announced to the user.
48      */
sendCustomAccessibilityEvent(@ullable View target, int type, @Nullable String text)49     public static void sendCustomAccessibilityEvent(@Nullable View target, int type,
50             @Nullable String text) {
51         if (target != null && isObservedEventType(target.getContext(), type)) {
52             AccessibilityEvent event = AccessibilityEvent.obtain(type);
53             target.onInitializeAccessibilityEvent(event);
54             if (!TextUtils.isEmpty(text)) {
55                 event.getText().add(text);
56             }
57             getManager(target.getContext()).sendAccessibilityEvent(event);
58         }
59     }
60 
getManager(Context context)61     private static AccessibilityManager getManager(Context context) {
62         return (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
63     }
64 
sendStateEventToTest(Context context, int stateOrdinal)65     public static void sendStateEventToTest(Context context, int stateOrdinal) {
66         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
67         if (accessibilityManager == null) return;
68 
69         final Bundle parcel = new Bundle();
70         parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
71 
72         sendEventToTest(
73                 accessibilityManager, context, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
74         Log.d(TestProtocol.PERMANENT_DIAG_TAG, "sendStateEventToTest: " + stateOrdinal);
75     }
76 
sendScrollFinishedEventToTest(Context context)77     public static void sendScrollFinishedEventToTest(Context context) {
78         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
79         if (accessibilityManager == null) return;
80 
81         sendEventToTest(accessibilityManager, context, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
82     }
83 
sendPauseDetectedEventToTest(Context context)84     public static void sendPauseDetectedEventToTest(Context context) {
85         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
86         if (accessibilityManager == null) return;
87 
88         sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
89     }
90 
sendDismissAnimationEndsEventToTest(Context context)91     public static void sendDismissAnimationEndsEventToTest(Context context) {
92         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
93         if (accessibilityManager == null) return;
94 
95         sendEventToTest(accessibilityManager, context, TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE,
96                 null);
97     }
98 
99     /**
100      * Notify running tests of a folder opened.
101      */
sendFolderOpenedEventToTest(Context context)102     public static void sendFolderOpenedEventToTest(Context context) {
103         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
104         if (accessibilityManager == null) return;
105 
106         sendEventToTest(accessibilityManager, context, TestProtocol.FOLDER_OPENED_MESSAGE, null);
107     }
108 
sendEventToTest( AccessibilityManager accessibilityManager, Context context, String eventTag, Bundle data)109     private static void sendEventToTest(
110             AccessibilityManager accessibilityManager,
111             Context context, String eventTag, Bundle data) {
112         final AccessibilityEvent e = AccessibilityEvent.obtain(
113                 AccessibilityEvent.TYPE_ANNOUNCEMENT);
114         e.setClassName(eventTag);
115         e.setParcelableData(data);
116         e.setPackageName(context.getApplicationContext().getPackageName());
117         accessibilityManager.sendAccessibilityEvent(e);
118     }
119 
120     /**
121      * Returns accessibility manager to be used for communication with UI Automation tests.
122      * The tests may exchange custom accessibility messages with the launcher; the accessibility
123      * manager is used in these communications.
124      *
125      * If the launcher runs not under a test, the return is null, and no attempt to process or send
126      * custom accessibility messages should be made.
127      */
getAccessibilityManagerForTest(Context context)128     private static AccessibilityManager getAccessibilityManagerForTest(Context context) {
129         // If not running in a test harness, don't participate in test exchanges.
130         if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return null;
131 
132         final AccessibilityManager accessibilityManager = getManager(context);
133         if (!accessibilityManager.isEnabled()) return null;
134 
135         return accessibilityManager;
136     }
137 
getRecommendedTimeoutMillis(Context context, int originalTimeout, int flags)138     public static int getRecommendedTimeoutMillis(Context context, int originalTimeout, int flags) {
139         if (Utilities.ATLEAST_Q) {
140             return getManager(context).getRecommendedTimeoutMillis(originalTimeout, flags);
141         }
142         return originalTimeout;
143     }
144 }
145