From de204a5721ad1b5d6e3e456c625f2981a9082cc8 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Thu, 18 Mar 2021 11:13:12 -0700 Subject: [PATCH] Apply the whole auto pip transaction in recents animation Take the full transaction details passed from Launcher, including position / windowCrop / scale / roundRadius and apply them in RecentsAnimationController.TaskAnimationAdapter#onCleanup to make sure the final state of autoEnterPip transition can be carried over. Note: there are still several frames off when entering PiP from landscape with autoEnterPip being enabled. Bug: 179720719 Test: manually using the ApiDemos app Change-Id: Ibfff75e09943960cfcd816d6c52a80d7a8af8fe8 --- .../view/IRecentsAnimationController.aidl | 8 +- .../PictureInPictureSurfaceTransaction.aidl | 19 +++ .../PictureInPictureSurfaceTransaction.java | 140 ++++++++++++++++++ .../pip/PipSurfaceTransactionHelper.java | 53 ++++--- .../RecentsAnimationControllerCompat.java | 13 +- .../shared/system/RemoteTransitionCompat.java | 5 +- .../server/wm/RecentsAnimationController.java | 54 +++---- 7 files changed, 230 insertions(+), 62 deletions(-) create mode 100644 core/java/android/window/PictureInPictureSurfaceTransaction.aidl create mode 100644 core/java/android/window/PictureInPictureSurfaceTransaction.java diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 10721ad305250..1706799758e13 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.view.IRemoteAnimationFinishedCallback; import android.graphics.GraphicBuffer; import android.graphics.Rect; +import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; /** @@ -43,11 +44,10 @@ interface IRecentsAnimationController { * accordingly. This should be called before `finish` * @param taskId for which the leash should be updated * @param destinationBounds bounds of the final PiP window - * @param windowCrop bounds to crop as part of final transform. - * @param float9 An array of 9 floats to be used as matrix transform. + * @param finishTransaction leash operations for the final transform. */ - void setFinishTaskBounds(int taskId, in Rect destinationBounds, in Rect windowCrop, - in float[] float9); + void setFinishTaskBounds(int taskId, in Rect destinationBounds, + in PictureInPictureSurfaceTransaction finishTransaction); /** * Notifies to the system that the animation into Recents should end, and all leashes associated diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.aidl b/core/java/android/window/PictureInPictureSurfaceTransaction.aidl new file mode 100644 index 0000000000000..fe460bdab89bb --- /dev/null +++ b/core/java/android/window/PictureInPictureSurfaceTransaction.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.window; + +parcelable PictureInPictureSurfaceTransaction; diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java new file mode 100644 index 0000000000000..bde91d90b0f99 --- /dev/null +++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java @@ -0,0 +1,140 @@ +/* + * 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 android.window; + +import android.annotation.Nullable; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Represents a set of {@link android.view.SurfaceControl.Transaction} operations used to + * operate on the {@link android.view.SurfaceControl} for picture-in-picture. + * + * @hide + */ +public final class PictureInPictureSurfaceTransaction implements Parcelable { + + public final float mPositionX; + public final float mPositionY; + + public final float mScaleX; + public final float mScaleY; + + public final float mRotation; + public final float mCornerRadius; + + private final Rect mWindowCrop = new Rect(); + + public PictureInPictureSurfaceTransaction(Parcel in) { + mPositionX = in.readFloat(); + mPositionY = in.readFloat(); + mScaleX = in.readFloat(); + mScaleY = in.readFloat(); + mRotation = in.readFloat(); + mCornerRadius = in.readFloat(); + mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR))); + } + + public PictureInPictureSurfaceTransaction(float positionX, float positionY, + float scaleX, float scaleY, + float rotation, float cornerRadius, + @Nullable Rect windowCrop) { + mPositionX = positionX; + mPositionY = positionY; + mScaleX = scaleX; + mScaleY = scaleY; + mRotation = rotation; + mCornerRadius = cornerRadius; + if (windowCrop != null) { + mWindowCrop.set(windowCrop); + } + } + + public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) { + this(other.mPositionX, other.mPositionY, + other.mScaleX, other.mScaleY, + other.mRotation, other.mCornerRadius, + other.mWindowCrop); + } + + public Rect getWindowCrop() { + return new Rect(mWindowCrop); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PictureInPictureSurfaceTransaction)) return false; + PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o; + return Objects.equals(mPositionX, that.mPositionX) + && Objects.equals(mPositionY, that.mPositionY) + && Objects.equals(mScaleX, that.mScaleX) + && Objects.equals(mScaleY, that.mScaleY) + && Objects.equals(mRotation, that.mRotation) + && Objects.equals(mCornerRadius, that.mCornerRadius) + && Objects.equals(mWindowCrop, that.mWindowCrop); + } + + @Override + public int hashCode() { + return Objects.hash(mPositionX, mPositionY, mScaleX, mScaleY, + mRotation, mCornerRadius, mWindowCrop); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeFloat(mPositionX); + out.writeFloat(mPositionY); + out.writeFloat(mScaleX); + out.writeFloat(mScaleY); + out.writeFloat(mRotation); + out.writeFloat(mCornerRadius); + out.writeTypedObject(mWindowCrop, 0 /* flags */); + } + + @Override + public String toString() { + return "PictureInPictureSurfaceTransaction(" + + " posX=" + mPositionX + + " posY=" + mPositionY + + " scaleX=" + mScaleX + + " scaleY=" + mScaleY + + " rotation=" + mRotation + + " cornerRadius=" + mCornerRadius + + " crop=" + mWindowCrop + + ")"; + } + + public static final @android.annotation.NonNull Creator + CREATOR = + new Creator() { + public PictureInPictureSurfaceTransaction createFromParcel(Parcel in) { + return new PictureInPictureSurfaceTransaction(in); + } + public PictureInPictureSurfaceTransaction[] newArray(int size) { + return new PictureInPictureSurfaceTransaction[size]; + } + }; +} 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 f50c3c925a1ce..7cd7c9ed7de5c 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 @@ -16,11 +16,15 @@ package com.android.systemui.shared.pip; +import static android.graphics.Matrix.MSCALE_X; +import static android.graphics.Matrix.MSCALE_Y; + import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.view.Choreographer; import android.view.SurfaceControl; +import android.window.PictureInPictureSurfaceTransaction; /** * TODO(b/171721389): unify this class with @@ -28,22 +32,32 @@ import android.view.SurfaceControl; * source of truth on enabling/disabling and the actual value of corner radius. */ public class PipSurfaceTransactionHelper { + /** corner radius is currently disabled. */ + private final float mCornerRadius = 0f; + private final Matrix mTmpTransform = new Matrix(); private final float[] mTmpFloat9 = new float[9]; private final RectF mTmpSourceRectF = new RectF(); private final RectF mTmpDestinationRectF = new RectF(); private final Rect mTmpDestinationRect = new Rect(); - public void scale(SurfaceControl.Transaction tx, SurfaceControl leash, + public PictureInPictureSurfaceTransaction scale( + SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds) { mTmpSourceRectF.set(sourceBounds); mTmpDestinationRectF.set(destinationBounds); mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top); + .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top) + .setCornerRadius(leash, mCornerRadius); + return new PictureInPictureSurfaceTransaction( + mTmpDestinationRectF.left, mTmpDestinationRectF.top, + mTmpFloat9[MSCALE_X], mTmpFloat9[MSCALE_Y], + 0 /* rotation*/, mCornerRadius, sourceBounds); } - public void scale(SurfaceControl.Transaction tx, SurfaceControl leash, + public PictureInPictureSurfaceTransaction scale( + SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degree, float positionX, float positionY) { mTmpSourceRectF.set(sourceBounds); @@ -51,10 +65,16 @@ public class PipSurfaceTransactionHelper { mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); mTmpTransform.postRotate(degree, 0, 0); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setPosition(leash, positionX, positionY); + .setPosition(leash, positionX, positionY) + .setCornerRadius(leash, mCornerRadius); + return new PictureInPictureSurfaceTransaction( + positionX, positionY, + mTmpFloat9[MSCALE_X], mTmpFloat9[MSCALE_Y], + degree, mCornerRadius, sourceBounds); } - public void scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash, + public PictureInPictureSurfaceTransaction scaleAndCrop( + SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets) { mTmpSourceRectF.set(sourceBounds); mTmpDestinationRect.set(sourceBounds); @@ -69,10 +89,14 @@ public class PipSurfaceTransactionHelper { mTmpTransform.setScale(scale, scale); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setWindowCrop(leash, mTmpDestinationRect) - .setPosition(leash, left, top); + .setPosition(leash, left, top) + .setCornerRadius(leash, mCornerRadius); + return new PictureInPictureSurfaceTransaction( + left, top, scale, scale, 0 /* rotation */, mCornerRadius, mTmpDestinationRect); } - public void scaleAndRotate(SurfaceControl.Transaction tx, SurfaceControl leash, + public PictureInPictureSurfaceTransaction scaleAndRotate( + SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets, float degree, float positionX, float positionY) { mTmpSourceRectF.set(sourceBounds); @@ -87,17 +111,10 @@ public class PipSurfaceTransactionHelper { mTmpTransform.postScale(scale, scale); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setWindowCrop(leash, mTmpDestinationRect) - .setPosition(leash, positionX, positionY); - } - - public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) { - tx.setCornerRadius(leash, 0); - } - - public void crop(SurfaceControl.Transaction tx, SurfaceControl leash, - Rect destinationBounds) { - tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) - .setPosition(leash, destinationBounds.left, destinationBounds.top); + .setPosition(leash, positionX, positionY) + .setCornerRadius(leash, mCornerRadius); + return new PictureInPictureSurfaceTransaction( + positionX, positionY, scale, scale, degree, mCornerRadius, mTmpDestinationRect); } /** @return {@link SurfaceControl.Transaction} instance with vsync-id */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index bf0d29a59beaf..dde20c13ea4f3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -20,6 +20,7 @@ import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; import android.view.IRecentsAnimationController; +import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -76,14 +77,12 @@ public class RecentsAnimationControllerCompat { * accordingly. This should be called before `finish` * @param taskId Task id of the Activity in PiP mode. * @param destinationBounds Bounds of the PiP window on home. - * @param windowCrop bounds to crop as part of final transform. - * @param float9 An array of 9 floats to be used as matrix transform. + * @param finishTransaction leash operations for the final transform. */ - public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop, - float[] float9) { + public void setFinishTaskBounds(int taskId, Rect destinationBounds, + PictureInPictureSurfaceTransaction finishTransaction) { try { - mAnimationController.setFinishTaskBounds(taskId, destinationBounds, windowCrop, - float9); + mAnimationController.setFinishTaskBounds(taskId, destinationBounds, finishTransaction); } catch (RemoteException e) { Log.d(TAG, "Failed to set finish task bounds", e); } @@ -152,4 +151,4 @@ public class RecentsAnimationControllerCompat { Log.e(TAG, "Failed to detach the navigation bar from app", e); } } -} \ No newline at end of file +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 06155bc7100cd..0f386b192c9c3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -32,6 +32,7 @@ import android.view.IRecentsAnimationController; import android.view.SurfaceControl; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; +import android.window.PictureInPictureSurfaceTransaction; import android.window.TransitionFilter; import android.window.TransitionInfo; import android.window.WindowContainerToken; @@ -138,9 +139,9 @@ public class RemoteTransitionCompat implements Parcelable { } @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds, - Rect windowCrop, float[] float9) { + PictureInPictureSurfaceTransaction finishTransaction) { if (mWrapped != null) { - mWrapped.setFinishTaskBounds(taskId, destinationBounds, windowCrop, float9); + mWrapped.setFinishTaskBounds(taskId, destinationBounds, finishTransaction); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index bd84854702198..87854c251d260 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -20,10 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.graphics.Matrix.MSCALE_X; -import static android.graphics.Matrix.MSCALE_Y; -import static android.graphics.Matrix.MSKEW_X; -import static android.graphics.Matrix.MSKEW_Y; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; @@ -40,6 +36,7 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.WindowConfiguration; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -59,6 +56,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; +import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; @@ -233,8 +231,8 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop, - float[] float9) { + public void setFinishTaskBounds(int taskId, Rect destinationBounds, + PictureInPictureSurfaceTransaction finishTransaction) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds); final long token = Binder.clearCallingIdentity(); @@ -244,8 +242,7 @@ public class RecentsAnimationController implements DeathRecipient { final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); if (taskAdapter.mTask.mTaskId == taskId) { taskAdapter.mFinishBounds.set(destinationBounds); - taskAdapter.mFinishWindowCrop.set(windowCrop); - taskAdapter.mFinishTransform = float9; + taskAdapter.mFinishTransaction = finishTransaction; break; } } @@ -1098,8 +1095,7 @@ public class RecentsAnimationController implements DeathRecipient { // The bounds of the target when animation is finished private final Rect mFinishBounds = new Rect(); // Bounds and transform for the final transaction. - private final Rect mFinishWindowCrop = new Rect(); - private float[] mFinishTransform; + private PictureInPictureSurfaceTransaction mFinishTransaction; TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { mTask = task; @@ -1138,29 +1134,25 @@ public class RecentsAnimationController implements DeathRecipient { if (!mFinishBounds.isEmpty()) { final SurfaceControl taskSurface = mTask.mSurfaceControl; final Transaction pendingTransaction = mTask.getPendingTransaction(); - if (mFinishTransform != null) { - pendingTransaction - .setMatrix(taskSurface, - mFinishTransform[MSCALE_X], mFinishTransform[MSKEW_Y], - mFinishTransform[MSKEW_X], mFinishTransform[MSCALE_Y]); - } - float left = mFinishBounds.left; - float top = mFinishBounds.top; - if (!mFinishWindowCrop.isEmpty()) { - pendingTransaction.setWindowCrop(taskSurface, mFinishWindowCrop); - if (mFinishTransform != null) { - // adjust the position for insets. - left -= mFinishWindowCrop.left * mFinishTransform[MSCALE_X]; - top -= mFinishWindowCrop.top * mFinishTransform[MSCALE_Y]; + if (mFinishTransaction != null) { + final Matrix matrix = new Matrix(); + matrix.setScale(mFinishTransaction.mScaleX, mFinishTransaction.mScaleY); + if (mFinishTransaction.mRotation != 0) { + matrix.postRotate(mFinishTransaction.mRotation); } + pendingTransaction.setMatrix(taskSurface, matrix, new float[9]) + .setPosition(taskSurface, + mFinishTransaction.mPositionX, mFinishTransaction.mPositionY) + .setWindowCrop(taskSurface, mFinishTransaction.getWindowCrop()) + .setCornerRadius(taskSurface, mFinishTransaction.mCornerRadius); + mTask.mLastRecentsAnimationBounds.set(mFinishBounds); + mFinishTransaction = null; + } else { + pendingTransaction + .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top) + .setWindowCrop(taskSurface, mFinishBounds); } - pendingTransaction - .setPosition(taskSurface, left, top) - .apply(); - mTask.mLastRecentsAnimationBounds.set(mFinishBounds); - // reset the variables - mFinishTransform = null; - mFinishWindowCrop.setEmpty(); + pendingTransaction.apply(); mFinishBounds.setEmpty(); } else if (!mTask.isAttached()) { // Apply the task's pending transaction in case it is detached and its transaction