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

Commit 014f2975 authored by Ben Lin's avatar Ben Lin
Browse files

Re-implement pinch-to-resize gesture.

This removes ScaleGestureDetector and instead just do the calculation
inside the class itself. As user sets two input points on the PiP
surface, this signals the start of the gesture, and as user drags the
two fingers, it resizes the PiP to the maximum possible size. After the
user releases their fingers, then it auto-offsets/snaps to the right
position.

Bug: 166478885
Test: Start a pinch-to-resize while feature flag is on
Change-Id: I634bd14e97f0cf0cc7bf9c1a1b64a1f12130e0e9
parent bd4b7bd4
Loading
Loading
Loading
Loading
+37 −13
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ public class PipAnimationController {
    public static final int TRANSITION_DIRECTION_LEAVE_PIP = 3;
    public static final int TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN = 4;
    public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5;
    public static final int TRANSITION_DIRECTION_SNAP_AFTER_RESIZE = 6;

    @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
            TRANSITION_DIRECTION_NONE,
@@ -61,7 +62,8 @@ public class PipAnimationController {
            TRANSITION_DIRECTION_TO_PIP,
            TRANSITION_DIRECTION_LEAVE_PIP,
            TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN,
            TRANSITION_DIRECTION_REMOVE_STACK
            TRANSITION_DIRECTION_REMOVE_STACK,
            TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface TransitionDirection {}
@@ -109,13 +111,27 @@ public class PipAnimationController {
    }

    @SuppressWarnings("unchecked")
    /**
     * Construct and return an animator that animates from the {@param startBounds} to the
     * {@param endBounds} with the given {@param direction}. If {@param direction} is type
     * {@link ANIM_TYPE_BOUNDS}, then {@param sourceHintRect} will be used to animate
     * in a better, more smooth manner.
     *
     * In the case where one wants to start animation during an intermediate animation (for example,
     * if the user is currently doing a pinch-resize, and upon letting go now PiP needs to animate
     * to the correct snap fraction region), then provide the base bounds, which is current PiP
     * leash bounds before transformation/any animation. This is so when we try to construct
     * the different transformation matrices for the animation, we are constructing this based off
     * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
     */
    @VisibleForTesting
    public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
            Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) {
    public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds,
            Rect startBounds, Rect endBounds, Rect sourceHintRect,
            @PipAnimationController.TransitionDirection int direction) {
        if (mCurrentAnimator == null) {
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
                            direction));
                    PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds,
                            sourceHintRect, direction));
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                && mCurrentAnimator.isRunning()) {
            // If we are still animating the fade into pip, then just move the surface and ensure
@@ -130,8 +146,8 @@ public class PipAnimationController {
        } else {
            mCurrentAnimator.cancel();
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
                            direction));
                    PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds,
                            sourceHintRect, direction));
        }
        return mCurrentAnimator;
    }
