From a6c8edd036843ff69a7a204a8162a3d9262d1bda Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 9 Nov 2020 16:20:33 -0800 Subject: [PATCH] Disable certain transaction when swiping pip to home When swiping pip to home is happening, following transactions become conflicts - onFixedRotationStarted / onFixedRotationFinished - onMovementBoundsChanged skip these transactions entirely if that's the case - Added also scaleAndRotate support in PipSurfaceTransactionHelper - Need to address the final glitch Video: http://rcll/aaaaaabFQoRHlzixHdtY/cOco1yoMXwFzSifFqQET9U Video: http://rcll/aaaaaabFQoRHlzixHdtY/f3IWG09DRO9aUNnzqSdPLU Bug: 171802909 Test: see video Change-Id: Ied0e9d485e3d9ecff4d7a9e4ea3687103b7b47c3 --- .../wm/shell/pip/PipTaskOrganizer.java | 43 +++++++++++-------- .../pip/PipSurfaceTransactionHelper.java | 35 ++++++++++++++- 2 files changed, 60 insertions(+), 18 deletions(-) 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 4b283e465f1df..519ce3611ce75 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 @@ -64,9 +64,9 @@ import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipUpdateThread; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import java.io.PrintWriter; import java.util.ArrayList; @@ -251,7 +251,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * If set to {@code true}, the entering animation will be skipped and we will wait for * {@link #onFixedRotationFinished(int)} callback to actually enter PiP. */ - private boolean mShouldDeferEnteringPip; + private boolean mWaitForFixedRotation; /** * If set to {@code true}, no entering PiP transition would be kicked off and most likely @@ -259,7 +259,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * auto PiP-able Activity to home. * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}. */ - private boolean mShouldIgnoreEnteringPipTransition; + private boolean mInSwipePipToHomeTransition; public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, @@ -304,7 +304,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } public boolean isDeferringEnterPipAnimation() { - return mState.isInPip() && mShouldDeferEnteringPip; + return mState.isInPip() && mWaitForFixedRotation; } /** @@ -336,9 +336,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams) { - mShouldIgnoreEnteringPipTransition = true; + mInSwipePipToHomeTransition = true; sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP); setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo); + // disable the conflicting transaction from fixed rotation, see also + // onFixedRotationStarted and onFixedRotationFinished + mWaitForFixedRotation = false; return mPipBoundsAlgorithm.getEntryDestinationBounds(); } @@ -348,7 +351,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { // do nothing if there is no startSwipePipToHome being called before - if (mShouldIgnoreEnteringPipTransition) { + if (mInSwipePipToHomeTransition) { mPipBoundsState.setBounds(destinationBounds); } } @@ -502,7 +505,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipMenuController.attach(leash); - if (mShouldIgnoreEnteringPipTransition) { + if (mInSwipePipToHomeTransition) { final Rect destinationBounds = mPipBoundsState.getBounds(); // animation is finished in the Launcher and here we directly apply the final touch. applyEnterPipSyncTransaction(destinationBounds, () -> { @@ -510,11 +513,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, finishResizeForMenu(destinationBounds); sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); }); - mShouldIgnoreEnteringPipTransition = false; + mInSwipePipToHomeTransition = false; return; } - if (mShouldDeferEnteringPip) { + if (mWaitForFixedRotation) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed final SurfaceControl.Transaction tx = @@ -666,8 +669,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.wtf(TAG, "Unrecognized token: " + token); return; } - mShouldDeferEnteringPip = false; - mShouldIgnoreEnteringPipTransition = false; + mWaitForFixedRotation = false; + mInSwipePipToHomeTransition = false; mPictureInPictureParams = null; mState = State.UNDEFINED; mPipUiEventLoggerLogger.setTaskInfo(null); @@ -694,17 +697,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onFixedRotationStarted(int displayId, int newRotation) { - mShouldDeferEnteringPip = true; + mWaitForFixedRotation = true; } @Override public void onFixedRotationFinished(int displayId) { - if (mShouldDeferEnteringPip && mState.isInPip()) { + if (mWaitForFixedRotation && mState.isInPip()) { final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); // schedule a regular animation to ensure all the callbacks are still being sent enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */); } - mShouldDeferEnteringPip = false; + mWaitForFixedRotation = false; } /** @@ -723,6 +726,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void onMovementBoundsChanged(Rect destinationBoundsOut, boolean fromRotation, boolean fromImeAdjustment, boolean fromShelfAdjustment, WindowContainerTransaction wct) { + // note that this can be called when swiping pip to home is happening. For instance, + // swiping an app in landscape to portrait home. skip this entirely if that's the case. + if (mInSwipePipToHomeTransition && fromRotation) { + if (DEBUG) Log.d(TAG, "skip onMovementBoundsChanged due to swipe-pip-to-home"); + return; + } final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() @@ -806,7 +815,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleAnimateResizePip(Rect toBounds, int duration, Consumer updateBoundsCallback) { - if (mShouldDeferEnteringPip) { + if (mWaitForFixedRotation) { Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred"); return; } @@ -820,7 +829,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration, Consumer updateBoundsCallback) { - if (mShouldDeferEnteringPip) { + if (mWaitForFixedRotation) { Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred"); return; } @@ -922,7 +931,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (mState.shouldBlockResizeRequest()) { return; } - if (mShouldDeferEnteringPip) { + if (mWaitForFixedRotation) { Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred"); return; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 22ffd2819c486..7f04f28198c04 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -19,6 +19,8 @@ package com.android.systemui.shared.pip; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; +import android.view.Choreographer; +import android.view.Surface; import android.view.SurfaceControl; /** @@ -50,8 +52,32 @@ public class PipSurfaceTransactionHelper { .setPosition(leash, left, top); } - public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds) { + public void scaleAndRotate(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds, Rect insets, + float degree, float positionX, float positionY) { + mTmpSourceRectF.set(sourceBounds); + mTmpDestinationRect.set(sourceBounds); + mTmpDestinationRect.inset(insets); + // Scale by the shortest edge and offset such that the top/left of the scaled inset + // source rect aligns with the top/left of the destination bounds + final float scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceBounds.width() + : (float) destinationBounds.height() / sourceBounds.height(); + mTmpTransform.setRotate(degree, 0, 0); + mTmpTransform.postScale(scale, scale); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9) + .setWindowCrop(leash, mTmpDestinationRect) + .setPosition(leash, positionX, positionY); + } + + public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds, + @Surface.Rotation int rotation) { resetScale(tx, leash, destinationBounds); + if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { + final int degree = (rotation == Surface.ROTATION_90) ? -90 : 90; + mTmpTransform.setRotate(degree, 0, 0); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9); + } resetCornerRadius(tx, leash); crop(tx, leash, destinationBounds); } @@ -71,4 +97,11 @@ public class PipSurfaceTransactionHelper { tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) .setPosition(leash, destinationBounds.left, destinationBounds.top); } + + /** @return {@link SurfaceControl.Transaction} instance with vsync-id */ + public static SurfaceControl.Transaction newSurfaceControlTransaction() { + final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); + tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); + return tx; + } }