blob: 84f7e89d9f2fe72f43d52dfc37c88bae84eb42c7 [file] [log] [blame]
Winson Chungfe1fa642017-03-13 10:51:22 -07001/*
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 */
16package com.android.systemui.pip.phone;
17
18import android.graphics.Rect;
19import android.graphics.Region;
20import android.os.Bundle;
21import android.os.Handler;
22import android.os.RemoteException;
23import android.view.MagnificationSpec;
24import android.view.accessibility.AccessibilityNodeInfo;
25import android.view.accessibility.AccessibilityWindowInfo;
26import android.view.accessibility.IAccessibilityInteractionConnection;
27import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Expose the touch actions to accessibility as if this object were a window with a single view.
34 * That pseudo-view exposes all of the actions this object can perform.
35 */
36public class PipAccessibilityInteractionConnection
37 extends IAccessibilityInteractionConnection.Stub {
38
39 public interface AccessibilityCallbacks {
40 void onAccessibilityShowMenu();
41 }
42
43 private static final long ACCESSIBILITY_NODE_ID = 1;
44 private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
45
46 private Handler mHandler;
47 private PipMotionHelper mMotionHelper;
48 private AccessibilityCallbacks mCallbacks;
49
50 private Rect mTmpBounds = new Rect();
51
52 public PipAccessibilityInteractionConnection(PipMotionHelper motionHelper,
53 AccessibilityCallbacks callbacks, Handler handler) {
54 mHandler = handler;
55 mMotionHelper = motionHelper;
56 mCallbacks = callbacks;
57 }
58
59 @Override
60 public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
61 Region interactiveRegion, int interactionId,
62 IAccessibilityInteractionConnectionCallback callback, int flags,
63 int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
64 try {
65 callback.setFindAccessibilityNodeInfosResult(
66 (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID)
67 ? getNodeList() : null, interactionId);
68 } catch (RemoteException re) {
69 /* best effort - ignore */
70 }
71 }
72
73 @Override
74 public void performAccessibilityAction(long accessibilityNodeId, int action,
75 Bundle arguments, int interactionId,
76 IAccessibilityInteractionConnectionCallback callback, int flags,
77 int interrogatingPid, long interrogatingTid) {
78 // We only support one view. A request for anything else is invalid
79 boolean result = false;
80 if (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID) {
81 switch (action) {
82 case AccessibilityNodeInfo.ACTION_CLICK:
83 mHandler.post(() -> {
84 mCallbacks.onAccessibilityShowMenu();
85 });
86 result = true;
87 break;
88 case AccessibilityNodeInfo.ACTION_DISMISS:
89 mMotionHelper.dismissPip();
90 result = true;
91 break;
92 case com.android.internal.R.id.accessibilityActionMoveWindow:
93 int newX = arguments.getInt(
94 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_X);
95 int newY = arguments.getInt(
96 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
97 Rect pipBounds = new Rect();
98 pipBounds.set(mMotionHelper.getBounds());
99 mTmpBounds.offsetTo(newX, newY);
100 mMotionHelper.movePip(mTmpBounds);
101 result = true;
102 break;
103 case AccessibilityNodeInfo.ACTION_EXPAND:
104 mMotionHelper.expandPip();
105 result = true;
106 break;
107 default:
108 // Leave result as false
109 }
110 }
111 try {
112 callback.setPerformAccessibilityActionResult(result, interactionId);
113 } catch (RemoteException re) {
114 /* best effort - ignore */
115 }
116 }
117
118 @Override
119 public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
120 String viewId, Region interactiveRegion, int interactionId,
121 IAccessibilityInteractionConnectionCallback callback, int flags,
122 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
123 // We have no view with a proper ID
124 try {
125 callback.setFindAccessibilityNodeInfoResult(null, interactionId);
126 } catch (RemoteException re) {
127 /* best effort - ignore */
128 }
129 }
130
131 @Override
132 public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
133 Region interactiveRegion, int interactionId,
134 IAccessibilityInteractionConnectionCallback callback, int flags,
135 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
136 // We have no view with text
137 try {
138 callback.setFindAccessibilityNodeInfoResult(null, interactionId);
139 } catch (RemoteException re) {
140 /* best effort - ignore */
141 }
142 }
143
144 @Override
145 public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
146 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
147 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
148 // We have no view that can take focus
149 try {
150 callback.setFindAccessibilityNodeInfoResult(null, interactionId);
151 } catch (RemoteException re) {
152 /* best effort - ignore */
153 }
154 }
155
156 @Override
157 public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
158 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
159 int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
160 // We have no view that can take focus
161 try {
162 callback.setFindAccessibilityNodeInfoResult(null, interactionId);
163 } catch (RemoteException re) {
164 /* best effort - ignore */
165 }
166 }
167
Rhed Jao23813d92018-12-18 21:30:52 +0800168 @Override
169 public void clearAccessibilityFocus() {
170 // We should not be here.
171 }
172
Jackal Guo8bcc0a92019-04-16 15:26:00 +0800173 @Override
174 public void notifyOutsideTouch() {
175 // Do nothing.
176 }
177
Winson Chungfe1fa642017-03-13 10:51:22 -0700178 public static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
179 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
180 info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
181 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
182 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
183 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
184 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
185 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
186 info.setImportantForAccessibility(true);
187 info.setClickable(true);
188 info.setVisibleToUser(true);
189 return info;
190 }
191
192 private List<AccessibilityNodeInfo> getNodeList() {
193 if (mAccessibilityNodeInfoList == null) {
194 mAccessibilityNodeInfoList = new ArrayList<>(1);
195 }
196 AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
197 mAccessibilityNodeInfoList.clear();
198 mAccessibilityNodeInfoList.add(info);
199 return mAccessibilityNodeInfoList;
200 }
201}