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

Commit fa2f004f authored by chaviw's avatar chaviw
Browse files

Add onPointerDownOutsideFocus for events outside the focused window (4/4)

Moved the logic that was handled in the TaskTapPointerEventListener that
handled switching displays when focus change into
WMS.onPointerDownOutsideFocus. The new method is called when InputDispatcher
notifies system server that a non focused window received a pointer down input
event. This allows WMS to check if the new window can receive focus before
changing display focus.

Test: Back button with ActivityView closes IME instead of entire
Activity.
Test: WindowFocusTests#testTapFocusableWindow,testTapNonFocusableWindow
Bug: 122535136

Change-Id: I4273ebc9b6d0eaf572864ad81461e6017053cfab
parent 29e5ea67
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -1810,6 +1810,7 @@ public class InputManagerService extends IInputManager.Stub

    // Native callback.
    private void onPointerDownOutsideFocus(IBinder touchedToken) {
        mWindowManagerCallbacks.onPointerDownOutsideFocus(touchedToken);
    }

    // Native callback.
@@ -2024,6 +2025,14 @@ public class InputManagerService extends IInputManager.Stub
        public int getPointerLayer();

        public int getPointerDisplayId();

        /**
         * Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
         * occurred on a window that did not have focus.
         *
         * @param touchedToken The token for the window that received the input event.
         */
        void onPointerDownOutsideFocus(IBinder touchedToken);
    }

    /**
+6 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;

import android.os.Debug;
import android.os.IBinder;
@@ -232,6 +233,11 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
        }
    }

    @Override
    public void onPointerDownOutsideFocus(IBinder touchedToken) {
        mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
    }

    /** Waits until the built-in input devices have been configured. */
    public boolean waitForInputDevicesReady(long timeoutMillis) {
        synchronized (mInputDevicesReadyMonitor) {
+0 −71
Original line number Diff line number Diff line
@@ -22,12 +22,9 @@ import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;

import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;

import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants.PointerEventListener;

@@ -40,67 +37,15 @@ import com.android.server.wm.WindowManagerService.H;
public class TaskTapPointerEventListener implements PointerEventListener {

    private final Region mTouchExcludeRegion = new Region();
    private final Region mTmpRegion = new Region();
    private final WindowManagerService mService;
    private final DisplayContent mDisplayContent;
    private final Handler mHandler;
    private final Runnable mMoveDisplayToTop;
    private final Rect mTmpRect = new Rect();
    private int mPointerIconType = TYPE_NOT_SPECIFIED;
    private int mLastDownX;
    private int mLastDownY;

    public TaskTapPointerEventListener(WindowManagerService service,
            DisplayContent displayContent) {
        mService = service;
        mDisplayContent = displayContent;
        mHandler = new Handler(mService.mH.getLooper());
        mMoveDisplayToTop = () -> {
            int x;
            int y;
            synchronized (this) {
                x = mLastDownX;
                y = mLastDownY;
            }
            synchronized (mService.mGlobalLock) {
                if (!mService.mPerDisplayFocusEnabled
                        && mService.mRoot.getTopFocusedDisplayContent() != mDisplayContent
                        && inputMethodWindowContains(x, y)) {
                    // In a single focus system, if the input method window and the input method
                    // target window are on the different displays, when the user is tapping on the
                    // input method window, we don't move its display to top. Otherwise, the input
                    // method target window will lose the focus.
                    return;
                }
                final Region windowTapExcludeRegion = Region.obtain();
                mDisplayContent.amendWindowTapExcludeRegion(windowTapExcludeRegion);
                if (windowTapExcludeRegion.contains(x, y)) {
                    windowTapExcludeRegion.recycle();
                    // The user is tapping on the window tap exclude region. We don't move this
                    // display to top. A window tap exclude region, for example, may be set by an
                    // ActivityView, and the region would match the bounds of both the ActivityView
                    // and the virtual display in it. In this case, we would take the tap that is on
                    // the embedded virtual display instead of this display.
                    return;
                }
                windowTapExcludeRegion.recycle();
                WindowContainer parent = mDisplayContent.getParent();
                if (parent != null && parent.getTopChild() != mDisplayContent) {
                    parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent,
                            true /* includingParents */);
                    // For compatibility, only the topmost activity is allowed to be resumed for
                    // pre-Q app. Ensure the topmost activities are resumed whenever a display is
                    // moved to top.
                    // TODO(b/123761773): Investigate whether we can move this into
                    // RootActivityContainer#updateTopResumedActivityIfNeeded(). Currently, it is
                    // risky to do so because it seems possible to resume activities as part of a
                    // larger transaction and it's too early to resume based on current order
                    // when performing updateTopResumedActivityIfNeeded().
                    mDisplayContent.mAcitvityDisplay.ensureActivitiesVisible(null /* starting */,
                            0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
                }
            }
        };
    }

    @Override
@@ -115,9 +60,6 @@ public class TaskTapPointerEventListener implements PointerEventListener {
                        mService.mTaskPositioningController.handleTapOutsideTask(
                                mDisplayContent, x, y);
                    }
                    mLastDownX = x;
                    mLastDownY = y;
                    mHandler.post(mMoveDisplayToTop);
                }
            }
            break;
@@ -178,17 +120,4 @@ public class TaskTapPointerEventListener implements PointerEventListener {
           mTouchExcludeRegion.set(newRegion);
        }
    }

    private int getDisplayId() {
        return mDisplayContent.getDisplayId();
    }

    private boolean inputMethodWindowContains(int x, int y) {
        final WindowState inputMethodWindow = mDisplayContent.mInputMethodWindow;
        if (inputMethodWindow == null || !inputMethodWindow.isVisibleLw()) {
            return false;
        }
        inputMethodWindow.getTouchableRegion(mTmpRegion);
        return mTmpRegion.contains(x, y);
    }
}
+42 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -4519,6 +4520,7 @@ public class WindowManagerService extends IWindowManager.Stub
        public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
        public static final int ANIMATION_FAILSAFE = 60;
        public static final int RECOMPUTE_FOCUS = 61;
        public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;

        /**
         * Used to denote that an integer field in a message will not be used.
@@ -4910,6 +4912,13 @@ public class WindowManagerService extends IWindowManager.Stub
                    }
                    break;
                }
                case ON_POINTER_DOWN_OUTSIDE_FOCUS: {
                    synchronized (mGlobalLock) {
                        final IBinder touchedToken = (IBinder) msg.obj;
                        onPointerDownOutsideFocusLocked(touchedToken);
                    }
                    break;
                }
            }
            if (DEBUG_WINDOW_TRACE) {
                Slog.v(TAG_WM, "handleMessage: exit");
@@ -7569,4 +7578,37 @@ public class WindowManagerService extends IWindowManager.Stub
            mGlobalLock.notifyAll();
        }
    }

    private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
        final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
        if (touchedWindow == null) {
            return;
        }

        final DisplayContent displayContent = touchedWindow.getDisplayContent();
        if (displayContent == null) {
            return;
        }

        if (!touchedWindow.canReceiveKeys()) {
            // If the window that received the input event cannot receive keys, don't move the
            // display it's on to the top since that window won't be able to get focus anyway.
            return;
        }

        final WindowContainer parent = displayContent.getParent();
        if (parent != null && parent.getTopChild() != displayContent) {
            parent.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
                    true /* includingParents */);
            // For compatibility, only the topmost activity is allowed to be resumed for pre-Q
            // app. Ensure the topmost activities are resumed whenever a display is moved to top.
            // TODO(b/123761773): Investigate whether we can move this into
            // RootActivityContainer#updateTopResumedActivityIfNeeded(). Currently, it is risky
            // to do so because it seems possible to resume activities as part of a larger
            // transaction and it's too early to resume based on current order when performing
            // updateTopResumedActivityIfNeeded().
            displayContent.mAcitvityDisplay.ensureActivitiesVisible(null /* starting */,
                    0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
        }
    }
}