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

Commit cf1d1b85 authored by Hongwei Wang's avatar Hongwei Wang
Browse files

Improve PIP enter transition w/ gesture nav (2/N)

Allow Launcher to query destination bounds of PiP window from SysUI.
Add also the callback notifies SysUI that the swipe-pip-to-home
operation is finished.

PipTaskOrganizer would ignore the entering transition if
swipe-pip-to-home should happen in Launcher.

Next: handle the swipe-pip-to-home transition in Launcher

Bug: 143965596
Test: N/A
Change-Id: Ia8bbdfcd4ed92c7ed968be84f0bf991feac8598e
parent ed528d6e
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -18,7 +18,10 @@ package com.android.wm.shell.pip;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.media.session.MediaController;

import com.android.wm.shell.pip.phone.PipTouchHandler;
@@ -232,4 +235,28 @@ public interface Pip {
     */
    default void suspendPipResizing(int reason) {
    }

    /**
     * Called by Launcher when swiping an auto-pip enabled Activity to home starts
     * @param componentName {@link ComponentName} represents the Activity entering PiP
     * @param activityInfo {@link ActivityInfo} tied to the Activity
     * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity
     * @param launcherRotation Rotation Launcher is in
     * @param shelfHeight Shelf height when landing PiP window onto Launcher
     * @return Destination bounds of PiP window based on the parameters passed in
     */
    default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
            PictureInPictureParams pictureInPictureParams,
            int launcherRotation, int shelfHeight) {
        return null;
    }

    /**
     * Called by Launcher when swiping an auto-pip enable Activity to home finishes
     * @param componentName {@link ComponentName} represents the Activity entering PiP
     * @param destinationBounds Destination bounds of PiP window
     */
    default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
        return;
    }
}
+61 −10
Original line number Diff line number Diff line
@@ -104,7 +104,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
        UNDEFINED(0),
        TASK_APPEARED(1),
        ENTERING_PIP(2),
        EXITING_PIP(3);
        ENTERED_PIP(3),
        EXITING_PIP(4);

        private final int mStateValue;

@@ -241,6 +242,14 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
     */
    private boolean mShouldDeferEnteringPip;

    /**
     * If set to {@code true}, no entering PiP transition would be kicked off and most likely
     * it's due to the fact that Launcher is handling the transition directly when swiping
     * auto PiP-able Activity to home.
     * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}.
     */
    private boolean mShouldIgnoreEnteringPipTransition;

    public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
            @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
            Optional<SplitScreen> splitScreenOptional,
@@ -308,6 +317,27 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
        mOneShotAnimationType = animationType;
    }

    /**
     * Callback when Launcher starts swipe-pip-to-home operation.
     * @return {@link Rect} for destination bounds.
     */
    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
            PictureInPictureParams pictureInPictureParams) {
        mShouldIgnoreEnteringPipTransition = true;
        mState = State.ENTERING_PIP;
        return mPipBoundsHandler.getDestinationBounds(componentName,
                getAspectRatioOrDefault(pictureInPictureParams),
                null /* bounds */, getMinimalSize(activityInfo));
    }

    /**
     * Callback when launcher finishes swipe-pip-to-home operation.
     * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
     */
    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
        mLastReportedBounds.set(destinationBounds);
    }

    /**
     * Expands PiP to the previous bounds, this is done in two phases using
     * {@link WindowContainerTransaction}
@@ -435,6 +465,16 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
        mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
        mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);

        if (mShouldIgnoreEnteringPipTransition) {
            // Animation has been finished together with Recents, directly apply the sync
            // transaction to PiP here.
            applyEnterPipSyncTransaction(mLastReportedBounds, () -> {
                mState = State.ENTERED_PIP;
            });
            mShouldIgnoreEnteringPipTransition = false;
            return;
        }

        if (mShouldDeferEnteringPip) {
            if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
            // if deferred, hide the surface till fixed rotation is completed
@@ -489,14 +529,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
                mSurfaceControlTransactionFactory.getTransaction();
        tx.setAlpha(mLeash, 0f);
        tx.apply();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
        wct.setBounds(mToken, destinationBounds);
        wct.scheduleFinishEnterPip(mToken, destinationBounds);
        mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
            @Override
            public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                t.apply();
        applyEnterPipSyncTransaction(destinationBounds, () -> {
            mUpdateHandler.post(() -> mPipAnimationController
                    .getAnimator(mLeash, destinationBounds, 0f, 1f)
                    .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
@@ -506,6 +539,21 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
            // mState is set right after the animation is kicked off to block any resize
            // requests such as offsetPip that may have been called prior to the transition.
            mState = State.ENTERING_PIP;
        });
    }

    private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
        wct.setBounds(mToken, destinationBounds);
        wct.scheduleFinishEnterPip(mToken, destinationBounds);
        mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
            @Override
            public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                t.apply();
                if (runnable != null) {
                    runnable.run();
                }
            }
        });
    }
@@ -523,6 +571,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize

    private void sendOnPipTransitionFinished(
            @PipAnimationController.TransitionDirection int direction) {
        if (direction == TRANSITION_DIRECTION_TO_PIP) {
            mState = State.ENTERED_PIP;
        }
        runOnMainHandler(() -> {
            for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
                final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+30 −11
Original line number Diff line number Diff line
@@ -22,9 +22,11 @@ import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
import android.os.Handler;
@@ -87,7 +89,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
        }
        // If there is an animation running (ie. from a shelf offset), then ensure that we calculate
        // the bounds for the next orientation using the destination bounds of the animation
        // TODO: Techincally this should account for movement animation bounds as well
        // TODO: Technically this should account for movement animation bounds as well
        Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
        final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext,
                mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation,
@@ -351,7 +353,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
     */
    @Override
    public void setShelfHeight(boolean visible, int height) {
        mHandler.post(() -> {
        mHandler.post(() -> setShelfHeightLocked(visible, height));
    }

    private void setShelfHeightLocked(boolean visible, int height) {
        final int shelfHeight = visible ? height : 0;
        final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
        if (changed) {
@@ -360,7 +365,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
                    false /* fromRotation */, false /* fromImeAdjustment */,
                    true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
        }
        });
    }

    @Override
