• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.systemui.pip.phone;
17 
18 import android.graphics.Rect;
19 import android.graphics.Region;
20 import android.os.Bundle;
21 import android.os.Handler;
22 import android.os.RemoteException;
23 import android.view.MagnificationSpec;
24 import android.view.accessibility.AccessibilityNodeInfo;
25 import android.view.accessibility.AccessibilityWindowInfo;
26 import android.view.accessibility.IAccessibilityInteractionConnection;
27 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
28 
29 import java.util.ArrayList;
30 import 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  */
36 public class PipAccessibilityInteractionConnection
37         extends IAccessibilityInteractionConnection.Stub {
38 
39     public interface AccessibilityCallbacks {
onAccessibilityShowMenu()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 
PipAccessibilityInteractionConnection(PipMotionHelper motionHelper, AccessibilityCallbacks callbacks, Handler handler)52     public PipAccessibilityInteractionConnection(PipMotionHelper motionHelper,
53             AccessibilityCallbacks callbacks, Handler handler) {
54         mHandler = handler;
55         mMotionHelper = motionHelper;
56         mCallbacks = callbacks;
57     }
58 
59     @Override
findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args)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
performAccessibilityAction(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid)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
findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)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
findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)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
findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)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
focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec)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 
168     @Override
clearAccessibilityFocus()169     public void clearAccessibilityFocus() {
170         // We should not be here.
171     }
172 
173     @Override
notifyOutsideTouch()174     public void notifyOutsideTouch() {
175         // Do nothing.
176     }
177 
obtainRootAccessibilityNodeInfo()178     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 
getNodeList()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 }
202