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

Commit 3fd72689 authored by Ikram Gabiyev's avatar Ikram Gabiyev Committed by Android (Google) Code Review
Browse files

Merge "Allow drag flings in PiP2 via PipTransitionState" into main

parents cb715b56 81f44313
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@ public abstract class Pip2Module {
            PhonePipMenuController menuPhoneController,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            @NonNull PipBoundsState pipBoundsState,
            @NonNull PipTransitionState pipTransitionState,
            @NonNull SizeSpecSource sizeSpecSource,
            PipMotionHelper pipMotionHelper,
            FloatingContentCoordinator floatingContentCoordinator,
@@ -139,8 +140,9 @@ public abstract class Pip2Module {
            @ShellMainThread ShellExecutor mainExecutor,
            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
        return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
                pipBoundsState, sizeSpecSource, pipMotionHelper, floatingContentCoordinator,
                pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional);
                pipBoundsState, pipTransitionState, sizeSpecSource, pipMotionHelper,
                floatingContentCoordinator, pipUiEventLogger, mainExecutor,
                pipPerfHintControllerOptional);
    }

    @WMSingleton
@@ -149,9 +151,13 @@ public abstract class Pip2Module {
            PipBoundsState pipBoundsState, PhonePipMenuController menuController,
            PipSnapAlgorithm pipSnapAlgorithm,
            FloatingContentCoordinator floatingContentCoordinator,
            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
            PipScheduler pipScheduler,
            Optional<PipPerfHintController> pipPerfHintControllerOptional,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            PipTransitionState pipTransitionState) {
        return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm,
                floatingContentCoordinator, pipPerfHintControllerOptional);
                floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional,
                pipBoundsAlgorithm, pipTransitionState);
    }

    @WMSingleton
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ public class PipInputConsumer {
     * Listener interface for callers to learn when this class is registered or unregistered with
     * window manager
     */
    private interface RegistrationListener {
    interface RegistrationListener {
        void onRegistrationChanged(boolean isRegistered);
    }

+89 −33
Original line number Diff line number Diff line
@@ -31,7 +31,9 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
import android.view.SurfaceControl;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -39,6 +41,7 @@ import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
@@ -57,6 +60,7 @@ import java.util.function.Consumer;
public class PipMotionHelper implements PipAppOpsListener.Callback,
        FloatingContentCoordinator.FloatingContent {
    private static final String TAG = "PipMotionHelper";
    private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change";
    private static final boolean DEBUG = false;

    private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
@@ -72,7 +76,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,

    private final Context mContext;
    private @NonNull PipBoundsState mPipBoundsState;

    private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm;
    private @NonNull PipScheduler mPipScheduler;
    private @NonNull PipTransitionState mPipTransitionState;
    private PhonePipMenuController mMenuController;
    private PipSnapAlgorithm mSnapAlgorithm;

@@ -144,6 +150,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     */
    private boolean mDismissalPending = false;

    /**
     * Set to true if bounds change transition has been scheduled from PipMotionHelper.
     */
    private boolean mWaitingForBoundsChangeTransition = false;

    /**
     * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
     * used to show menu activity when the expand animation is completed.
@@ -152,22 +163,25 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,

    public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
            PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm,
            FloatingContentCoordinator floatingContentCoordinator,
            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
            FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler,
            Optional<PipPerfHintController> pipPerfHintControllerOptional,
            PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) {
        mContext = context;
        mPipBoundsState = pipBoundsState;
        mPipBoundsAlgorithm = pipBoundsAlgorithm;
        mPipScheduler = pipScheduler;
        mMenuController = menuController;
        mSnapAlgorithm = snapAlgorithm;
        mFloatingContentCoordinator = floatingContentCoordinator;
        mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
        mResizePipUpdateListener = (target, values) -> {
            if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
                /*
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null);
                 */
                mPipScheduler.scheduleUserResizePip(
                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
            }
        };
        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
    }

    void init() {
@@ -236,12 +250,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                mPipBoundsState.setBounds(toBounds);
            } else {
                mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds);
                /*
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
                        (Rect newBounds) -> {
                                mMenuController.updateMenuLayout(newBounds);
                        });
                 */
                mPipScheduler.scheduleUserResizePip(toBounds);
            }
        } else {
            // If PIP is 'catching up' after being stuck in the dismiss target, update the animation
@@ -552,11 +561,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    /** Set new fling configs whose min/max values respect the given movement bounds. */
    private void rebuildFlingConfigs() {
        mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
                mPipBoundsState.getMovementBounds().left,
                mPipBoundsState.getMovementBounds().right);
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).left,
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).right);
        mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
                mPipBoundsState.getMovementBounds().top,
                mPipBoundsState.getMovementBounds().bottom);
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).top,
                mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom);
        final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets();
        mStashConfigX = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION,
