diff options
5 files changed, 86 insertions, 23 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index 7805ec34e105..383afcf6f821 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -16,9 +16,11 @@ package com.android.wm.shell.pip2.phone; +import android.app.PictureInPictureParams; import android.content.Context; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.SystemProperties; import android.view.SurfaceControl; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -28,6 +30,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; +import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDesktopState; @@ -39,6 +42,7 @@ import com.android.wm.shell.shared.split.SplitScreenConstants; import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.Optional; +import java.util.function.Supplier; /** * Scheduler for Shell initiated PiP transitions and animations. @@ -46,6 +50,15 @@ import java.util.Optional; public class PipScheduler { private static final String TAG = PipScheduler.class.getSimpleName(); + /** + * The fixed start delay in ms when fading out the content overlay from bounds animation. + * The fadeout animation is guaranteed to start after the client has drawn under the new config. + */ + public static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS = + SystemProperties.getInt( + "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400); + private static final int CONTENT_OVERLAY_FADE_OUT_DURATION_MS = 500; + private final Context mContext; private final PipBoundsState mPipBoundsState; private final ShellExecutor mMainExecutor; @@ -60,6 +73,7 @@ public class PipScheduler { @Nullable private Runnable mUpdateMovementBoundsRunnable; private PipAlphaAnimatorSupplier mPipAlphaAnimatorSupplier; + private Supplier<PictureInPictureParams> mPipParamsSupplier; public PipScheduler(Context context, PipBoundsState pipBoundsState, @@ -222,6 +236,16 @@ public class PipScheduler { tx.apply(); } + void startOverlayFadeoutAnimation(@NonNull SurfaceControl overlayLeash, + boolean withStartDelay, @NonNull Runnable onAnimationEnd) { + PipAlphaAnimator animator = mPipAlphaAnimatorSupplier.get(mContext, overlayLeash, + null /* startTx */, null /* finishTx */, PipAlphaAnimator.FADE_OUT); + animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DURATION_MS); + animator.setStartDelay(withStartDelay ? EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0); + animator.setAnimationEndCallback(onAnimationEnd); + animator.start(); + } + void setUpdateMovementBoundsRunnable(@Nullable Runnable updateMovementBoundsRunnable) { mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; } @@ -236,6 +260,25 @@ public class PipScheduler { if (mPipBoundsState.getBounds().equals(newBounds)) { return; } + + // Take a screenshot of PiP and fade it out after resize is finished if seamless resize + // is off and if the PiP size is changing. + boolean animateCrossFadeResize = !getPipParams().isSeamlessResizeEnabled() + && !(mPipBoundsState.getBounds().width() == newBounds.width() + && mPipBoundsState.getBounds().height() == newBounds.height()); + if (animateCrossFadeResize) { + final Rect crop = new Rect(newBounds); + crop.offsetTo(0, 0); + // Note: Put this at layer=MAX_VALUE-2 since the input consumer for PIP is placed at + // MAX_VALUE-1 + final SurfaceControl snapshotSurface = ScreenshotUtils.takeScreenshot( + mSurfaceControlTransactionFactory.getTransaction(), + mPipTransitionState.getPinnedTaskLeash(), crop, Integer.MAX_VALUE - 2); + startOverlayFadeoutAnimation(snapshotSurface, false /* withStartDelay */, () -> { + mSurfaceControlTransactionFactory.getTransaction().remove(snapshotSurface).apply(); + }); + } + mPipBoundsState.setBounds(newBounds); maybeUpdateMovementBounds(); } @@ -259,4 +302,14 @@ public class PipScheduler { void setPipAlphaAnimatorSupplier(@NonNull PipAlphaAnimatorSupplier supplier) { mPipAlphaAnimatorSupplier = supplier; } + + void setPipParamsSupplier(@NonNull Supplier<PictureInPictureParams> pipParamsSupplier) { + mPipParamsSupplier = pipParamsSupplier; + } + + @NonNull + private PictureInPictureParams getPipParams() { + if (mPipParamsSupplier == null) return new PictureInPictureParams.Builder().build(); + return mPipParamsSupplier.get(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java index dbcbf3663827..d6634845ee21 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java @@ -91,6 +91,7 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener, }); } mPipResizeAnimatorSupplier = PipResizeAnimator::new; + mPipScheduler.setPipParamsSupplier(this::getPictureInPictureParams); } void setPictureInPictureParams(@Nullable PictureInPictureParams params) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 0cfab11dbc64..91fbd456eb63 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -50,7 +50,6 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; -import android.os.SystemProperties; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; @@ -102,15 +101,6 @@ public class PipTransition extends PipTransitionController implements "animating_bounds_change_duration"; static final int BOUNDS_CHANGE_JUMPCUT_DURATION = 0; - /** - * The fixed start delay in ms when fading out the content overlay from bounds animation. - * The fadeout animation is guaranteed to start after the client has drawn under the new config. - */ - private static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS = - SystemProperties.getInt( - "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400); - private static final int CONTENT_OVERLAY_FADE_OUT_DURATION_MS = 500; - // // Dependencies // @@ -481,7 +471,8 @@ public class PipTransition extends PipTransitionController implements if (swipePipToHomeOverlay != null) { // fadeout the overlay if needed. - startOverlayFadeoutAnimation(swipePipToHomeOverlay, () -> { + mPipScheduler.startOverlayFadeoutAnimation(swipePipToHomeOverlay, + true /* withStartDelay */, () -> { SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); tx.remove(swipePipToHomeOverlay); tx.apply(); @@ -542,8 +533,8 @@ public class PipTransition extends PipTransitionController implements animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange)); animator.setAnimationEndCallback(() -> { if (animator.getContentOverlayLeash() != null) { - startOverlayFadeoutAnimation(animator.getContentOverlayLeash(), - animator::clearAppIconOverlay); + mPipScheduler.startOverlayFadeoutAnimation(animator.getContentOverlayLeash(), + true /* withStartDelay */, animator::clearAppIconOverlay); } finishTransition(); }); @@ -551,16 +542,6 @@ public class PipTransition extends PipTransitionController implements return true; } - private void startOverlayFadeoutAnimation(@NonNull SurfaceControl overlayLeash, - @NonNull Runnable onAnimationEnd) { - PipAlphaAnimator animator = new PipAlphaAnimator(mContext, overlayLeash, - null /* startTx */, null /* finishTx */, PipAlphaAnimator.FADE_OUT); - animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DURATION_MS); - animator.setStartDelay(EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS); - animator.setAnimationEndCallback(onAnimationEnd); - animator.start(); - } - private void handleBoundsEnterFixedRotation(TransitionInfo info, TransitionInfo.Change outPipTaskChange, TransitionInfo.Change outPipActivityChange) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java index 0c1952910d1a..275e4882a79d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java @@ -28,6 +28,7 @@ import static org.mockito.kotlin.VerificationKt.times; import static org.mockito.kotlin.VerificationKt.verify; import android.app.ActivityManager; +import android.app.PictureInPictureParams; import android.content.Context; import android.content.res.Resources; import android.graphics.Matrix; @@ -47,6 +48,7 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import com.android.wm.shell.pip2.animation.PipAlphaAnimator; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.util.StubTransaction; import org.junit.Before; import org.junit.Test; @@ -107,6 +109,8 @@ public class PipSchedulerTest { mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory); mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, startTx, finishTx, direction) -> mMockAlphaAnimator); + final PictureInPictureParams params = new PictureInPictureParams.Builder().build(); + mPipScheduler.setPipParamsSupplier(() -> params); SurfaceControl testLeash = new SurfaceControl.Builder() .setContainerLayer() @@ -289,6 +293,28 @@ public class PipSchedulerTest { verify(mMockUpdateMovementBoundsRunnable, times(1)).run(); } + @Test + public void finishResize_nonSeamless_alphaAnimatorStarted() { + final PictureInPictureParams params = + new PictureInPictureParams.Builder().setSeamlessResizeEnabled(false).build(); + mPipScheduler.setPipParamsSupplier(() -> params); + when(mMockFactory.getTransaction()).thenReturn(new StubTransaction()); + + mPipScheduler.scheduleFinishResizePip(TEST_BOUNDS); + + verify(mMockAlphaAnimator, times(1)).start(); + } + + @Test + public void finishResize_seamless_animatorNotStarted() { + final PictureInPictureParams params = + new PictureInPictureParams.Builder().setSeamlessResizeEnabled(true).build(); + mPipScheduler.setPipParamsSupplier(() -> params); + + mPipScheduler.scheduleFinishResizePip(TEST_BOUNDS); + verify(mMockAlphaAnimator, never()).start(); + } + private void setNullPipTaskToken() { when(mMockPipTransitionState.getPipTaskToken()).thenReturn(null); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java index 1b462c30e017..333569a7206e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java @@ -294,6 +294,8 @@ public class PipTaskListenerTest { mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer, mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState, mMockPipBoundsAlgorithm, mMockShellExecutor); + clearInvocations(mMockPipScheduler); + Bundle extras = new Bundle(); extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, false); |