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

Commit a9b24fb2 authored by Garfield Tan's avatar Garfield Tan
Browse files

Use spy windows to resize tasks

This CL does a few things to achieve the goal:

1. It converts the input windows of decor container layers to spy
   windows, so that input windows behind them can receive input events
   at the same time. This is what allows the close button in the caption
   to receive mouse clicks even if the mouse clicks in the intersected
   area of the close button and the square top-right touch resize
   handle.
2. It adds task input sinks behind decor container layers, so that
   motion events received by resize handlers out of task windows won't
   be dispatched to windows behind the resize handlers. I chose to put
   the task input sinks under the decor container layers so that the
   logic can be mostly limited inside DragResizeInputListener.
3. Resize handlers pilfer events when they decide to handle resize
   gestures. Windows that received the same gesture will receive a
   cancelled event.

There are a few remaining issues:

1. We update mouse cursors whenever the resize handlers receive hover
   events. We should avoid updating them when they aren't supposed to
   to resize task windows if the resize is started at the hover events,
   so windows behind the resize handlers have chances to update the
   mouse cursor shape.
2. The mouse cursor shape are updated to double arrows if the mouse
   hovers in corner areas that are slightly over task radii inward to
   one side of the task bounds, and less than corner sizes inward to
   an adjacent side of the task bounds. However this place is completely
   inside the task bounds, so we shouldn't resize the task at all with
   mice.

This CL also changes the CtrlType annotation to be flags.

Bug: 266866903
Test: Close buttons in captions be clicked when the mouse cursor is
close to the corner.

Change-Id: I28215f1dc51bfaaccfd5fa4856186f4a6f0e4925
parent 41fc44e3
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -145,7 +145,9 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
                    mDisplay.getDisplayId(),
                    0 /* taskCornerRadius */,
                    mDecorationContainerSurface,
                    mDragPositioningCallback);
                    mDragPositioningCallback,
                    mSurfaceControlBuilderSupplier,
                    mSurfaceControlTransactionSupplier);
        }

        final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
+3 −1
Original line number Diff line number Diff line
@@ -234,7 +234,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                    mDisplay.getDisplayId(),
                    mRelayoutParams.mCornerRadius,
                    mDecorationContainerSurface,
                    mDragPositioningCallback);
                    mDragPositioningCallback,
                    mSurfaceControlBuilderSupplier,
                    mSurfaceControlTransactionSupplier);
        }

        final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
+3 −1
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import android.annotation.IntDef;
 * Callback called when receiving drag-resize or drag-move related input events.
 */
public interface DragPositioningCallback {
    @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
    @IntDef(flag = true, value = {
            CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM
    })
    @interface CtrlType {}

    int CTRL_TYPE_UNDEFINED = 0;
+87 −5
Original line number Diff line number Diff line
@@ -18,8 +18,11 @@ package com.android.wm.shell.windowdecor;

import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;

import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
@@ -48,6 +51,8 @@ import android.view.WindowManagerGlobal;

import com.android.internal.view.BaseIWindow;

import java.util.function.Supplier;

/**
 * An input event listener registered to InputDispatcher to receive input events on task edges and
 * and corners. Converts them to drag resize requests.
@@ -60,6 +65,7 @@ class DragResizeInputListener implements AutoCloseable {
    private final Handler mHandler;
    private final Choreographer mChoreographer;
    private final InputManager mInputManager;
    private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;

    private final int mDisplayId;
    private final BaseIWindow mFakeWindow;
@@ -69,6 +75,10 @@ class DragResizeInputListener implements AutoCloseable {
    private final TaskResizeInputEventReceiver mInputEventReceiver;
    private final DragPositioningCallback mCallback;

    private final SurfaceControl mInputSinkSurface;
    private final BaseIWindow mFakeSinkWindow;
    private final InputChannel mSinkInputChannel;

    private int mTaskWidth;
    private int mTaskHeight;
    private int mResizeHandleThickness;
@@ -90,15 +100,18 @@ class DragResizeInputListener implements AutoCloseable {
            int displayId,
            int taskCornerRadius,
            SurfaceControl decorationSurface,
            DragPositioningCallback callback) {
            DragPositioningCallback callback,
            Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
            Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
        mInputManager = context.getSystemService(InputManager.class);
        mHandler = handler;
        mChoreographer = choreographer;
        mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
        mDisplayId = displayId;
        mTaskCornerRadius = taskCornerRadius;
        mDecorationSurface = decorationSurface;
        // Use a fake window as the backing surface is a container layer and we don't want to create
        // a buffer layer for it so we can't use ViewRootImpl.
        // Use a fake window as the backing surface is a container layer, and we don't want to
        // create a buffer layer for it, so we can't use ViewRootImpl.
        mFakeWindow = new BaseIWindow();
        mFakeWindow.setSession(mWindowSession);
        mFocusGrantToken = new Binder();
@@ -111,7 +124,7 @@ class DragResizeInputListener implements AutoCloseable {
                    null /* hostInputToken */,
                    FLAG_NOT_FOCUSABLE,
                    PRIVATE_FLAG_TRUSTED_OVERLAY,
                    0 /* inputFeatures */,
                    INPUT_FEATURE_SPY,
                    TYPE_APPLICATION,
                    null /* windowToken */,
                    mFocusGrantToken,
@@ -126,6 +139,35 @@ class DragResizeInputListener implements AutoCloseable {
        mCallback = callback;
        mDragDetector = new DragDetector(mInputEventReceiver);
        mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());