@@ -623,22 +632,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    private void onBoundsPhysicsAnimationEnd() {
        // The physics animation ended, though we may not necessarily be done animating, such as
        // when we're still dragging after moving out of the magnetic target.
        if (!mDismissalPending
                && !mSpringingToTouch
                && !mMagnetizedPip.getObjectStuckToTarget()) {
            // All motion operations have actually finished.
            mPipBoundsState.setBounds(
                    mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
            mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
            if (!mDismissalPending) {
        if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) {
            // do not schedule resize if PiP is dismissing, which may cause app re-open to
            // mBounds instead of its normal bounds.
                // mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
            }
            Bundle extra = new Bundle();
            extra.putBoolean(FLING_BOUNDS_CHANGE, true);
            mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
            return;
        }
        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
        mSpringingToTouch = false;
        mDismissalPending = false;
        settlePipBoundsAfterPhysicsAnimation(true /* animatingAfter */);
        cleanUpHighPerfSessionMaybe();
    }

@@ -662,7 +664,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                            + " callers=\n%s", TAG, toBounds, Debug.getCallers(5, "    "));
        }
        if (!toBounds.equals(getBounds())) {
            // mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
            mPipScheduler.scheduleAnimateResizePip(toBounds);
        }
    }

@@ -685,6 +687,60 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        // setAnimatingToBounds(toBounds);
    }

    private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
            @PipTransitionState.TransitionState int newState,
            @Nullable Bundle extra) {
        switch (newState) {
            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
                if (!extra.getBoolean(FLING_BOUNDS_CHANGE)) break;

                // If touch is turned off and we are in a fling animation, schedule a transition.
                mWaitingForBoundsChangeTransition = true;
                mPipScheduler.scheduleAnimateResizePip(
                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
                break;
            case PipTransitionState.CHANGING_PIP_BOUNDS:
                if (!mWaitingForBoundsChangeTransition) break;

                // If bounds change transition was scheduled from this class, handle leash updates.
                mWaitingForBoundsChangeTransition = false;
                SurfaceControl.Transaction startTx = extra.getParcelable(
                        PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
                Rect destinationBounds = extra.getParcelable(
                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
                startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
                        destinationBounds.left, destinationBounds.top);
                startTx.apply();

                // All motion operations have actually finished, so make bounds cache updates.
                settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
                cleanUpHighPerfSessionMaybe();

                // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core.
                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
                break;
            case PipTransitionState.EXITING_PIP:
                // We need to force finish any local animators if about to leave PiP, to avoid
                // breaking the state (e.g. leashes are cleaned up upon exit).
                if (!mPipBoundsState.getMotionBoundsState().isInMotion()) break;
                cancelPhysicsAnimation();
                settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
        }
    }

    private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) {
        if (!animatingAfter) {
            // The physics animation ended, though we may not necessarily be done animating, such as
            // when we're still dragging after moving out of the magnetic target. Only set the final
            // bounds state and clear motion bounds completely if the whole animation is over.
            mPipBoundsState.setBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
            mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
        }
        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
        mSpringingToTouch = false;
        mDismissalPending = false;
    }

    /**
     * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the
     * magnetic dismiss target so it can calculate PIP's size and position.
+19 −0
Original line number Diff line number Diff line
@@ -25,16 +25,19 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -156,4 +159,20 @@ public class PipScheduler {
        wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
        mPipTransitionController.startResizeTransition(wct);
    }

    /**
     * Directly perform a scaled matrix transformation on the leash. This will not perform any
     * {@link WindowContainerTransaction}.
     */
    public void scheduleUserResizePip(Rect toBounds) {
        if (toBounds.isEmpty()) {
            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Attempted to user resize PIP to empty bounds, aborting.", TAG);
            return;
        }
        SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash;
        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
        tx.setPosition(leash, toBounds.left, toBounds.top);
        tx.apply();
    }
}
+49 −12
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.wm.shell.pip2.phone;

import static android.view.WindowManager.INPUT_CONSUMER_PIP;

