Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe1fa64b authored by Winson Chung's avatar Winson Chung
Browse files

Refactoring accessibility class out to own file.

Bug: 34773134
Test: Open PIP, turn on talk back and use it.
Change-Id: I8a73eb7230ee02afd16997513fe19c2f0805d094
parent 41eae911
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -98,6 +98,9 @@ public class InputConsumerController {
     */
    public void setRegistrationListener(RegistrationListener listener) {
        mRegistrationListener = listener;
        if (mRegistrationListener != null) {
            mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null);
        }
    }

    /**
@@ -122,6 +125,9 @@ public class InputConsumerController {
                Log.e(TAG, "Failed to create PIP input consumer", e);
            }
            mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
            if (mRegistrationListener != null) {
                mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
            }
        }
    }

@@ -137,6 +143,9 @@ public class InputConsumerController {
            }
            mInputEventReceiver.dispose();
            mInputEventReceiver = null;
            if (mRegistrationListener != null) {
                mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
            }
        }
    }

+191 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.systemui.pip.phone;

import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;

import java.util.ArrayList;
import java.util.List;

/**
 * Expose the touch actions to accessibility as if this object were a window with a single view.
 * That pseudo-view exposes all of the actions this object can perform.
 */
public class PipAccessibilityInteractionConnection
        extends IAccessibilityInteractionConnection.Stub {

    public interface AccessibilityCallbacks {
        void onAccessibilityShowMenu();
    }

    private static final long ACCESSIBILITY_NODE_ID = 1;
    private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;

    private Handler mHandler;
    private PipMotionHelper mMotionHelper;
    private AccessibilityCallbacks mCallbacks;

    private Rect mTmpBounds = new Rect();

    public PipAccessibilityInteractionConnection(PipMotionHelper motionHelper,
            AccessibilityCallbacks callbacks, Handler handler) {
        mHandler = handler;
        mMotionHelper = motionHelper;
        mCallbacks = callbacks;
    }

    @Override
    public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
            Region interactiveRegion, int interactionId,
            IAccessibilityInteractionConnectionCallback callback, int flags,
            int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
        try {
            callback.setFindAccessibilityNodeInfosResult(
                    (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID)
                            ? getNodeList() : null, interactionId);
        } catch (RemoteException re) {
                /* best effort - ignore */
        }
    }

    @Override
    public void performAccessibilityAction(long accessibilityNodeId, int action,
            Bundle arguments, int interactionId,
            IAccessibilityInteractionConnectionCallback callback, int flags,
            int interrogatingPid, long interrogatingTid) {
        // We only support one view. A request for anything else is invalid
        boolean result = false;
        if (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID) {
            switch (action) {
                case AccessibilityNodeInfo.ACTION_CLICK:
                    mHandler.post(() -> {
                        mCallbacks.onAccessibilityShowMenu();
                    });
                    result = true;
                    break;
                case AccessibilityNodeInfo.ACTION_DISMISS:
                    mMotionHelper.dismissPip();
                    result = true;
                    break;
                case com.android.internal.R.id.accessibilityActionMoveWindow:
                    int newX = arguments.getInt(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_X);
                    int newY = arguments.getInt(
                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
                    Rect pipBounds = new Rect();
                    pipBounds.set(mMotionHelper.getBounds());
                    mTmpBounds.offsetTo(newX, newY);
                    mMotionHelper.movePip(mTmpBounds);
                    result = true;
                    break;
                case AccessibilityNodeInfo.ACTION_EXPAND:
                    mMotionHelper.expandPip();
                    result = true;
                    break;
                default:
                    // Leave result as false
            }
        }
        try {
            callback.setPerformAccessibilityActionResult(result, interactionId);
        } catch (RemoteException re) {
                /* best effort - ignore */
        }
    }

    @Override
    public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
            String viewId, Region interactiveRegion, int interactionId,
            IAccessibilityInteractionConnectionCallback callback, int flags,
            int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
        // We have no view with a proper ID
        try {
            callback.setFindAccessibilityNodeInfoResult(null, interactionId);
        } catch (RemoteException re) {
            /* best effort - ignore */
        }
    }

    @Override
    public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
            Region interactiveRegion, int interactionId,
            IAccessibilityInteractionConnectionCallback callback, int flags,
            int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
        // We have no view with text
        try {
            callback.setFindAccessibilityNodeInfoResult(null, interactionId);
        } catch (RemoteException re) {
            /* best effort - ignore */
        }
    }

    @Override
    public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
            int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
            int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
        // We have no view that can take focus
        try {
            callback.setFindAccessibilityNodeInfoResult(null, interactionId);
        } catch (RemoteException re) {
            /* best effort - ignore */
        }
    }

    @Override
    public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
            int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
            int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
        // We have no view that can take focus
        try {
            callback.setFindAccessibilityNodeInfoResult(null, interactionId);
        } catch (RemoteException re) {
            /* best effort - ignore */
        }
    }

    public static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
        info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
                AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
        info.setImportantForAccessibility(true);
        info.setClickable(true);
        info.setVisibleToUser(true);
        return info;
    }

    private List<AccessibilityNodeInfo> getNodeList() {
        if (mAccessibilityNodeInfoList == null) {
            mAccessibilityNodeInfoList = new ArrayList<>(1);
        }
        AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
        mAccessibilityNodeInfoList.clear();
        mAccessibilityNodeInfoList.add(info);
        return mAccessibilityNodeInfoList;
    }
}
+10 −157
Original line number Diff line number Diff line
@@ -21,22 +21,16 @@ import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
import android.view.IPinnedStackController;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -45,8 +39,6 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
@@ -96,13 +88,6 @@ public class PipTouchHandler {
        }
    };

    private Runnable mShowMenuRunnable = new Runnable() {
        @Override
        public void run() {
            mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
        }
    };

    // Behaviour states
    private boolean mIsMenuVisible;
    private boolean mIsMinimized;
