Home | History | Annotate | Download | only in phone
      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 {
     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 
    168     public static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
    169         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
    170         info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
    171                 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
    172         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
    173         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
    174         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
    175         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
    176         info.setImportantForAccessibility(true);
    177         info.setClickable(true);
    178         info.setVisibleToUser(true);
    179         return info;
    180     }
    181 
    182     private List<AccessibilityNodeInfo> getNodeList() {
    183         if (mAccessibilityNodeInfoList == null) {
    184             mAccessibilityNodeInfoList = new ArrayList<>(1);
    185         }
    186         AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
    187         mAccessibilityNodeInfoList.clear();
    188         mAccessibilityNodeInfoList.add(info);
    189         return mAccessibilityNodeInfoList;
    190     }
    191 }
    192