import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD;
import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
@@ -30,18 +32,19 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.provider.DeviceConfig;
import android.util.Size;
import android.view.DisplayCutout;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -80,6 +83,7 @@ public class PipTouchHandler {
    private final Context mContext;
    private final PipBoundsAlgorithm mPipBoundsAlgorithm;
    @NonNull private final PipBoundsState mPipBoundsState;
    @NonNull private final PipTransitionState mPipTransitionState;
    @NonNull private final SizeSpecSource mSizeSpecSource;
    private final PipUiEventLogger mPipUiEventLogger;
    private final PipDismissTargetHandler mPipDismissTargetHandler;
@@ -125,6 +129,7 @@ public class PipTouchHandler {
    private final FloatingContentCoordinator mFloatingContentCoordinator;
    private PipMotionHelper mMotionHelper;
    private PipTouchGesture mGesture;
    private PipInputConsumer mPipInputConsumer;

    // Temp vars
    private final Rect mTmpBounds = new Rect();
@@ -167,6 +172,7 @@ public class PipTouchHandler {
            PhonePipMenuController menuController,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            @NonNull PipBoundsState pipBoundsState,
            @NonNull PipTransitionState pipTransitionState,
            @NonNull SizeSpecSource sizeSpecSource,
            PipMotionHelper pipMotionHelper,
            FloatingContentCoordinator floatingContentCoordinator,
@@ -179,6 +185,9 @@ public class PipTouchHandler {
        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
        mPipBoundsAlgorithm = pipBoundsAlgorithm;
        mPipBoundsState = pipBoundsState;

        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
        mSizeSpecSource = sizeSpecSource;
        mMenuController = menuController;
        mPipUiEventLogger = pipUiEventLogger;
@@ -227,6 +236,11 @@ public class PipTouchHandler {
        mPipResizeGestureHandler.init();
        mPipDismissTargetHandler.init();

        mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
                INPUT_CONSUMER_PIP, mMainExecutor);
        mPipInputConsumer.setInputListener(this::handleTouchEvent);
        mPipInputConsumer.setRegistrationListener(this::onRegistrationChanged);

        mEnableStash = DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_SYSTEMUI,
                PIP_STASHING,
@@ -294,19 +308,17 @@ public class PipTouchHandler {

    void onActivityPinned() {
        mPipDismissTargetHandler.createOrUpdateDismissTarget();

        mPipResizeGestureHandler.onActivityPinned();
        mFloatingContentCoordinator.onContentAdded(mMotionHelper);
        mPipInputConsumer.registerInputConsumer();
    }

    void onActivityUnpinned(ComponentName topPipActivity) {
        if (topPipActivity == null) {
    void onActivityUnpinned() {
        // Clean up state after the last PiP activity is removed
        mPipDismissTargetHandler.cleanUpDismissTarget();

        mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
        }
        mPipResizeGestureHandler.onActivityUnpinned();
        mPipInputConsumer.unregisterInputConsumer();
    }

    void onPinnedStackAnimationEnded(
@@ -512,6 +524,7 @@ public class PipTouchHandler {
            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
@@ -528,11 +541,13 @@ public class PipTouchHandler {
            return true;
        }

        if (!mTouchState.isUserInteracting()) {
        // Ignore the motion event When the entry animation is waiting to be started
        if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) {
            ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Waiting to start the entry animation, skip the motion event.", TAG);
            return true;
        }
         */

        // Update the touch state
        mTouchState.onTouchEvent(ev);
@@ -808,7 +823,7 @@ public class PipTouchHandler {
            mMovementWithinDismiss = touchState.getDownTouchPosition().y
                    >= mPipBoundsState.getMovementBounds().bottom;
            mMotionHelper.setSpringingToTouch(false);
            // mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl());
            mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.mPinnedTaskLeash);

            // If the menu is still visible then just poke the menu
            // so that it will timeout after the user stops touching it
@@ -880,7 +895,8 @@ public class PipTouchHandler {
                // Reset the touch state on up before the fling settles
                mTouchState.reset();
                if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) {
                    mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */);
                    // mMotionHelper.stashToEdge(vel.x, vel.y,
                    //      this::stashEndAction /* endAction */);
                } else {
                    if (mPipBoundsState.isStashed()) {
                        // Reset stashed state if previously stashed
@@ -1059,6 +1075,27 @@ public class PipTouchHandler {
        mPipResizeGestureHandler.setOhmOffset(offset);
    }

    private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
            @PipTransitionState.TransitionState int newState,
            @Nullable Bundle extra) {
        switch (newState) {
            case PipTransitionState.ENTERED_PIP:
                onActivityPinned();
                mTouchState.setAllowInputEvents(true);
                break;
            case PipTransitionState.EXITED_PIP:
                mTouchState.setAllowInputEvents(false);
                onActivityUnpinned();
                break;
            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
                mTouchState.setAllowInputEvents(false);
                break;
            case PipTransitionState.CHANGED_PIP_BOUNDS:
                mTouchState.setAllowInputEvents(true);
                break;
        }
    }

    /**
     * Dumps the {@link PipTouchHandler} state.
     */
Loading