Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +41 −64 Original line number Diff line number Diff line Loading @@ -107,38 +107,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; // Not a complete set of states but serves what we want right now. private enum State { UNDEFINED(0), TASK_APPEARED(1), ENTRY_SCHEDULED(2), ENTERING_PIP(3), ENTERED_PIP(4), EXITING_PIP(5); private final int mStateValue; State(int value) { mStateValue = value; } private boolean isInPip() { return mStateValue >= TASK_APPEARED.mStateValue && mStateValue != EXITING_PIP.mStateValue; } /** * Resize request can be initiated in other component, ignore if we are no longer in PIP, * still waiting for animation or we're exiting from it. * * @return {@code true} if the resize request should be blocked/ignored. */ private boolean shouldBlockResizeRequest() { return mStateValue < ENTERING_PIP.mStateValue || mStateValue == EXITING_PIP.mStateValue; } } private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; Loading Loading @@ -190,7 +158,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final boolean isExitPipDirection = isOutPipDirection(direction) || isRemovePipDirection(direction); if (mState != State.EXITING_PIP || isExitPipDirection) { if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP || isExitPipDirection) { // Finish resize as long as we're not exiting PIP, or, if we are, only if this is // the end of an exit PIP animation. // This is necessary in case there was a resize animation ongoing when exit PIP Loading Loading @@ -233,7 +202,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private ActivityManager.RunningTaskInfo mDeferredTaskInfo; private WindowContainerToken mToken; private SurfaceControl mLeash; private State mState = State.UNDEFINED; private PipTransitionState mPipTransitionState; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private long mLastOneShotAlphaAnimationTime; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory Loading Loading @@ -278,6 +247,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull SyncTransactionQueue syncTransactionQueue, @NonNull PipTransitionState pipTransitionState, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, @NonNull PipMenuController pipMenuController, Loading @@ -291,6 +261,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @ShellMainThread ShellExecutor mainExecutor) { mContext = context; mSyncTransactionQueue = syncTransactionQueue; mPipTransitionState = pipTransitionState; mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; mPipMenuController = pipMenuController; Loading Loading @@ -326,14 +297,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } public boolean isInPip() { return mState.isInPip(); return mPipTransitionState.isInPip(); } /** * Returns whether the entry animation is waiting to be started. */ public boolean isEntryScheduled() { return mState == State.ENTRY_SCHEDULED; return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED; } /** Loading Loading @@ -401,9 +372,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void exitPip(int animationDurationMs) { if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) { if (!mPipTransitionState.isInPip() || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP || mToken == null) { Log.wtf(TAG, "Not allowed to exitPip in current state" + " mState=" + mState + " mToken=" + mToken); + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken); return; } Loading @@ -427,7 +400,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, wct.setBoundsChangeTransaction(mToken, tx); // Set the exiting state first so if there is fixed rotation later, the running animation // won't be interrupted by alpha animation for existing PiP. mState = State.EXITING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); if (Transitions.ENABLE_SHELL_TRANSITIONS) { mPipTransitionController.startTransition(destinationBounds, wct); Loading Loading @@ -470,9 +443,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Removes PiP immediately. */ public void removePip() { if (!mState.isInPip() || mToken == null) { if (!mPipTransitionState.isInPip() || mToken == null) { Log.wtf(TAG, "Not allowed to removePip in current state" + " mState=" + mState + " mToken=" + mToken); + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken); return; } Loading @@ -486,7 +459,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setDuration(mExitAnimationDuration); animator.setInterpolator(Interpolators.ALPHA_OUT); animator.start(); mState = State.EXITING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); } private void removePipImmediately() { Loading @@ -508,7 +481,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Objects.requireNonNull(info, "Requires RunningTaskInfo"); mTaskInfo = info; mToken = mTaskInfo.token; mState = State.TASK_APPEARED; mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED); mLeash = leash; mPictureInPictureParams = mTaskInfo.pictureInPictureParams; setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, Loading Loading @@ -562,7 +535,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, null /* updateBoundsCallback */); mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); mOneShotAnimationType = ANIM_TYPE_BOUNDS; Loading @@ -589,7 +562,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); animateResizePip(currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */); mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } /** Loading @@ -614,7 +587,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); mState = State.ENTRY_SCHEDULED; mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED); applyEnterPipSyncTransaction(destinationBounds, () -> { mPipAnimationController .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) Loading @@ -625,7 +598,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .start(); // 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; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); }, null /* boundsChangeTransaction */); } Loading Loading @@ -672,7 +645,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void sendOnPipTransitionStarted( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } mPipTransitionController.sendOnPipTransitionStarted(direction); } Loading @@ -681,7 +654,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, void sendOnPipTransitionFinished( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERED_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); } mPipTransitionController.sendOnPipTransitionFinished(direction); // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent. Loading @@ -706,7 +679,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { if (mState == State.UNDEFINED) { if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } final WindowContainerToken token = info.token; Loading @@ -718,7 +691,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, clearWaitForFixedRotation(); mInSwipePipToHomeTransition = false; mPictureInPictureParams = null; mState = State.UNDEFINED; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); Loading @@ -732,8 +705,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) { Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState); if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) { Log.d(TAG, "Defer onTaskInfoChange in current state: " + mPipTransitionState.getTransitionState()); // Defer applying PiP parameters if the task is entering PiP to avoid disturbing // the animation. mDeferredTaskInfo = info; Loading Loading @@ -766,7 +741,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mNextRotation = newRotation; mWaitForFixedRotation = true; if (mState.isInPip()) { if (mPipTransitionState.isInPip()) { // Fade out the existing PiP to avoid jump cut during seamless rotation. fadeExistingPip(false /* show */); } Loading @@ -777,7 +752,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (!mWaitForFixedRotation) { return; } if (mState == State.TASK_APPEARED) { if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) { if (mInSwipePipToHomeTransition) { onEndOfSwipePipToHomeTransition(); } else { Loading @@ -785,9 +760,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(), mEnterAnimationDuration); } } else if (mState == State.ENTERED_PIP && mHasFadeOut) { } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP && mHasFadeOut) { fadeExistingPip(true /* show */); } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) { } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP && mDeferredAnimEndTransaction != null) { final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController.getCurrentAnimator(); final Rect destinationBounds = animator.getDestinationBounds(); Loading Loading @@ -848,7 +825,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { final boolean rotatingPip = mState.isInPip() && fromRotation; final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation; if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) { // The position will be used by fade-in animation when the fixed rotation is done. mPipBoundsState.setBounds(destinationBoundsOut); Loading Loading @@ -981,7 +958,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, int durationMs, Consumer<Rect> updateBoundsCallback) { if (!mState.isInPip()) { if (!mPipTransitionState.isInPip()) { // TODO: tend to use shouldBlockResizeRequest here as well but need to consider // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window // container transaction callback and we want to set the mState immediately. Loading Loading @@ -1011,7 +988,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .crop(tx, mLeash, toBounds) .round(tx, mLeash, mState.isInPip()); .round(tx, mLeash, mPipTransitionState.isInPip()); if (mPipMenuController.isMenuVisible()) { mPipMenuController.resizePipMenu(mLeash, tx, toBounds); } else { Loading Loading @@ -1089,7 +1066,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void scheduleFinishResizePip(Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, Consumer<Rect> updateBoundsCallback) { if (mState.shouldBlockResizeRequest()) { if (mPipTransitionState.shouldBlockResizeRequest()) { return; } Loading @@ -1106,7 +1083,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .resetScale(tx, mLeash, destinationBounds) .round(tx, mLeash, mState.isInPip()); .round(tx, mLeash, mPipTransitionState.isInPip()); return tx; } Loading @@ -1115,7 +1092,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleOffsetPip(Rect originalBounds, int offset, int duration, Consumer<Rect> updateBoundsCallback) { if (mState.shouldBlockResizeRequest()) { if (mPipTransitionState.shouldBlockResizeRequest()) { return; } if (mWaitForFixedRotation) { Loading Loading @@ -1373,7 +1350,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mToken=" + mToken + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +10 −1 Original line number Diff line number Diff line Loading @@ -52,19 +52,23 @@ import com.android.wm.shell.transition.Transitions; */ public class PipTransition extends PipTransitionController { private final PipTransitionState mPipTransitionState; private final int mEnterExitAnimationDuration; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; private Rect mExitDestinationBounds = new Rect(); public PipTransition(Context context, PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsState pipBoundsState, PipTransitionState pipTransitionState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, Transitions transitions, @NonNull ShellTaskOrganizer shellTaskOrganizer) { super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); mPipTransitionState = pipTransitionState; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); } Loading @@ -85,6 +89,7 @@ public class PipTransition extends PipTransitionController { if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) { final TransitionInfo.Change change = info.getChanges().get(0); mFinishCallback = finishCallback; startTransaction.apply(); boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(), new Rect(mExitDestinationBounds)); mExitDestinationBounds.setEmpty(); Loading Loading @@ -114,6 +119,7 @@ public class PipTransition extends PipTransitionController { startTransaction.setAlpha(wallpaper.getLeash(), 1.f); } mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), startTransaction, finishTransaction); Loading @@ -130,6 +136,9 @@ public class PipTransition extends PipTransitionController { public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx) { if (isInPipDirection(direction)) { mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); } WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(taskInfo, destinationBounds, direction, tx, wct); Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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; import android.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and * {@link PipTransition}. */ public class PipTransitionState { public static final int UNDEFINED = 0; public static final int TASK_APPEARED = 1; public static final int ENTRY_SCHEDULED = 2; public static final int ENTERING_PIP = 3; public static final int ENTERED_PIP = 4; public static final int EXITING_PIP = 5; // Not a complete set of states but serves what we want right now. @IntDef(prefix = { "TRANSITION_STATE_" }, value = { UNDEFINED, TASK_APPEARED, ENTRY_SCHEDULED, ENTERING_PIP, ENTERED_PIP, EXITING_PIP }) @Retention(RetentionPolicy.SOURCE) public @interface TransitionState {} private @TransitionState int mState; public PipTransitionState() { mState = UNDEFINED; } public void setTransitionState(@TransitionState int state) { mState = state; } public @TransitionState int getTransitionState() { return mState; } public boolean isInPip() { return mState >= TASK_APPEARED && mState != EXITING_PIP; } /** * Resize request can be initiated in other component, ignore if we are no longer in PIP, * still waiting for animation or we're exiting from it. * * @return {@code true} if the resize request should be blocked/ignored. */ public boolean shouldBlockResizeRequest() { return mState < ENTERING_PIP || mState == EXITING_PIP; } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; private TestShellExecutor mMainExecutor; private PipBoundsState mPipBoundsState; private PipTransitionState mPipTransitionState; private PipBoundsAlgorithm mPipBoundsAlgorithm; private ComponentName mComponent1; Loading @@ -90,11 +91,12 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); mPipTransitionState = new PipTransitionState(); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, new PipSnapAlgorithm()); mMainExecutor = new TestShellExecutor(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mMockSyncTransactionQueue, mPipBoundsState, mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState, mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController, mMockPipSurfaceTransactionHelper, mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController, Loading packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +9 −1 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.tv.TvPipController; import com.android.wm.shell.pip.tv.TvPipMenuController; Loading Loading @@ -142,12 +143,19 @@ public abstract class TvPipModule { return new PipAnimationController(pipSurfaceTransactionHelper); } @WMSingleton @Provides static PipTransitionState providePipTransitionState() { return new PipTransitionState(); } @WMSingleton @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, TvPipMenuController tvPipMenuController, SyncTransactionQueue syncTransactionQueue, PipBoundsState pipBoundsState, PipTransitionState pipTransitionState, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, PipTransitionController pipTransitionController, Loading @@ -157,7 +165,7 @@ public abstract class TvPipModule { PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm, syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm, tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +41 −64 Original line number Diff line number Diff line Loading @@ -107,38 +107,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; // Not a complete set of states but serves what we want right now. private enum State { UNDEFINED(0), TASK_APPEARED(1), ENTRY_SCHEDULED(2), ENTERING_PIP(3), ENTERED_PIP(4), EXITING_PIP(5); private final int mStateValue; State(int value) { mStateValue = value; } private boolean isInPip() { return mStateValue >= TASK_APPEARED.mStateValue && mStateValue != EXITING_PIP.mStateValue; } /** * Resize request can be initiated in other component, ignore if we are no longer in PIP, * still waiting for animation or we're exiting from it. * * @return {@code true} if the resize request should be blocked/ignored. */ private boolean shouldBlockResizeRequest() { return mStateValue < ENTERING_PIP.mStateValue || mStateValue == EXITING_PIP.mStateValue; } } private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; Loading Loading @@ -190,7 +158,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final boolean isExitPipDirection = isOutPipDirection(direction) || isRemovePipDirection(direction); if (mState != State.EXITING_PIP || isExitPipDirection) { if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP || isExitPipDirection) { // Finish resize as long as we're not exiting PIP, or, if we are, only if this is // the end of an exit PIP animation. // This is necessary in case there was a resize animation ongoing when exit PIP Loading Loading @@ -233,7 +202,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private ActivityManager.RunningTaskInfo mDeferredTaskInfo; private WindowContainerToken mToken; private SurfaceControl mLeash; private State mState = State.UNDEFINED; private PipTransitionState mPipTransitionState; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private long mLastOneShotAlphaAnimationTime; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory Loading Loading @@ -278,6 +247,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull SyncTransactionQueue syncTransactionQueue, @NonNull PipTransitionState pipTransitionState, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, @NonNull PipMenuController pipMenuController, Loading @@ -291,6 +261,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @ShellMainThread ShellExecutor mainExecutor) { mContext = context; mSyncTransactionQueue = syncTransactionQueue; mPipTransitionState = pipTransitionState; mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; mPipMenuController = pipMenuController; Loading Loading @@ -326,14 +297,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } public boolean isInPip() { return mState.isInPip(); return mPipTransitionState.isInPip(); } /** * Returns whether the entry animation is waiting to be started. */ public boolean isEntryScheduled() { return mState == State.ENTRY_SCHEDULED; return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED; } /** Loading Loading @@ -401,9 +372,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void exitPip(int animationDurationMs) { if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) { if (!mPipTransitionState.isInPip() || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP || mToken == null) { Log.wtf(TAG, "Not allowed to exitPip in current state" + " mState=" + mState + " mToken=" + mToken); + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken); return; } Loading @@ -427,7 +400,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, wct.setBoundsChangeTransaction(mToken, tx); // Set the exiting state first so if there is fixed rotation later, the running animation // won't be interrupted by alpha animation for existing PiP. mState = State.EXITING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); if (Transitions.ENABLE_SHELL_TRANSITIONS) { mPipTransitionController.startTransition(destinationBounds, wct); Loading Loading @@ -470,9 +443,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Removes PiP immediately. */ public void removePip() { if (!mState.isInPip() || mToken == null) { if (!mPipTransitionState.isInPip() || mToken == null) { Log.wtf(TAG, "Not allowed to removePip in current state" + " mState=" + mState + " mToken=" + mToken); + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken); return; } Loading @@ -486,7 +459,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setDuration(mExitAnimationDuration); animator.setInterpolator(Interpolators.ALPHA_OUT); animator.start(); mState = State.EXITING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); } private void removePipImmediately() { Loading @@ -508,7 +481,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Objects.requireNonNull(info, "Requires RunningTaskInfo"); mTaskInfo = info; mToken = mTaskInfo.token; mState = State.TASK_APPEARED; mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED); mLeash = leash; mPictureInPictureParams = mTaskInfo.pictureInPictureParams; setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, Loading Loading @@ -562,7 +535,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, null /* updateBoundsCallback */); mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); mOneShotAnimationType = ANIM_TYPE_BOUNDS; Loading @@ -589,7 +562,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); animateResizePip(currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */); mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } /** Loading @@ -614,7 +587,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); mState = State.ENTRY_SCHEDULED; mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED); applyEnterPipSyncTransaction(destinationBounds, () -> { mPipAnimationController .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) Loading @@ -625,7 +598,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .start(); // 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; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); }, null /* boundsChangeTransaction */); } Loading Loading @@ -672,7 +645,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void sendOnPipTransitionStarted( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } mPipTransitionController.sendOnPipTransitionStarted(direction); } Loading @@ -681,7 +654,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, void sendOnPipTransitionFinished( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERED_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); } mPipTransitionController.sendOnPipTransitionFinished(direction); // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent. Loading @@ -706,7 +679,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { if (mState == State.UNDEFINED) { if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } final WindowContainerToken token = info.token; Loading @@ -718,7 +691,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, clearWaitForFixedRotation(); mInSwipePipToHomeTransition = false; mPictureInPictureParams = null; mState = State.UNDEFINED; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); Loading @@ -732,8 +705,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) { Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState); if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) { Log.d(TAG, "Defer onTaskInfoChange in current state: " + mPipTransitionState.getTransitionState()); // Defer applying PiP parameters if the task is entering PiP to avoid disturbing // the animation. mDeferredTaskInfo = info; Loading Loading @@ -766,7 +741,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mNextRotation = newRotation; mWaitForFixedRotation = true; if (mState.isInPip()) { if (mPipTransitionState.isInPip()) { // Fade out the existing PiP to avoid jump cut during seamless rotation. fadeExistingPip(false /* show */); } Loading @@ -777,7 +752,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (!mWaitForFixedRotation) { return; } if (mState == State.TASK_APPEARED) { if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) { if (mInSwipePipToHomeTransition) { onEndOfSwipePipToHomeTransition(); } else { Loading @@ -785,9 +760,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(), mEnterAnimationDuration); } } else if (mState == State.ENTERED_PIP && mHasFadeOut) { } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP && mHasFadeOut) { fadeExistingPip(true /* show */); } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) { } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP && mDeferredAnimEndTransaction != null) { final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController.getCurrentAnimator(); final Rect destinationBounds = animator.getDestinationBounds(); Loading Loading @@ -848,7 +825,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { final boolean rotatingPip = mState.isInPip() && fromRotation; final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation; if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) { // The position will be used by fade-in animation when the fixed rotation is done. mPipBoundsState.setBounds(destinationBoundsOut); Loading Loading @@ -981,7 +958,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, int durationMs, Consumer<Rect> updateBoundsCallback) { if (!mState.isInPip()) { if (!mPipTransitionState.isInPip()) { // TODO: tend to use shouldBlockResizeRequest here as well but need to consider // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window // container transaction callback and we want to set the mState immediately. Loading Loading @@ -1011,7 +988,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .crop(tx, mLeash, toBounds) .round(tx, mLeash, mState.isInPip()); .round(tx, mLeash, mPipTransitionState.isInPip()); if (mPipMenuController.isMenuVisible()) { mPipMenuController.resizePipMenu(mLeash, tx, toBounds); } else { Loading Loading @@ -1089,7 +1066,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void scheduleFinishResizePip(Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, Consumer<Rect> updateBoundsCallback) { if (mState.shouldBlockResizeRequest()) { if (mPipTransitionState.shouldBlockResizeRequest()) { return; } Loading @@ -1106,7 +1083,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .resetScale(tx, mLeash, destinationBounds) .round(tx, mLeash, mState.isInPip()); .round(tx, mLeash, mPipTransitionState.isInPip()); return tx; } Loading @@ -1115,7 +1092,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleOffsetPip(Rect originalBounds, int offset, int duration, Consumer<Rect> updateBoundsCallback) { if (mState.shouldBlockResizeRequest()) { if (mPipTransitionState.shouldBlockResizeRequest()) { return; } if (mWaitForFixedRotation) { Loading Loading @@ -1373,7 +1350,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mToken=" + mToken + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +10 −1 Original line number Diff line number Diff line Loading @@ -52,19 +52,23 @@ import com.android.wm.shell.transition.Transitions; */ public class PipTransition extends PipTransitionController { private final PipTransitionState mPipTransitionState; private final int mEnterExitAnimationDuration; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; private Rect mExitDestinationBounds = new Rect(); public PipTransition(Context context, PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsState pipBoundsState, PipTransitionState pipTransitionState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, Transitions transitions, @NonNull ShellTaskOrganizer shellTaskOrganizer) { super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); mPipTransitionState = pipTransitionState; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); } Loading @@ -85,6 +89,7 @@ public class PipTransition extends PipTransitionController { if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) { final TransitionInfo.Change change = info.getChanges().get(0); mFinishCallback = finishCallback; startTransaction.apply(); boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(), new Rect(mExitDestinationBounds)); mExitDestinationBounds.setEmpty(); Loading Loading @@ -114,6 +119,7 @@ public class PipTransition extends PipTransitionController { startTransaction.setAlpha(wallpaper.getLeash(), 1.f); } mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), startTransaction, finishTransaction); Loading @@ -130,6 +136,9 @@ public class PipTransition extends PipTransitionController { public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx) { if (isInPipDirection(direction)) { mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); } WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(taskInfo, destinationBounds, direction, tx, wct); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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; import android.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and * {@link PipTransition}. */ public class PipTransitionState { public static final int UNDEFINED = 0; public static final int TASK_APPEARED = 1; public static final int ENTRY_SCHEDULED = 2; public static final int ENTERING_PIP = 3; public static final int ENTERED_PIP = 4; public static final int EXITING_PIP = 5; // Not a complete set of states but serves what we want right now. @IntDef(prefix = { "TRANSITION_STATE_" }, value = { UNDEFINED, TASK_APPEARED, ENTRY_SCHEDULED, ENTERING_PIP, ENTERED_PIP, EXITING_PIP }) @Retention(RetentionPolicy.SOURCE) public @interface TransitionState {} private @TransitionState int mState; public PipTransitionState() { mState = UNDEFINED; } public void setTransitionState(@TransitionState int state) { mState = state; } public @TransitionState int getTransitionState() { return mState; } public boolean isInPip() { return mState >= TASK_APPEARED && mState != EXITING_PIP; } /** * Resize request can be initiated in other component, ignore if we are no longer in PIP, * still waiting for animation or we're exiting from it. * * @return {@code true} if the resize request should be blocked/ignored. */ public boolean shouldBlockResizeRequest() { return mState < ENTERING_PIP || mState == EXITING_PIP; } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; private TestShellExecutor mMainExecutor; private PipBoundsState mPipBoundsState; private PipTransitionState mPipTransitionState; private PipBoundsAlgorithm mPipBoundsAlgorithm; private ComponentName mComponent1; Loading @@ -90,11 +91,12 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); mPipTransitionState = new PipTransitionState(); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, new PipSnapAlgorithm()); mMainExecutor = new TestShellExecutor(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mMockSyncTransactionQueue, mPipBoundsState, mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState, mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController, mMockPipSurfaceTransactionHelper, mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController, Loading
packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +9 −1 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.tv.TvPipController; import com.android.wm.shell.pip.tv.TvPipMenuController; Loading Loading @@ -142,12 +143,19 @@ public abstract class TvPipModule { return new PipAnimationController(pipSurfaceTransactionHelper); } @WMSingleton @Provides static PipTransitionState providePipTransitionState() { return new PipTransitionState(); } @WMSingleton @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, TvPipMenuController tvPipMenuController, SyncTransactionQueue syncTransactionQueue, PipBoundsState pipBoundsState, PipTransitionState pipTransitionState, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, PipTransitionController pipTransitionController, Loading @@ -157,7 +165,7 @@ public abstract class TvPipModule { PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm, syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm, tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); Loading