@@ -373,6 +377,21 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
        mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback);
    }

    @Override
    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
            PictureInPictureParams pictureInPictureParams,
            int launcherRotation, int shelfHeight) {
        setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
        mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, launcherRotation);
        return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
                pictureInPictureParams);
    }

    @Override
    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
        mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds);
    }

    @Override
    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
        if (isOutPipDirection(direction)) {
+26 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.shared.recents;

import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -166,4 +169,27 @@ interface ISystemUiProxy {
     * Notifies to expand notification panel.
     */
    void expandNotificationPanel() = 29;

    /**
     * Notifies that Activity is about to be swiped to home with entering PiP transition and
     * queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
     *
     * @param componentName ComponentName represents the Activity
     * @param activityInfo ActivityInfo tied to the Activity
     * @param pictureInPictureParams PictureInPictureParams tied to the Activity
     * @param launcherRotation Launcher rotation to calculate the PiP destination bounds
     * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
     * @return destination bounds the PiP window should land into
     */
    Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
                in PictureInPictureParams pictureInPictureParams,
                int launcherRotation, int shelfHeight) = 30;

    /**
     * Notifies the swiping Activity to PiP onto home transition is finished
     *
     * @param componentName ComponentName represents the Activity
     * @param destinationBounds the destination bounds the PiP window lands into
     */
    void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31;
}
+34 −0
Original line number Diff line number Diff line
@@ -35,12 +35,14 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T

import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -503,6 +505,38 @@ public class OverviewProxyService extends CurrentUserTracker implements
            }
        }

        @Override
        public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
                PictureInPictureParams pictureInPictureParams,
                int launcherRotation, int shelfHeight) {
            if (!verifyCaller("startSwipePipToHome") || !mHasPipFeature) {
                return null;
            }
            long binderToken = Binder.clearCallingIdentity();
            try {
                return mPipOptional.map(pip ->
                        pip.startSwipePipToHome(componentName, activityInfo,
                                pictureInPictureParams, launcherRotation, shelfHeight))
                        .orElse(null);
            } finally {
                Binder.restoreCallingIdentity(binderToken);
            }
        }

        @Override
        public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
            if (!verifyCaller("stopSwipePipToHome") || !mHasPipFeature) {
                return;
            }
            long binderToken = Binder.clearCallingIdentity();
            try {
                mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome(
                        componentName, destinationBounds));
            } finally {
                Binder.restoreCallingIdentity(binderToken);
            }
        }

        private boolean verifyCaller(String reason) {
            final int callerId = Binder.getCallingUserHandle().getIdentifier();
            if (callerId != mCurrentBoundedUserId) {