@@ -263,7 +248,12 @@ public class PipTouchHandler {

    private void onRegistrationChanged(boolean isRegistered) {
        mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
                ? new AccessibilityInteractionConnection() : null);
                ? new PipAccessibilityInteractionConnection(mMotionHelper,
                        this::onAccessibilityShowMenu, mHandler) : null);
    }

    private void onAccessibilityShowMenu() {
        mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
    }

    private boolean handleTouchEvent(MotionEvent ev) {
@@ -314,7 +304,8 @@ public class PipTouchHandler {
                if (!mSendingHoverAccessibilityEvents) {
                    AccessibilityEvent event = AccessibilityEvent.obtain(
                            AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                    AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
                    AccessibilityNodeInfo info =
                            PipAccessibilityInteractionConnection.obtainRootAccessibilityNodeInfo();
                    event.setSource(info);
                    info.recycle();
                    mAccessibilityManager.sendAccessibilityEvent(event);
@@ -326,7 +317,8 @@ public class PipTouchHandler {
                if (mSendingHoverAccessibilityEvents) {
                    AccessibilityEvent event = AccessibilityEvent.obtain(
                            AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                    AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
                    AccessibilityNodeInfo info =
                            PipAccessibilityInteractionConnection.obtainRootAccessibilityNodeInfo();
                    event.setSource(info);
                    info.recycle();
                    mAccessibilityManager.sendAccessibilityEvent(event);
@@ -625,143 +617,4 @@ public class PipTouchHandler {
        mMotionHelper.dump(pw, innerPrefix);
    }

    private static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() {
        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
        info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
                AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_MOVE_WINDOW);
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
        info.setImportantForAccessibility(true);
        info.setClickable(true);
        info.setVisibleToUser(true);
        return info;
    }

    /**
     * Expose the touch actions to accessibility as if this object were a window with a single view.
     * That pseudo-view exposes all of the actions this object can perform.
     */
    class AccessibilityInteractionConnection extends IAccessibilityInteractionConnection.Stub {
        static final long ACCESSIBILITY_NODE_ID = 1;
        List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;

        @Override
        public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
                Region interactiveRegion, int interactionId,
                IAccessibilityInteractionConnectionCallback callback, int flags,
                int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
            try {
                callback.setFindAccessibilityNodeInfosResult(
                        (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID)
                                ? getNodeList() : null, interactionId);
            } catch (RemoteException re) {
                    /* best effort - ignore */
            }
        }

        @Override
        public void performAccessibilityAction(long accessibilityNodeId, int action,
                Bundle arguments, int interactionId,
                IAccessibilityInteractionConnectionCallback callback, int flags,
                int interrogatingPid, long interrogatingTid) {
            // We only support one view. A request for anything else is invalid
            boolean result = false;
            if (accessibilityNodeId == AccessibilityNodeInfo.ROOT_NODE_ID) {
                switch (action) {
                    case AccessibilityNodeInfo.ACTION_CLICK:
                        mHandler.post(mShowMenuRunnable);
                        result = true;
                        break;
                    case AccessibilityNodeInfo.ACTION_DISMISS:
                        mMotionHelper.dismissPip();
                        result = true;
                        break;
                    case com.android.internal.R.id.accessibilityActionMoveWindow:
                        int newX = arguments.getInt(
                                AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_X);
                        int newY = arguments.getInt(
                                AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
                        Rect pipBounds = new Rect();
                        pipBounds.set(mMotionHelper.getBounds());
                        mTmpBounds.offsetTo(newX, newY);
                        mMotionHelper.movePip(mTmpBounds);
                        result = true;
                        break;
                    case AccessibilityNodeInfo.ACTION_EXPAND:
                        mMotionHelper.expandPip();
                        result = true;
                        break;
                    default:
                        // Leave result as false
                }
            }
            try {
                callback.setPerformAccessibilityActionResult(result, interactionId);
            } catch (RemoteException re) {
                    /* best effort - ignore */
            }
        }

        @Override
        public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
                String viewId, Region interactiveRegion, int interactionId,
                IAccessibilityInteractionConnectionCallback callback, int flags,
                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
            // We have no view with a proper ID
            try {
                callback.setFindAccessibilityNodeInfoResult(null, interactionId);
            } catch (RemoteException re) {
                /* best effort - ignore */
            }
        }

        @Override
        public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
                Region interactiveRegion, int interactionId,
                IAccessibilityInteractionConnectionCallback callback, int flags,
                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
            // We have no view with text
            try {
                callback.setFindAccessibilityNodeInfoResult(null, interactionId);
            } catch (RemoteException re) {
                /* best effort - ignore */
            }
        }

        @Override
        public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
                int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
            // We have no view that can take focus
            try {
                callback.setFindAccessibilityNodeInfoResult(null, interactionId);
            } catch (RemoteException re) {
                /* best effort - ignore */
            }
        }

        @Override
        public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
                int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
            // We have no view that can take focus
            try {
                callback.setFindAccessibilityNodeInfoResult(null, interactionId);
            } catch (RemoteException re) {
                /* best effort - ignore */
            }
        }

        private List<AccessibilityNodeInfo> getNodeList() {
            if (mAccessibilityNodeInfoList == null) {
                mAccessibilityNodeInfoList = new ArrayList<>(1);
            }
            AccessibilityNodeInfo info = obtainRootAccessibilityNodeInfo();
            mAccessibilityNodeInfoList.clear();
            mAccessibilityNodeInfoList.add(info);
            return mAccessibilityNodeInfoList;
        }
    }
}