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

Commit 192fe76a authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Support touch event on letterbox surface

This is part of "Compatible behavior for non-resizable activity".
It allows window to receive motion event when sliding from a letterbox
surface. That makes it easier for users to slide out the side menu of
a letterboxed activity.

Note this only works when the touchable region of the activity doesn't
cover letterbox surface (lower layer than activity).

Bug: 112288258
Test: manual - Start an activity with fixed aspect ratio and make sure
      it touchable region matches its content size, and then slide from
      letterbox region into the activity, it should receive touch event.
Change-Id: Ibff9103adbdad0ae5dd93c4a44b00f41d06c1007
parent 0671c44a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -884,6 +884,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
            dc.setFocusedApp(null);
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
        }
        if (mLetterbox != null) {
            mLetterbox.destroy();
            mLetterbox = null;
        }

        if (!delayed) {
            updateReportedVisibilityLocked();
@@ -1297,6 +1301,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
                }
            }
        }

        if (prevDc != mDisplayContent && mLetterbox != null) {
            mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId());
        }
    }

    /**
@@ -1844,6 +1852,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (needsLetterbox) {
            if (mLetterbox == null) {
                mLetterbox = new Letterbox(() -> makeChildSurface(null));
                mLetterbox.attachInput(w);
            }
            getPosition(mTmpPoint);
            mLetterbox.layout(getParent().getBounds(), w.getFrameLw(), mTmpPoint);
+113 −14
Original line number Diff line number Diff line
@@ -20,7 +20,15 @@ import static android.view.SurfaceControl.HIDDEN;

import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Process;
import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;

import com.android.server.UiThread;

import java.util.function.Supplier;

@@ -40,6 +48,7 @@ public class Letterbox {
    private final LetterboxSurface mLeft = new LetterboxSurface("left");
    private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
    private final LetterboxSurface mRight = new LetterboxSurface("right");
    private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };

    /**
     * Constructs a Letterbox.
@@ -87,8 +96,12 @@ public class Letterbox {
     * Returns true if any part of the letterbox overlaps with the given {@code rect}.
     */
    public boolean isOverlappingWith(Rect rect) {
        return mTop.isOverlappingWith(rect) || mLeft.isOverlappingWith(rect)
                || mBottom.isOverlappingWith(rect) || mRight.isOverlappingWith(rect);
        for (LetterboxSurface surface : mSurfaces) {
            if (surface.isOverlappingWith(rect)) {
                return true;
            }
        }
        return false;
    }

    /**
@@ -107,25 +120,94 @@ public class Letterbox {
        mOuter.setEmpty();
        mInner.setEmpty();

        mTop.remove();
        mLeft.remove();
        mBottom.remove();
        mRight.remove();
        for (LetterboxSurface surface : mSurfaces) {
            surface.remove();
        }
    }

    /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
    public boolean needsApplySurfaceChanges() {
        return mTop.needsApplySurfaceChanges()
                || mLeft.needsApplySurfaceChanges()
                || mBottom.needsApplySurfaceChanges()
                || mRight.needsApplySurfaceChanges();
        for (LetterboxSurface surface : mSurfaces) {
            if (surface.needsApplySurfaceChanges()) {
                return true;
            }
        }
        return false;
    }

    public void applySurfaceChanges(SurfaceControl.Transaction t) {
        mTop.applySurfaceChanges(t);
        mLeft.applySurfaceChanges(t);
        mBottom.applySurfaceChanges(t);
        mRight.applySurfaceChanges(t);
        for (LetterboxSurface surface : mSurfaces) {
            surface.applySurfaceChanges(t);
        }
    }

    /** Enables touches to slide into other neighboring surfaces. */
    void attachInput(WindowState win) {
        for (LetterboxSurface surface : mSurfaces) {
            surface.attachInput(win);
        }
    }

    void onMovedToDisplay(int displayId) {
        for (LetterboxSurface surface : mSurfaces) {
            if (surface.mInputInterceptor != null) {
                surface.mInputInterceptor.mWindowHandle.displayId = displayId;
            }
        }
    }

    private static class InputInterceptor {
        final InputChannel mServerChannel;
        final InputChannel mClientChannel;
        final InputWindowHandle mWindowHandle;
        final InputEventReceiver mInputEventReceiver;
        final WindowManagerService mWmService;

        InputInterceptor(String namePrefix, WindowState win) {
            mWmService = win.mWmService;
            final String name = namePrefix + (win.mAppToken != null ? win.mAppToken : win);
            final InputChannel[] channels = InputChannel.openInputChannelPair(name);
            mServerChannel = channels[0];
            mClientChannel = channels[1];
            mInputEventReceiver = new SimpleInputReceiver(mClientChannel);

            final Binder token = new Binder();
            mWmService.mInputManager.registerInputChannel(mServerChannel, token);

            mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
                    null /* clientWindow */, win.getDisplayId());
            mWindowHandle.name = name;
            mWindowHandle.token = token;
            mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_SLIPPERY;
            mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
            mWindowHandle.dispatchingTimeoutNanos =
                    WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
            mWindowHandle.visible = true;
            mWindowHandle.ownerPid = Process.myPid();
            mWindowHandle.ownerUid = Process.myUid();
            mWindowHandle.scaleFactor = 1.0f;
        }

        void updateTouchableRegion(Rect frame) {
            mWindowHandle.touchableRegion.set(frame);
            mWindowHandle.touchableRegion.translate(-frame.left, -frame.top);
        }

        void dispose() {
            mWmService.mInputManager.unregisterInputChannel(mServerChannel);
            mInputEventReceiver.dispose();
            mServerChannel.dispose();
            mClientChannel.dispose();
        }

        private static class SimpleInputReceiver extends InputEventReceiver {
            SimpleInputReceiver(InputChannel inputChannel) {
                super(inputChannel, UiThread.getHandler().getLooper());
            }
        }
    }

    private class LetterboxSurface {
@@ -137,6 +219,8 @@ public class Letterbox {
        private final Rect mLayoutFrameGlobal = new Rect();
        private final Rect mLayoutFrameRelative = new Rect();

        private InputInterceptor mInputInterceptor;

        public LetterboxSurface(String type) {
            mType = type;
        }
@@ -154,11 +238,22 @@ public class Letterbox {
            mSurface.setColor(new float[]{0, 0, 0});
        }

        void attachInput(WindowState win) {
            if (mInputInterceptor != null) {
                mInputInterceptor.dispose();
            }
            mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
        }

        public void remove() {
            if (mSurface != null) {
                mSurface.remove();
                mSurface = null;
            }
            if (mInputInterceptor != null) {
                mInputInterceptor.dispose();
                mInputInterceptor = null;
            }
        }

        public int getWidth() {
@@ -193,6 +288,10 @@ public class Letterbox {
                t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
                t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
                        mSurfaceFrameRelative.height());
                if (mInputInterceptor != null) {
                    mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative);
                    t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
                }
                t.show(mSurface);
            } else if (mSurface != null) {
                t.hide(mSurface);