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

Commit 7c10e48b authored by Ben Lin's avatar Ben Lin
Browse files

Implement pinch to resize.

Bug: 166478885
Test: adb shell cmd device_config put systemui pip_pinch_resize true
Change-Id: Ide6fecd7a26c040b533fb49d399744c5617bfc93
parent 932d7167
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -390,9 +390,9 @@ public final class SystemUiDeviceConfigFlags {
    public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";

    /**
     * (boolean) Whether to enable user-drag resizing for PIP.
     * (boolean) Whether to enable pinch resizing for PIP.
     */
    public static final String PIP_USER_RESIZE = "pip_user_resize";
    public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";

    /**
     * (float) Bottom height in DP for Back Gesture.
+143 −29
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@
 */
package com.android.systemui.pip.phone;

import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_PINCH_RESIZE;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
@@ -46,6 +46,7 @@ import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;

import com.android.internal.policy.TaskResizingAlgorithm;
@@ -67,6 +68,8 @@ import java.util.function.Function;
public class PipResizeGestureHandler {

    private static final String TAG = "PipResizeGestureHandler";
    private static final float PINCH_THRESHOLD = 0.05f;
    private static final float STARTING_SCALE_FACTOR = 1.0f;

    private static final int INVALID_SYSUI_STATE_MASK =
            SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
@@ -83,6 +86,7 @@ public class PipResizeGestureHandler {
    private final int mDisplayId;
    private final Executor mMainExecutor;
    private final SysUiState mSysUiState;
    private final ScaleGestureDetector mScaleGestureDetector;
    private final Region mTmpRegion = new Region();

    private final PointF mDownPoint = new PointF();
@@ -105,8 +109,10 @@ public class PipResizeGestureHandler {
    private boolean mAllowGesture;
    private boolean mIsAttached;
    private boolean mIsEnabled;
    private boolean mEnableUserResize;
    private boolean mEnablePinchResize;
    private boolean mThresholdCrossed;
    private boolean mUsingPinchToZoom = false;
    private float mScaleFactor = STARTING_SCALE_FACTOR;

    private InputMonitor mInputMonitor;
    private InputEventReceiver mInputEventReceiver;
@@ -136,17 +142,73 @@ public class PipResizeGestureHandler {
        context.getDisplay().getRealSize(mMaxSize);
        reloadResources();

        mEnableUserResize = DeviceConfig.getBoolean(
        mScaleGestureDetector = new ScaleGestureDetector(context,
                new ScaleGestureDetector.OnScaleGestureListener() {
                    @Override
                    public boolean onScale(ScaleGestureDetector detector) {
                        mScaleFactor *= detector.getScaleFactor();

                        if (!mThresholdCrossed
                                && (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD)
                                || mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) {
                            mThresholdCrossed = true;
                            mInputMonitor.pilferPointers();
                        }
                        if (mThresholdCrossed) {
                            int height = Math.min(mMaxSize.y, Math.max(mMinSize.y,
                                    (int) (mScaleFactor * mLastDownBounds.height())));
                            int width = Math.min(mMaxSize.x, Math.max(mMinSize.x,
                                    (int) (mScaleFactor * mLastDownBounds.width())));
                            int top, bottom, left, right;

                            if ((mCtrlType & CTRL_TOP) != 0) {
                                top = mLastDownBounds.bottom - height;
                                bottom = mLastDownBounds.bottom;
                            } else {
                                top = mLastDownBounds.top;
                                bottom = mLastDownBounds.top + height;
                            }

                            if ((mCtrlType & CTRL_LEFT) != 0) {
                                left = mLastDownBounds.right - width;
                                right = mLastDownBounds.right;
                            } else {
                                left = mLastDownBounds.left;
                                right = mLastDownBounds.left + width;
                            }

                            mLastResizeBounds.set(left, top, right, bottom);
                            mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds,
                                    mLastResizeBounds,
                                    null);
                        }
                        return true;
                    }

                    @Override
                    public boolean onScaleBegin(ScaleGestureDetector detector) {
                        setCtrlTypeForPinchToZoom();
                        return true;
                    }

                    @Override
                    public void onScaleEnd(ScaleGestureDetector detector) {
                        mScaleFactor = STARTING_SCALE_FACTOR;
                        finishResize();
                    }
                });

        mEnablePinchResize = DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_SYSTEMUI,
                PIP_USER_RESIZE,
                /* defaultValue = */ true);
                PIP_PINCH_RESIZE,
                /* defaultValue = */ false);
        deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
                new DeviceConfig.OnPropertiesChangedListener() {
                    @Override
                    public void onPropertiesChanged(DeviceConfig.Properties properties) {
                        if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
                            mEnableUserResize = properties.getBoolean(
                                    PIP_USER_RESIZE, /* defaultValue = */ true);
                        if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) {
                            mEnablePinchResize = properties.getBoolean(
                                    PIP_PINCH_RESIZE, /* defaultValue = */ false);
                        }
                    }
                });
@@ -193,7 +255,7 @@ public class PipResizeGestureHandler {
    }

    private void updateIsEnabled() {
        boolean isEnabled = mIsAttached && mEnableUserResize;
        boolean isEnabled = mIsAttached;
        if (isEnabled == mIsEnabled) {
            return;
        }
@@ -211,7 +273,11 @@ public class PipResizeGestureHandler {

    private void onInputEvent(InputEvent ev) {
        if (ev instanceof MotionEvent) {
            onMotionEvent((MotionEvent) ev);
            if (mUsingPinchToZoom) {
                mScaleGestureDetector.onTouchEvent((MotionEvent) ev);
            } else {
                onDragCornerResize((MotionEvent) ev);
            }
        }
    }

@@ -254,8 +320,51 @@ public class PipResizeGestureHandler {
    }

    public boolean willStartResizeGesture(MotionEvent ev) {
        return mEnableUserResize && isInValidSysUiState()
                && isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY());
        if (isInValidSysUiState()) {
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    // Always pass the DOWN event to the ScaleGestureDetector
                    mScaleGestureDetector.onTouchEvent(ev);
                    if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
                        return true;
                    }
                    break;

                case MotionEvent.ACTION_POINTER_DOWN:
                    if (mEnablePinchResize && ev.getPointerCount() == 2) {
                        mUsingPinchToZoom = true;
                        return true;
                    }
                    break;

                default:
                    break;
            }
        }
        return false;
    }

    private void setCtrlTypeForPinchToZoom() {
        final Rect currentPipBounds = mMotionHelper.getBounds();
        mLastDownBounds.set(mMotionHelper.getBounds());

        Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
        mDisplayBounds.set(movementBounds.left,
                movementBounds.top,
                movementBounds.right + currentPipBounds.width(),
                movementBounds.bottom + currentPipBounds.height());

        if (currentPipBounds.left == mDisplayBounds.left) {
            mCtrlType |= CTRL_RIGHT;
        } else {
            mCtrlType |= CTRL_LEFT;
        }

        if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) {
            mCtrlType |= CTRL_TOP;
        } else {
            mCtrlType |= CTRL_BOTTOM;
        }
    }

    private void setCtrlType(int x, int y) {
@@ -295,7 +404,7 @@ public class PipResizeGestureHandler {
        return (mSysUiState.getFlags() & INVALID_SYSUI_STATE_MASK) == 0;
    }

    private void onMotionEvent(MotionEvent ev) {
    private void onDragCornerResize(MotionEvent ev) {
        int action = ev.getActionMasked();
        float x = ev.getX();
        float y = ev.getY();
@@ -345,6 +454,13 @@ public class PipResizeGestureHandler {
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    finishResize();
                    break;
            }
        }
    }

    private void finishResize() {
        if (!mLastResizeBounds.isEmpty()) {
            mUserResizeBounds.set(mLastResizeBounds);
            mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
@@ -360,13 +476,11 @@ public class PipResizeGestureHandler {
        } else {
            resetState();
        }
                    break;
            }
        }
    }

    private void resetState() {
        mCtrlType = CTRL_NONE;
        mUsingPinchToZoom = false;
        mAllowGesture = false;
        mThresholdCrossed = false;
    }
@@ -397,7 +511,7 @@ public class PipResizeGestureHandler {
        pw.println(innerPrefix + "mAllowGesture=" + mAllowGesture);
        pw.println(innerPrefix + "mIsAttached=" + mIsAttached);
        pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled);
        pw.println(innerPrefix + "mEnableUserResize=" + mEnableUserResize);
        pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize);
        pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
    }

+1 −2
Original line number Diff line number Diff line
@@ -650,8 +650,7 @@ public class PipTouchHandler {
        }

        MotionEvent ev = (MotionEvent) inputEvent;
        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
                && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
        if (mPipResizeGestureHandler.willStartResizeGesture(ev)) {
            // Initialize the touch state for the gesture, but immediately reset to invalidate the
            // gesture
            mTouchState.onTouchEvent(ev);