        mInputSinkSurface = surfaceControlBuilderSupplier.get()
                .setName("TaskInputSink of " + decorationSurface)
                .setContainerLayer()
                .setParent(mDecorationSurface)
                .build();
        mSurfaceControlTransactionSupplier.get()
                .setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
                .show(mInputSinkSurface)
                .apply();
        mFakeSinkWindow = new BaseIWindow();
        mSinkInputChannel = new InputChannel();
        try {
            mWindowSession.grantInputChannel(
                    mDisplayId,
                    mInputSinkSurface,
                    mFakeSinkWindow,
                    null /* hostInputToken */,
                    FLAG_NOT_FOCUSABLE,
                    0 /* privateFlags */,
                    INPUT_FEATURE_NO_INPUT_CHANNEL,
                    TYPE_INPUT_CONSUMER,
                    null /* windowToken */,
                    mFocusGrantToken,
                    "TaskInputSink of " + decorationSurface,
                    mSinkInputChannel);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

    /**
@@ -219,7 +261,35 @@ class DragResizeInputListener implements AutoCloseable {
                    mDecorationSurface,
                    FLAG_NOT_FOCUSABLE,
                    PRIVATE_FLAG_TRUSTED_OVERLAY,
                    0 /* inputFeatures */,
                    INPUT_FEATURE_SPY,
                    touchRegion);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }

        mSurfaceControlTransactionSupplier.get()
                .setWindowCrop(mInputSinkSurface, mTaskWidth, mTaskHeight)
                .apply();
        // The touch region of the TaskInputSink should be the touch region of this
        // DragResizeInputHandler minus the task bounds. Pilfering events isn't enough to prevent
        // input windows from handling down events, which will bring tasks in the back to front.
        //
        // Note not the entire touch region responds to both mouse and touchscreen events.
        // Therefore, in the region that only responds to one of them, it would be a no-op to
        // perform a gesture in the other type of events. We currently only have a mouse-only region
        // out of the task bounds, and due to the roughness of touchscreen events, it's not a severe
        // issue. However, were there touchscreen-only a region out of the task bounds, mouse
        // gestures will become no-op in that region, even though the mouse gestures may appear to
        // be performed on the input window behind the resize handle.
        touchRegion.op(0, 0, mTaskWidth, mTaskHeight, Region.Op.DIFFERENCE);
        try {
            mWindowSession.updateInputChannel(
                    mSinkInputChannel.getToken(),
                    mDisplayId,
                    mInputSinkSurface,
                    FLAG_NOT_FOCUSABLE,
                    0 /* privateFlags */,
                    INPUT_FEATURE_NO_INPUT_CHANNEL,
                    touchRegion);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
@@ -248,6 +318,16 @@ class DragResizeInputListener implements AutoCloseable {
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }

        mSinkInputChannel.dispose();
        try {
            mWindowSession.remove(mFakeSinkWindow);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
        mSurfaceControlTransactionSupplier.get()
                .remove(mInputSinkSurface)
                .apply();
    }

    private class TaskResizeInputEventReceiver extends InputEventReceiver
@@ -316,6 +396,8 @@ class DragResizeInputListener implements AutoCloseable {
                        mShouldHandleEvents = isInResizeHandleBounds(x, y);
                    }
                    if (mShouldHandleEvents) {
                        mInputManager.pilferPointers(mInputChannel.getToken());

                        mDragPointerId = e.getPointerId(0);
                        float rawX = e.getRawX(0);
                        float rawY = e.getRawY(0);
+19 −4
Original line number Diff line number Diff line
@@ -63,6 +63,24 @@ import java.util.function.Supplier;
public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
        implements AutoCloseable {

    /**
     * The Z-order of {@link #mCaptionContainerSurface}.
     * <p>
     * We use {@link #mDecorationContainerSurface} to define input window for task resizing; by
     * layering it in front of {@link #mCaptionContainerSurface}, we can allow it to handle input
     * prior to caption view itself, treating corner inputs as resize events rather than
     * repositioning.
     */
    static final int CAPTION_LAYER_Z_ORDER = -1;
    /**
     * The Z-order of the task input sink in {@link DragPositioningCallback}.
     * <p>
     * This task input sink is used to prevent undesired dispatching of motion events out of task
     * bounds; by layering it behind {@link #mCaptionContainerSurface}, we allow captions to handle
     * input events first.
     */
    static final int INPUT_SINK_Z_ORDER = -2;

    /**
     * System-wide context. Only used to create context with overridden configurations.
     */
@@ -238,11 +256,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
        final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
        final int captionWidth = taskBounds.width();

        // We use mDecorationContainerSurface to define input window for task resizing; by layering
        // it in front of mCaptionContainerSurface, we can allow it to handle input prior to
        // caption view itself, treating corner inputs as resize events rather than repositioning.
        startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
                .setLayer(mCaptionContainerSurface, -1)
                .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                .show(mCaptionContainerSurface);

        if (ViewRootImpl.CAPTION_ON_SHELL) {