diff options
| author | 2021-01-25 13:55:58 -0800 | |
|---|---|---|
| committer | 2021-02-03 10:34:17 -0800 | |
| commit | 5426e2ae4795a832063d3dc6b0a9942c7c38ee15 (patch) | |
| tree | 92d710d9cd3896b6edec129efd9858af3cfcaf64 | |
| parent | 08b0b82df910955935d9fb7cfd8938bc8f7f9edb (diff) | |
Add basic PiP Transition foundation.
This adds the proper hook for framework to request PiP transition from
Shell and run the animation as needed. Currently only Enter using
3-finger navigation is supported, along with a KI that the PiP bounds
are not finalized at the end of the animation.
Bug: 165793917
Test: Enter PiP with ENABLE_SHELL_TRANSITIONS flag on
Change-Id: I0b992840ddcf8ddfcec9759a5cb06b630f1899a8
18 files changed, 673 insertions, 193 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 90992fb92324..45aa3870ecb6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -21,6 +21,7 @@ import android.animation.Animator; import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.app.TaskInfo; import android.graphics.Rect; import android.view.Choreographer; import android.view.SurfaceControl; @@ -99,18 +100,20 @@ public class PipAnimationController { @SuppressWarnings("unchecked") @VisibleForTesting - public PipTransitionAnimator getAnimator(SurfaceControl leash, + public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, Rect destinationBounds, float alphaStart, float alphaEnd) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd)); + PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart, + alphaEnd)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { mCurrentAnimator.updateEndValue(alphaEnd); } else { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd)); + PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart, + alphaEnd)); } return mCurrentAnimator; } @@ -131,13 +134,13 @@ public class PipAnimationController { * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed. */ @VisibleForTesting - public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds, - Rect startBounds, Rect endBounds, Rect sourceHintRect, + public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, + Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds, - sourceHintRect, direction, 0 /* startingAngle */)); + PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, + endBounds, sourceHintRect, direction, 0 /* startingAngle */)); } 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 @@ -152,8 +155,8 @@ public class PipAnimationController { } else { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( - PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds, - sourceHintRect, direction, startingAngle)); + PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, + endBounds, sourceHintRect, direction, startingAngle)); } return mCurrentAnimator; } @@ -177,18 +180,18 @@ public class PipAnimationController { /** * Called when PiP animation is started. */ - public void onPipAnimationStart(PipTransitionAnimator animator) {} + public void onPipAnimationStart(TaskInfo taskInfo, PipTransitionAnimator animator) {} /** * Called when PiP animation is ended. */ - public void onPipAnimationEnd(SurfaceControl.Transaction tx, + public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, PipTransitionAnimator animator) {} /** * Called when PiP animation is cancelled. */ - public void onPipAnimationCancel(PipTransitionAnimator animator) {} + public void onPipAnimationCancel(TaskInfo taskInfo, PipTransitionAnimator animator) {} } /** @@ -198,6 +201,7 @@ public class PipAnimationController { public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { + private final TaskInfo mTaskInfo; private final SurfaceControl mLeash; private final @AnimationType int mAnimationType; private final Rect mDestinationBounds = new Rect(); @@ -213,9 +217,10 @@ public class PipAnimationController { private PipSurfaceTransactionHelper mSurfaceTransactionHelper; private @TransitionDirection int mTransitionDirection; - private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType, - Rect destinationBounds, T baseValue, T startValue, T endValue, - float startingAngle) { + private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, + @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, + T endValue, float startingAngle) { + mTaskInfo = taskInfo; mLeash = leash; mAnimationType = animationType; mDestinationBounds.set(destinationBounds); @@ -234,7 +239,7 @@ public class PipAnimationController { mCurrentValue = mStartValue; onStartTransaction(mLeash, newSurfaceControlTransaction()); if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationStart(this); + mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this); } } @@ -250,14 +255,14 @@ public class PipAnimationController { final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); onEndTransaction(mLeash, tx, mTransitionDirection); if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationEnd(tx, this); + mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this); } } @Override public void onAnimationCancel(Animator animation) { if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationCancel(this); + mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this); } } @@ -368,9 +373,9 @@ public class PipAnimationController { abstract void applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction); - static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, + static PipTransitionAnimator<Float> ofAlpha(TaskInfo taskInfo, SurfaceControl leash, Rect destinationBounds, float startValue, float endValue) { - return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA, + return new PipTransitionAnimator<Float>(taskInfo, leash, ANIM_TYPE_ALPHA, destinationBounds, startValue, startValue, endValue, 0) { @Override void applySurfaceControlTransaction(SurfaceControl leash, @@ -403,7 +408,7 @@ public class PipAnimationController { }; } - static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, + static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle) { // Just for simplicity we'll interpolate between the source rect hint insets and empty @@ -427,7 +432,7 @@ public class PipAnimationController { final Rect sourceInsets = new Rect(0, 0, 0, 0); // construct new Rect instances in case they are recycled - return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS, + return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), startingAngle) { private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index a8961ea3d8a8..ac5d14c802d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -19,7 +19,9 @@ package com.android.wm.shell.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import android.annotation.NonNull; +import android.app.PictureInPictureParams; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; @@ -27,7 +29,6 @@ import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.Size; import android.util.TypedValue; -import android.view.DisplayInfo; import android.view.Gravity; import com.android.wm.shell.common.DisplayLayout; @@ -142,11 +143,53 @@ public class PipBoundsAlgorithm { true /* useCurrentMinEdgeSize */, false /* useCurrentSize */); } + /** + * + * Get the smallest/most minimal size allowed. + */ + public Size getMinimalSize(ActivityInfo activityInfo) { + if (activityInfo == null || activityInfo.windowLayout == null) { + return null; + } + final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout; + // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout> + // without minWidth/minHeight + if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) { + return new Size(windowLayout.minWidth, windowLayout.minHeight); + } + return null; + } + + /** + * Returns the source hint rect if it is valid (if provided and is contained by the current + * task bounds). + */ + public static Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) { + final Rect sourceHintRect = params != null && params.hasSourceBoundsHint() + ? params.getSourceRectHint() + : null; + if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) { + return sourceHintRect; + } + return null; + } + public float getDefaultAspectRatio() { return mDefaultAspectRatio; } /** + * + * Give the aspect ratio if the supplied PiP params have one, or else return default. + */ + public float getAspectRatioOrDefault( + @android.annotation.Nullable PictureInPictureParams params) { + return params != null && params.hasSetAspectRatio() + ? params.getAspectRatio() + : getDefaultAspectRatio(); + } + + /** * @return whether the given {@param aspectRatio} is valid. */ private boolean isValidPictureInPictureAspectRatio(float aspectRatio) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index b112c51455d2..cb39b4e63655 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -25,7 +25,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.util.Size; import android.view.Display; -import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.TriConsumer; @@ -344,6 +343,16 @@ public final class PipBoundsState { } } + /** + * Initialize states when first entering PiP. + */ + public void setBoundsStateForEntry(ComponentName componentName, float aspectRatio, + Size overrideMinSize) { + setLastPipComponentName(componentName); + setAspectRatio(aspectRatio); + setOverrideMinSize(overrideMinSize); + } + /** Returns whether the shelf is currently showing. */ public boolean isShelfShowing() { return mIsShelfShowing; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b7958b7a7077..fb83006e8522 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -41,10 +41,10 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; +import android.app.TaskInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; @@ -54,7 +54,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.util.Rational; -import android.util.Size; import android.view.Display; import android.view.SurfaceControl; import android.window.TaskOrganizer; @@ -63,7 +62,6 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -71,10 +69,10 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.phone.PipMotionHelper; +import com.android.wm.shell.transition.Transitions; + import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -132,8 +130,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final @NonNull PipMenuController mPipMenuController; private final PipAnimationController mPipAnimationController; + private final PipTransitionController mPipTransitionController; private final PipUiEventLogger mPipUiEventLoggerLogger; - private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); @@ -145,7 +143,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = new PipAnimationController.PipAnimationCallback() { @Override - public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) { + public void onPipAnimationStart(TaskInfo taskInfo, + PipAnimationController.PipTransitionAnimator animator) { final int direction = animator.getTransitionDirection(); if (direction == TRANSITION_DIRECTION_TO_PIP) { // TODO (b//169221267): Add jank listener for transactions without buffer updates. @@ -156,7 +155,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } @Override - public void onPipAnimationEnd(SurfaceControl.Transaction tx, + public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { final int direction = animator.getTransitionDirection(); finishResize(tx, animator.getDestinationBounds(), direction, @@ -170,7 +169,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } @Override - public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) { + public void onPipAnimationCancel(TaskInfo taskInfo, + PipAnimationController.PipTransitionAnimator animator) { sendOnPipTransitionCancelled(animator.getTransitionDirection()); } }; @@ -202,7 +202,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, @NonNull PipMenuController pipMenuController, + @NonNull PipAnimationController pipAnimationController, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, + @NonNull PipTransitionController pipTransitionController, Optional<LegacySplitScreen> splitScreenOptional, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, @@ -211,10 +213,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; mPipMenuController = pipMenuController; + mPipTransitionController = pipTransitionController; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; - mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper); + mPipAnimationController = pipAnimationController; mPipUiEventLoggerLogger = pipUiEventLogger; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitScreenOptional = splitScreenOptional; @@ -246,13 +249,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * Registers {@link PipTransitionCallback} to receive transition callbacks. - */ - public void registerPipTransitionCallback(PipTransitionCallback callback) { - mPipTransitionCallbacks.add(callback); - } - - /** * Registers a callback when a display change has been detected when we enter PiP. */ public void registerOnDisplayIdChangeCallback(IntConsumer onDisplayIdChangeCallback) { @@ -275,7 +271,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams) { mInSwipePipToHomeTransition = true; - sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP); + sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP); setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo); // disable the conflicting transaction from fixed rotation, see also // onFixedRotationStarted and onFixedRotationFinished @@ -296,9 +292,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params, ActivityInfo activityInfo) { - mPipBoundsState.setLastPipComponentName(componentName); - mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(params)); - mPipBoundsState.setOverrideMinSize(getMinimalSize(activityInfo)); + mPipBoundsState.setBoundsStateForEntry(componentName, + mPipBoundsAlgorithm.getAspectRatioOrDefault(params), + mPipBoundsAlgorithm.getMinimalSize(activityInfo)); } /** @@ -362,8 +358,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, t.apply(); // Make sure to grab the latest source hint rect as it could have been // updated right after applying the windowing mode change. - final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams, - destinationBounds); + final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + mPictureInPictureParams, destinationBounds); scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds, 0 /* startingAngle */, sourceHintRect, direction, animationDurationMs, null /* updateBoundsCallback */); @@ -398,7 +394,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // removePipImmediately is expected when the following animation finishes. mPipAnimationController - .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f) + .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f) .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -470,10 +466,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + mPipMenuController.attach(mLeash); + } + return; + } + if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { mPipMenuController.attach(mLeash); - final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams, - currentBounds); + final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + info.pictureInPictureParams, currentBounds); scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); @@ -486,21 +489,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } - /** - * Returns the source hint rect if it is valid (if provided and is contained by the current - * task bounds). - */ - private Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) { - final Rect sourceHintRect = params != null - && params.hasSourceBoundsHint() - ? params.getSourceRectHint() - : null; - if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) { - return sourceHintRect; - } - return null; - } - @VisibleForTesting void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) { // If we are fading the PIP in, then we should move the pip to the final location as @@ -512,7 +500,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, tx.apply(); applyEnterPipSyncTransaction(destinationBounds, () -> { mPipAnimationController - .getAnimator(mLeash, destinationBounds, 0f, 1f) + .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) @@ -547,19 +535,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void sendOnPipTransitionStarted( @PipAnimationController.TransitionDirection int direction) { - sendOnPipTransitionStarted(mTaskInfo.baseActivity, direction); - } - - private void sendOnPipTransitionStarted(ComponentName componentName, - @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERING_PIP; } - final Rect pipBounds = mPipBoundsState.getBounds(); - for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { - final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionStarted(componentName, direction, pipBounds); - } + mPipTransitionController.sendOnPipTransitionStarted(direction); } private void sendOnPipTransitionFinished( @@ -567,18 +546,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERED_PIP; } - for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { - final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction); - } + mPipTransitionController.sendOnPipTransitionFinished(direction); } private void sendOnPipTransitionCancelled( @PipAnimationController.TransitionDirection int direction) { - for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { - final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction); - } + mPipTransitionController.sendOnPipTransitionCancelled(direction); } /** @@ -616,7 +589,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); mPipBoundsState.setLastPipComponentName(info.topActivity); - mPipBoundsState.setOverrideMinSize(getMinimalSize(info.topActivityInfo)); + mPipBoundsState.setOverrideMinSize( + mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo)); final PictureInPictureParams newParams = info.pictureInPictureParams; if (newParams == null || !applyPictureInPictureParams(newParams)) { Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams); @@ -1081,33 +1055,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE ? mPipBoundsState.getBounds() : currentBounds; mPipAnimationController - .getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect, - direction, startingAngle) + .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, + sourceHintRect, direction, startingAngle) .setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .start(); } - private Size getMinimalSize(ActivityInfo activityInfo) { - if (activityInfo == null || activityInfo.windowLayout == null) { - return null; - } - final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout; - // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout> - // without minWidth/minHeight - if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) { - return new Size(windowLayout.minWidth, windowLayout.minHeight); - } - return null; - } - - private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) { - return params == null || !params.hasSetAspectRatio() - ? mPipBoundsAlgorithm.getDefaultAspectRatio() - : params.getAspectRatio(); - } - /** * Sync with {@link LegacySplitScreen} on destination bounds if PiP is going to split screen. * @@ -1157,24 +1112,4 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public String toString() { return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP); } - - /** - * Callback interface for PiP transitions (both from and to PiP mode) - */ - public interface PipTransitionCallback { - /** - * Callback when the pip transition is started. - */ - void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds); - - /** - * Callback when the pip transition is finished. - */ - void onPipTransitionFinished(ComponentName activity, int direction); - - /** - * Callback when the pip transition is cancelled. - */ - void onPipTransitionCanceled(ComponentName activity, int direction); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java new file mode 100644 index 000000000000..91e8c9939244 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -0,0 +1,162 @@ +/* + * 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 static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + +import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; +import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; +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; + +import android.app.TaskInfo; +import android.content.Context; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.transition.Transitions; + +/** + * Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and + * exit animation. + */ +public class PipTransition extends PipTransitionController { + + private final int mEnterExitAnimationDuration; + private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + private Transitions.TransitionFinishCallback mFinishCallback; + + public PipTransition(Context context, + PipBoundsState pipBoundsState, PipMenuController pipMenuController, + PipBoundsAlgorithm pipBoundsAlgorithm, + PipAnimationController pipAnimationController, + Transitions transitions, + @NonNull ShellTaskOrganizer shellTaskOrganizer) { + super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, + pipAnimationController, transitions, shellTaskOrganizer); + mEnterExitAnimationDuration = context.getResources() + .getInteger(R.integer.config_pipResizeAnimationDuration); + } + + @Override + public boolean startAnimation(@android.annotation.NonNull IBinder transition, + @android.annotation.NonNull TransitionInfo info, + @android.annotation.NonNull SurfaceControl.Transaction t, + @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() != null + && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_PINNED) { + mFinishCallback = finishCallback; + return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t); + } + } + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } + + @Override + public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, + @PipAnimationController.TransitionDirection int direction, + SurfaceControl.Transaction tx) { + WindowContainerTransaction wct = new WindowContainerTransaction(); + prepareFinishResizeTransaction(taskInfo, destinationBounds, + direction, tx, wct); + mFinishCallback.onTransitionFinished(wct, null); + finishResizeForMenu(destinationBounds); + } + + private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, + final SurfaceControl.Transaction t) { + setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams, + taskInfo.topActivityInfo); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds(); + PipAnimationController.PipTransitionAnimator animator; + if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + final Rect sourceHintRect = + PipBoundsAlgorithm.getValidSourceHintRect( + taskInfo.pictureInPictureParams, currentBounds); + animator = mPipAnimationController.getAnimator(taskInfo, leash, + currentBounds, currentBounds, destinationBounds, sourceHintRect, + TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */); + } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + t.setAlpha(leash, 0f); + t.apply(); + animator = mPipAnimationController.getAnimator(taskInfo, leash, + destinationBounds, 0f, 1f); + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + } else { + throw new RuntimeException("Unrecognized animation type: " + + mOneShotAnimationType); + } + animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(mEnterExitAnimationDuration) + .start(); + return true; + } + + private void finishResizeForMenu(Rect destinationBounds) { + mPipMenuController.movePipMenu(null, null, destinationBounds); + mPipMenuController.updateMenuBounds(destinationBounds); + } + + private void prepareFinishResizeTransaction(TaskInfo taskInfo, Rect destinationBounds, + @PipAnimationController.TransitionDirection int direction, + SurfaceControl.Transaction tx, + WindowContainerTransaction wct) { + Rect taskBounds = null; + if (isInPipDirection(direction)) { + // If we are animating from fullscreen using a bounds animation, then reset the + // activity windowing mode set by WM, and set the task bounds to the final bounds + taskBounds = destinationBounds; + wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); + wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds); + } else if (isOutPipDirection(direction)) { + // If we are animating to fullscreen, then we need to reset the override bounds + // on the task to ensure that the task "matches" the parent's bounds. + taskBounds = (direction == TRANSITION_DIRECTION_LEAVE_PIP) + ? null : destinationBounds; + wct.setWindowingMode(taskInfo.token, getOutPipWindowingMode()); + // Simply reset the activity mode set prior to the animation running. + wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); + } + + wct.setBounds(taskInfo.token, taskBounds); + wct.setBoundsChangeTransaction(taskInfo.token, tx); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java new file mode 100644 index 000000000000..d801c918973a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -0,0 +1,184 @@ +/* + * 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 static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; + +import android.app.PictureInPictureParams; +import android.app.TaskInfo; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.view.SurfaceControl; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.transition.Transitions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Responsible supplying PiP Transitions. + */ +public abstract class PipTransitionController implements Transitions.TransitionHandler { + + protected final PipAnimationController mPipAnimationController; + protected final PipBoundsAlgorithm mPipBoundsAlgorithm; + protected final PipBoundsState mPipBoundsState; + protected final ShellTaskOrganizer mShellTaskOrganizer; + protected final PipMenuController mPipMenuController; + private final Handler mMainHandler; + private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); + + protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback = + new PipAnimationController.PipAnimationCallback() { + @Override + public void onPipAnimationStart(TaskInfo taskInfo, + PipAnimationController.PipTransitionAnimator animator) { + final int direction = animator.getTransitionDirection(); + if (direction == TRANSITION_DIRECTION_TO_PIP) { + // TODO (b//169221267): Add jank listener for transactions without buffer + // updates. + //InteractionJankMonitor.getInstance().begin( + // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000); + } + sendOnPipTransitionStarted(direction); + } + + @Override + public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, + PipAnimationController.PipTransitionAnimator animator) { + final int direction = animator.getTransitionDirection(); + mPipBoundsState.setBounds(animator.getDestinationBounds()); + if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { + return; + } + onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx); + sendOnPipTransitionFinished(direction); + if (direction == TRANSITION_DIRECTION_TO_PIP) { + // TODO (b//169221267): Add jank listener for transactions without buffer + // updates. + //InteractionJankMonitor.getInstance().end( + // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP); + } + } + + @Override + public void onPipAnimationCancel(TaskInfo taskInfo, + PipAnimationController.PipTransitionAnimator animator) { + sendOnPipTransitionCancelled(animator.getTransitionDirection()); + } + }; + + /** + * Called when transition is about to finish. This is usually for performing tasks such as + * applying WindowContainerTransaction to finalize the PiP bounds and send to the framework. + */ + public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, + @PipAnimationController.TransitionDirection int direction, + SurfaceControl.Transaction tx) { + } + + public PipTransitionController(PipBoundsState pipBoundsState, + PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, + PipAnimationController pipAnimationController, Transitions transitions, + @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) { + mPipBoundsState = pipBoundsState; + mPipMenuController = pipMenuController; + mShellTaskOrganizer = shellTaskOrganizer; + mPipBoundsAlgorithm = pipBoundsAlgorithm; + mPipAnimationController = pipAnimationController; + mMainHandler = new Handler(Looper.getMainLooper()); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.addHandler(this); + } + } + + /** + * Registers {@link PipTransitionCallback} to receive transition callbacks. + */ + public void registerPipTransitionCallback(PipTransitionCallback callback) { + mPipTransitionCallbacks.add(callback); + } + + protected void sendOnPipTransitionStarted( + @PipAnimationController.TransitionDirection int direction) { + final Rect pipBounds = mPipBoundsState.getBounds(); + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionStarted(direction, pipBounds); + } + } + + protected void sendOnPipTransitionFinished( + @PipAnimationController.TransitionDirection int direction) { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionFinished(direction); + } + } + + protected void sendOnPipTransitionCancelled( + @PipAnimationController.TransitionDirection int direction) { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionCanceled(direction); + } + } + + /** + * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined + * and can be overridden to restore to an alternate windowing mode. + */ + public int getOutPipWindowingMode() { + // By default, simply reset the windowing mode to undefined. + return WINDOWING_MODE_UNDEFINED; + } + + protected void setBoundsStateForEntry(ComponentName componentName, + PictureInPictureParams params, + ActivityInfo activityInfo) { + mPipBoundsState.setBoundsStateForEntry(componentName, + mPipBoundsAlgorithm.getAspectRatioOrDefault(params), + mPipBoundsAlgorithm.getMinimalSize(activityInfo)); + } + + /** + * Callback interface for PiP transitions (both from and to PiP mode) + */ + public interface PipTransitionCallback { + /** + * Callback when the pip transition is started. + */ + void onPipTransitionStarted(int direction, Rect pipBounds); + + /** + * Callback when the pip transition is finished. + */ + void onPipTransitionFinished(int direction); + + /** + * Callback when the pip transition is cancelled. + */ + void onPipTransitionCanceled(int direction); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index c06f9d03cdf7..c3970e33d736 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -61,6 +61,7 @@ import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUtils; import java.io.PrintWriter; @@ -69,7 +70,7 @@ import java.util.function.Consumer; /** * Manages the picture-in-picture (PIP) UI and states for Phones. */ -public class PipController implements PipTaskOrganizer.PipTransitionCallback { +public class PipController implements PipTransitionController.PipTransitionCallback { private static final String TAG = "PipController"; private Context mContext; @@ -82,6 +83,7 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { private PipBoundsAlgorithm mPipBoundsAlgorithm; private PipBoundsState mPipBoundsState; private PipTouchHandler mTouchHandler; + private PipTransitionController mPipTransitionController; protected final PipImpl mImpl = new PipImpl(); private final Rect mTmpInsetBounds = new Rect(); @@ -214,7 +216,6 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } } - /** * Instantiates {@link PipController}, returns {@code null} if the feature not supported. */ @@ -223,7 +224,8 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, + WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { Slog.w(TAG, "Device doesn't support Pip feature"); @@ -232,7 +234,8 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, - pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor) + pipTouchHandler, pipTransitionController, windowManagerShellWrapper, + taskStackListener, mainExecutor) .mImpl; } @@ -245,6 +248,7 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, + PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor @@ -266,9 +270,10 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { mMenuController = phonePipMenuController; mTouchHandler = pipTouchHandler; mAppOpsListener = pipAppOpsListener; + mPipTransitionController = pipTransitionController; mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), INPUT_CONSUMER_PIP, mainExecutor); - mPipTaskOrganizer.registerPipTransitionCallback(this); + mPipTransitionController.registerPipTransitionCallback(this); mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> { mPipBoundsState.setDisplayId(displayId); onDisplayChanged(displayController.getDisplayLayout(displayId), @@ -489,7 +494,7 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } @Override - public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { + public void onPipTransitionStarted(int direction, Rect pipBounds) { if (isOutPipDirection(direction)) { // Exiting PIP, save the reentry state to restore to when re-entering. saveReentryState(pipBounds); @@ -514,12 +519,12 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } @Override - public void onPipTransitionFinished(ComponentName activity, int direction) { + public void onPipTransitionFinished(int direction) { onPipTransitionFinishedOrCanceled(direction); } @Override - public void onPipTransitionCanceled(ComponentName activity, int direction) { + public void onPipTransitionCanceled(int direction) { onPipTransitionFinishedOrCanceled(direction); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index fd4ea61713ef..b19dcae2def8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -22,7 +22,6 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; @@ -44,6 +43,7 @@ import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import java.util.function.Consumer; @@ -152,13 +152,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ private Runnable mPostPipTransitionCallback; - private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback = - new PipTaskOrganizer.PipTransitionCallback() { + private final PipTransitionController.PipTransitionCallback mPipTransitionCallback = + new PipTransitionController.PipTransitionCallback() { @Override - public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {} + public void onPipTransitionStarted(int direction, Rect pipBounds) {} @Override - public void onPipTransitionFinished(ComponentName activity, int direction) { + public void onPipTransitionFinished(int direction) { if (mPostPipTransitionCallback != null) { mPostPipTransitionCallback.run(); mPostPipTransitionCallback = null; @@ -166,20 +166,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } @Override - public void onPipTransitionCanceled(ComponentName activity, int direction) {} + public void onPipTransitionCanceled(int direction) {} }; public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController, - PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, - ShellExecutor mainExecutor) { + PipSnapAlgorithm snapAlgorithm, PipTransitionController pipTransitionController, + FloatingContentCoordinator floatingContentCoordinator, ShellExecutor mainExecutor) { mContext = context; mPipTaskOrganizer = pipTaskOrganizer; mPipBoundsState = pipBoundsState; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFloatingContentCoordinator = floatingContentCoordinator; - mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback); + pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback); mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 3cb3ae89b5f5..e69c6f2f47bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -51,6 +51,7 @@ import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUiEventLogger; import java.io.PrintWriter; @@ -156,6 +157,7 @@ public class PipTouchHandler { PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, FloatingContentCoordinator floatingContentCoordinator, PipUiEventLogger pipUiEventLogger, ShellExecutor mainExecutor) { @@ -168,7 +170,7 @@ public class PipTouchHandler { mMenuController.addListener(new PipMenuListener()); mGesture = new DefaultPipTouchGesture(); mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer, - mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(), + mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(), pipTransitionController, floatingContentCoordinator, mainExecutor); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 75fc9f5a4ecf..56f183fd7303 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -46,6 +46,7 @@ import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -53,7 +54,7 @@ import java.lang.annotation.RetentionPolicy; /** * Manages the picture-in-picture (PIP) UI and states. */ -public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, +public class TvPipController implements PipTransitionController.PipTransitionCallback, TvPipMenuController.Delegate, TvPipNotificationController.Delegate { private static final String TAG = "TvPipController"; static final boolean DEBUG = true; @@ -105,6 +106,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, TvPipNotificationController pipNotificationController, @@ -116,6 +118,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, + pipTransitionController, tvPipMenuController, pipMediaController, pipNotificationController, @@ -129,6 +132,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, TvPipNotificationController pipNotificationController, @@ -152,7 +156,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, mTvPipMenuController.setDelegate(this); mPipTaskOrganizer = pipTaskOrganizer; - mPipTaskOrganizer.registerPipTransitionCallback(this); + pipTransitionController.registerPipTransitionCallback(this); loadConfigurations(); @@ -302,17 +306,17 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, } @Override - public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { + public void onPipTransitionStarted(int direction, Rect pipBounds) { if (DEBUG) Log.d(TAG, "onPipTransition_Started(), state=" + stateToName(mState)); } @Override - public void onPipTransitionCanceled(ComponentName activity, int direction) { + public void onPipTransitionCanceled(int direction) { if (DEBUG) Log.d(TAG, "onPipTransition_Canceled(), state=" + stateToName(mState)); } @Override - public void onPipTransitionFinished(ComponentName activity, int direction) { + public void onPipTransitionFinished(int direction) { if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState)); if (mState == STATE_PIP_MENU) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java new file mode 100644 index 000000000000..b7caf72641a3 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java @@ -0,0 +1,72 @@ +/* + * 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.tv; + +import android.app.TaskInfo; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.pip.PipAnimationController; +import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMenuController; +import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.transition.Transitions; + +/** + * PiP Transition for TV. + * TODO: Implement animation once TV is using Transitions. + */ +public class TvPipTransition extends PipTransitionController { + public TvPipTransition(PipBoundsState pipBoundsState, + PipMenuController pipMenuController, + PipBoundsAlgorithm pipBoundsAlgorithm, + PipAnimationController pipAnimationController, + Transitions transitions, + @NonNull ShellTaskOrganizer shellTaskOrganizer) { + super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, + transitions, shellTaskOrganizer); + } + + @Override + public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, int direction, + SurfaceControl.Transaction tx) { + + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index c565a4cc2e28..0087d917f007 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import android.app.TaskInfo; import android.graphics.Matrix; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -54,6 +55,9 @@ public class PipAnimationControllerTest extends ShellTestCase { private SurfaceControl mLeash; @Mock + private TaskInfo mTaskInfo; + + @Mock private PipAnimationController.PipAnimationCallback mPipAnimationCallback; @Before @@ -70,7 +74,7 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void getAnimator_withAlpha_returnFloatAnimator() { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, new Rect(), 0f, 1f); + .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f); assertEquals("Expect ANIM_TYPE_ALPHA animation", animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA); @@ -79,7 +83,7 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void getAnimator_withBounds_returnBoundsAnimator() { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null, + .getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null, TRANSITION_DIRECTION_TO_PIP, 0); assertEquals("Expect ANIM_TYPE_BOUNDS animation", @@ -93,13 +97,13 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController - .getAnimator(mLeash, baseValue, startValue, endValue1, null, + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP, 0); oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); oldAnimator.start(); final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController - .getAnimator(mLeash, baseValue, startValue, endValue2, null, + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null, TRANSITION_DIRECTION_TO_PIP, 0); assertEquals("getAnimator with same type returns same animator", @@ -111,13 +115,13 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void getAnimator_setTransitionDirection() { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, new Rect(), 0f, 1f) + .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP); assertEquals("Transition to PiP mode", animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_PIP); animator = mPipAnimationController - .getAnimator(mLeash, new Rect(), 0f, 1f) + .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP); assertEquals("Transition to fullscreen mode", animator.getTransitionDirection(), TRANSITION_DIRECTION_LEAVE_PIP); @@ -131,7 +135,7 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, baseValue, startValue, endValue1, null, + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP, 0); animator.updateEndValue(endValue2); @@ -145,7 +149,7 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect startValue = new Rect(0, 0, 100, 100); final Rect endValue = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController - .getAnimator(mLeash, baseValue, startValue, endValue, null, + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP, 0); animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); @@ -153,16 +157,16 @@ public class PipAnimationControllerTest extends ShellTestCase { // onAnimationStart triggers onPipAnimationStart animator.onAnimationStart(animator); - verify(mPipAnimationCallback).onPipAnimationStart(animator); + verify(mPipAnimationCallback).onPipAnimationStart(mTaskInfo, animator); // onAnimationCancel triggers onPipAnimationCancel animator.onAnimationCancel(animator); - verify(mPipAnimationCallback).onPipAnimationCancel(animator); + verify(mPipAnimationCallback).onPipAnimationCancel(mTaskInfo, animator); // onAnimationEnd triggers onPipAnimationEnd animator.onAnimationEnd(animator); - verify(mPipAnimationCallback).onPipAnimationEnd(any(SurfaceControl.Transaction.class), - eq(animator)); + verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo), + any(SurfaceControl.Transaction.class), eq(animator)); } /** diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 7a810a1742d7..9430af946899 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -18,27 +18,23 @@ package com.android.wm.shell.pip; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.pm.ActivityInfo; -import android.graphics.Rect; import android.os.RemoteException; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Rational; import android.util.Size; -import android.view.Display; import android.view.DisplayInfo; import android.window.WindowContainerToken; @@ -47,9 +43,8 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.pip.phone.PhonePipMenuController; import org.junit.Before; import org.junit.Test; @@ -69,14 +64,17 @@ public class PipTaskOrganizerTest extends ShellTestCase { private PipTaskOrganizer mSpiedPipTaskOrganizer; @Mock private DisplayController mMockdDisplayController; - @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; + @Mock private PhonePipMenuController mMockPhonePipMenuController; + @Mock private PipAnimationController mMockPipAnimationController; + @Mock private PipTransitionController mMockPipTransitionController; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; private TestShellExecutor mMainExecutor; private PipBoundsState mPipBoundsState; + private PipBoundsAlgorithm mPipBoundsAlgorithm; private ComponentName mComponent1; private ComponentName mComponent2; @@ -87,10 +85,12 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); + mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); mMainExecutor = new TestShellExecutor(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, - mMockPipBoundsAlgorithm, mMockPhonePipMenuController, - mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController, + mPipBoundsAlgorithm, mMockPhonePipMenuController, + mMockPipAnimationController, mMockPipSurfaceTransactionHelper, + mMockPipTransitionController, mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor)); mMainExecutor.flushAll(); preparePipTaskOrg(); @@ -117,7 +117,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Test public void startSwipePipToHome_updatesLastPipComponentName() { - mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, null); + mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null)); assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName()); } @@ -126,7 +126,8 @@ public class PipTaskOrganizerTest extends ShellTestCase { public void startSwipePipToHome_updatesOverrideMinSize() { final Size minSize = new Size(100, 80); - mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize), null); + mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize), + createPipParams(null)); assertEquals(minSize, mPipBoundsState.getOverrideMinSize()); } @@ -200,9 +201,6 @@ public class PipTaskOrganizerTest extends ShellTestCase { final DisplayInfo info = new DisplayInfo(); mPipBoundsState.setDisplayLayout(new DisplayLayout(info, mContext.getResources(), true, true)); - when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect()); - when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat())) - .thenReturn(new Rect()); mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong()); doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 62ffac4fbd3f..cfe84639d24e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -46,6 +46,7 @@ import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import org.junit.Before; import org.junit.Test; @@ -68,6 +69,7 @@ public class PipControllerTest extends ShellTestCase { @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; @Mock private PipMediaController mMockPipMediaController; @Mock private PipTaskOrganizer mMockPipTaskOrganizer; + @Mock private PipTransitionController mMockPipTransitionController; @Mock private PipTouchHandler mMockPipTouchHandler; @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper; @Mock private PipBoundsState mMockPipBoundsState; @@ -80,8 +82,8 @@ public class PipControllerTest extends ShellTestCase { mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, - mMockExecutor); + mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, + mMockTaskStackListener, mMockExecutor); doAnswer(invocation -> { ((Runnable) invocation.getArgument(0)).run(); return null; @@ -90,7 +92,7 @@ public class PipControllerTest extends ShellTestCase { @Test public void instantiatePipController_registersPipTransitionCallback() { - verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any()); + verify(mMockPipTransitionController).registerPipTransitionCallback(any()); } @Test @@ -113,8 +115,8 @@ public class PipControllerTest extends ShellTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, - mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, - mMockExecutor)); + mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, + mMockTaskStackListener, mMockExecutor)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index b4cfbc281d61..449ad88f6532 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUiEventLogger; import org.junit.Before; @@ -67,6 +68,9 @@ public class PipTouchHandlerTest extends ShellTestCase { private PipTaskOrganizer mPipTaskOrganizer; @Mock + private PipTransitionController mMockPipTransitionController; + + @Mock private FloatingContentCoordinator mFloatingContentCoordinator; @Mock @@ -98,7 +102,8 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipSnapAlgorithm = new PipSnapAlgorithm(); mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, - mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor); + mMockPipTransitionController, mFloatingContentCoordinator, mPipUiEventLogger, + mMainExecutor); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 416de0465f1a..0795d89eb0bc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -29,15 +29,19 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; 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.PipUiEventLogger; import com.android.wm.shell.pip.tv.TvPipController; import com.android.wm.shell.pip.tv.TvPipMenuController; import com.android.wm.shell.pip.tv.TvPipNotificationController; +import com.android.wm.shell.pip.tv.TvPipTransition; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -58,6 +62,7 @@ public abstract class TvPipModule { PipTaskOrganizer pipTaskOrganizer, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, + PipTransitionController pipTransitionController, TvPipNotificationController tvPipNotificationController, TaskStackListenerImpl taskStackListener, WindowManagerShellWrapper windowManagerShellWrapper, @@ -68,6 +73,7 @@ public abstract class TvPipModule { pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, + pipTransitionController, tvPipMenuController, pipMediaController, tvPipNotificationController, @@ -92,6 +98,16 @@ public abstract class TvPipModule { // Handler needed for loadDrawableAsync() in PipControlsViewController @WMSingleton @Provides + static PipTransitionController provideTvPipTransition( + Transitions transitions, ShellTaskOrganizer shellTaskOrganizer, + PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm, + PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) { + return new TvPipTransition(pipBoundsState, pipMenuController, + pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); + } + + @WMSingleton + @Provides static TvPipMenuController providesTvPipMenuController( Context context, PipBoundsState pipBoundsState, @@ -113,16 +129,26 @@ public abstract class TvPipModule { @WMSingleton @Provides + static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper + pipSurfaceTransactionHelper) { + return new PipAnimationController(pipSurfaceTransactionHelper); + } + + @WMSingleton + @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, TvPipMenuController tvPipMenuController, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, + PipAnimationController pipAnimationController, + PipTransitionController pipTransitionController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - tvPipMenuController, pipSurfaceTransactionHelper, splitScreenOptional, - displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); + tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, + pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, + shellTaskOrganizer, mainExecutor); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 12a3b5d66d55..2aaa0951d9d9 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -17,13 +17,11 @@ package com.android.systemui.wmshell; import android.animation.AnimationHandler; -import android.app.ActivityTaskManager; import android.content.Context; import android.os.Handler; import android.view.IWindowManager; import com.android.systemui.dagger.WMSingleton; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.apppairs.AppPairs; @@ -41,18 +39,19 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransition; +import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.splitscreen.SplitScreen; -import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -104,12 +103,13 @@ public class WMShellModule { PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, + WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController, - phonePipMenuController, pipTaskOrganizer, pipTouchHandler, + phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController, windowManagerShellWrapper, taskStackListener, mainExecutor)); } @@ -143,12 +143,13 @@ public class WMShellModule { PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, FloatingContentCoordinator floatingContentCoordinator, PipUiEventLogger pipUiEventLogger, @ShellMainThread ShellExecutor mainExecutor) { return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm, - pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger, - mainExecutor); + pipBoundsState, pipTaskOrganizer, pipTransitionController, + floatingContentCoordinator, pipUiEventLogger, mainExecutor); } @WMSingleton @@ -157,12 +158,32 @@ public class WMShellModule { PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PhonePipMenuController menuPhoneController, + PipAnimationController pipAnimationController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, + PipTransitionController pipTransitionController, Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional, - displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); + menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper, + pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, + shellTaskOrganizer, mainExecutor); + } + + @WMSingleton + @Provides + static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper + pipSurfaceTransactionHelper) { + return new PipAnimationController(pipSurfaceTransactionHelper); + } + + @WMSingleton + @Provides + static PipTransitionController providePipTransitionController(Context context, + Transitions transitions, ShellTaskOrganizer shellTaskOrganizer, + PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm, + PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) { + return new PipTransition(context, pipBoundsState, pipMenuController, + pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index bbf6c7616d46..96e0e284afe9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -37,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_NONE; @@ -2180,6 +2181,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // display area, so reparent. rootTask.reparent(taskDisplayArea, true /* onTop */); } + mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask); + // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating // TODO(task-org): Figure-out more structured way to do this long term. |