@@ -180,6 +196,7 @@ public class PipAnimationController {
        private final @AnimationType int mAnimationType;
        private final Rect mDestinationBounds = new Rect();

        private T mBaseValue;
        protected T mCurrentValue;
        protected T mStartValue;
        private T mEndValue;
@@ -190,10 +207,11 @@ public class PipAnimationController {
        private @TransitionDirection int mTransitionDirection;

        private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
                Rect destinationBounds, T startValue, T endValue) {
                Rect destinationBounds, T baseValue, T startValue, T endValue) {
            mLeash = leash;
            mAnimationType = animationType;
            mDestinationBounds.set(destinationBounds);
            mBaseValue = baseValue;
            mStartValue = startValue;
            mEndValue = endValue;
            addListener(this);
@@ -263,6 +281,10 @@ public class PipAnimationController {
            return mStartValue;
        }

        T getBaseValue() {
            return mBaseValue;
        }

        @VisibleForTesting
        public T getEndValue() {
            return mEndValue;
@@ -334,7 +356,7 @@ public class PipAnimationController {
        static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
                Rect destinationBounds, float startValue, float endValue) {
            return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
                    destinationBounds, startValue, endValue) {
                    destinationBounds, startValue, startValue, endValue) {
                @Override
                void applySurfaceControlTransaction(SurfaceControl leash,
                        SurfaceControl.Transaction tx, float fraction) {
@@ -367,7 +389,7 @@ public class PipAnimationController {
        }

        static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
                Rect startValue, Rect endValue, Rect sourceHintRect,
                Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
                @PipAnimationController.TransitionDirection int direction) {
            // Just for simplicity we'll interpolate between the source rect hint insets and empty
            // insets to calculate the window crop
@@ -375,7 +397,7 @@ public class PipAnimationController {
            if (isOutPipDirection(direction)) {
                initialSourceValue = new Rect(endValue);
            } else {
                initialSourceValue = new Rect(startValue);
                initialSourceValue = new Rect(baseValue);
            }

            final Rect sourceHintRectInsets;
@@ -391,22 +413,24 @@ public class PipAnimationController {

            // construct new Rect instances in case they are recycled
            return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
                    endValue, new Rect(startValue), new Rect(endValue)) {
                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
                private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
                private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());

                @Override
                void applySurfaceControlTransaction(SurfaceControl leash,
                        SurfaceControl.Transaction tx, float fraction) {
                    final Rect base = getBaseValue();
                    final Rect start = getStartValue();
                    final Rect end = getEndValue();
                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                    setCurrentValue(bounds);
                    if (inScaleTransition() || sourceHintRect == null) {

                        if (isOutPipDirection(direction)) {
                            getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
                        } else {
                            getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
                            getSurfaceTransactionHelper().scale(tx, leash, base, bounds);
                        }
                    } else {
                        final Rect insets;
+19 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
@@ -814,6 +815,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
    }

    /**
     * Animates resizing of the pinned stack given the duration and start bounds.
     * This is used when the starting bounds is not the current PiP bounds.
     */
    public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
            Consumer<Rect> updateBoundsCallback) {
        if (mShouldDeferEnteringPip) {
            Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
            return;
        }
        scheduleAnimateResizePip(fromBounds, toBounds, null /* sourceHintRect */,
                TRANSITION_DIRECTION_SNAP_AFTER_RESIZE, duration, updateBoundsCallback);
    }

    private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
            Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
            int durationMs, Consumer<Rect> updateBoundsCallback) {
@@ -1073,8 +1088,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            Log.w(TAG, "Abort animation, invalid leash");
            return;
        }
        Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
                ? mPipBoundsState.getBounds() : currentBounds;
        mPipAnimationController
                .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect, direction)
                .getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect,
                        direction)
                .setTransitionDirection(direction)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setDuration(durationMs)
+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.wm.shell.pip.phone;

import android.graphics.Point;
import android.graphics.Rect;

/**
 * Helper class to calculate the new size given two-fingers pinch to resize.
 */
public class PipPinchResizingAlgorithm {
    private static final Rect TMP_RECT = new Rect();
    /**
     * Given inputs and requirements and current PiP bounds, return the new size.
     *
     * @param x0 x-coordinate of the primary input.
     * @param y0 y-coordinate of the primary input.
     * @param x1 x-coordinate of the secondary input.
     * @param y1 y-coordinate of the secondary input.
     * @param downx0 x-coordinate of the original down point of the primary input.
     * @param downy0 y-coordinate of the original down ponit of the primary input.
     * @param downx1 x-coordinate of the original down point of the secondary input.
     * @param downy1 y-coordinate of the original down point of the secondary input.
     * @param currentPipBounds current PiP bounds.
     * @param minVisibleWidth minimum visible width.
     * @param minVisibleHeight minimum visible height.
     * @param maxSize max size.
     * @return The new resized PiP bounds, sharing the same center.
     */
    public static Rect pinchResize(float x0, float y0, float x1, float y1,
            float downx0, float downy0, float downx1, float downy1, Rect currentPipBounds,
            int minVisibleWidth, int minVisibleHeight, Point maxSize) {

        int width = currentPipBounds.width();
        int height = currentPipBounds.height();
        int left = currentPipBounds.left;
        int top = currentPipBounds.top;
        int right = currentPipBounds.right;
        int bottom = currentPipBounds.bottom;
        final float aspect = (float) width / (float) height;
        final int widthDelta = Math.round(Math.abs(x0 - x1) - Math.abs(downx0 - downx1));
        final int heightDelta = Math.round(Math.abs(y0 - y1) - Math.abs(downy0 - downy1));

        width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x));
        height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));

        // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
        // drag axis. What ever is producing the bigger rectangle will be chosen.
        int width1;
        int width2;
        int height1;
        int height2;
        if (aspect > 1.0f) {
            // Assuming that the width is our target we calculate the height.
            width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
            height1 = Math.round((float) width1 / aspect);
            if (height1 < minVisibleHeight) {
                // If the resulting height is too small we adjust to the minimal size.
                height1 = minVisibleHeight;
                width1 = Math.max(minVisibleWidth,
                        Math.min(maxSize.x, Math.round((float) height1 * aspect)));
            }
            // Assuming that the height is our target we calculate the width.
            height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height));
            width2 = Math.round((float) height2 * aspect);
            if (width2 < minVisibleWidth) {
                // If the resulting width is too small we adjust to the minimal size.
                width2 = minVisibleWidth;
                height2 = Math.max(minVisibleHeight,
                        Math.min(maxSize.y, Math.round((float) width2 / aspect)));
            }
        } else {
            // Assuming that the width is our target we calculate the height.
            width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
            height1 = Math.round((float) width1 * aspect);
            if (height1 < minVisibleHeight) {
                // If the resulting height is too small we adjust to the minimal size.
                height1 = minVisibleHeight;
                width1 = Math.max(minVisibleWidth,
                        Math.min(maxSize.x, Math.round((float) height1 / aspect)));
            }
            // Assuming that the height is our target we calculate the width.
            height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height));
            width2 = Math.round((float) height2 / aspect);
            if (width2 < minVisibleWidth) {
                // If the resulting width is too small we adjust to the minimal size.
                width2 = minVisibleWidth;
                height2 = Math.max(minVisibleHeight,
                        Math.min(maxSize.y, Math.round((float) width2 * aspect)));
            }
        }

        // Use the bigger of the two rectangles if the major change was positive, otherwise
        // do the opposite.
        final boolean grows = width > (right - left) || height > (bottom - top);
        if (grows == (width1 * height1 > width2 * height2)) {
            width = width1;
            height = height1;
        } else {
            width = width2;
            height = height2;
        }

        TMP_RECT.set(currentPipBounds.centerX() - width / 2,
                currentPipBounds.centerY() - height / 2,
                currentPipBounds.centerX() + width / 2,
                currentPipBounds.centerY() + height / 2);
        return TMP_RECT;
    }
}
+121 −101

File changed.

Preview size limit exceeded, changes collapsed.

+5 −0
Original line number Diff line number Diff line
@@ -470,6 +470,11 @@ public class PipTouchHandler {
            return true;
        }

        if (mPipResizeGestureHandler.hasOngoingGesture()) {
            mPipDismissTargetHandler.hideDismissTargetMaybe();
            return true;
        }

        if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
                && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
            // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
Loading