From ea2130f4990a1a9e598b13c7102cf9434579cccf Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Mon, 13 Sep 2021 17:24:41 +0200 Subject: [PATCH 1/3] Add color layer to TaskDisplayArea for animations We want a solid color background to be used for task animations so that we show that instead of the wallpaper. We need a way to set the color when the animation start and clear it at the end of the animation. Test: atest FlickerTests:TaskTransitionTest Bug: 199507257 Change-Id: I47fe530ffe37b453bbec8e65e0a4199fba2f04a3 --- .../android/server/wm/TaskDisplayArea.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index d450dbffe4a1a..70464fc4e0ea8 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -42,6 +42,9 @@ import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static java.lang.Integer.MIN_VALUE; + +import android.annotation.ColorInt; import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration; @@ -79,6 +82,22 @@ final class TaskDisplayArea extends DisplayArea { DisplayContent mDisplayContent; + /** + * A color layer that serves as a solid color background to certain animations. + */ + private SurfaceControl mColorBackgroundLayer; + + /** + * This counter is used to make sure we don't prematurely clear the background color in the + * case that background color animations are interleaved. + * NOTE: The last set color will remain until the counter is reset to 0, which means that an + * animation background color may sometime remain after the animation has finished through an + * animation with a different background color if an animation starts after and ends before + * another where both set different background colors. However, this is not a concern as + * currently all task animation backgrounds are the same color. + */ + private int mColorLayerCounter = 0; + /** * A control placed at the appropriate level for transitions to occur. */ @@ -961,6 +980,11 @@ final class TaskDisplayArea extends DisplayArea { void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { if (getParent() != null) { super.onParentChanged(newParent, oldParent, () -> { + mColorBackgroundLayer = makeChildSurface(null) + .setColorLayer() + .setName("colorBackgroundLayer") + .setCallsite("TaskDisplayArea.onParentChanged") + .build(); mAppAnimationLayer = makeChildSurface(null) .setName("animationLayer") .setCallsite("TaskDisplayArea.onParentChanged") @@ -977,6 +1001,7 @@ final class TaskDisplayArea extends DisplayArea { .setName("splitScreenDividerAnchor") .setCallsite("TaskDisplayArea.onParentChanged") .build(); + getSyncTransaction() .show(mAppAnimationLayer) .show(mBoostedAppAnimationLayer) @@ -986,11 +1011,13 @@ final class TaskDisplayArea extends DisplayArea { } else { super.onParentChanged(newParent, oldParent); mWmService.mTransactionFactory.get() + .remove(mColorBackgroundLayer) .remove(mAppAnimationLayer) .remove(mBoostedAppAnimationLayer) .remove(mHomeAppAnimationLayer) .remove(mSplitScreenDividerAnchor) .apply(); + mColorBackgroundLayer = null; mAppAnimationLayer = null; mBoostedAppAnimationLayer = null; mHomeAppAnimationLayer = null; @@ -998,6 +1025,39 @@ final class TaskDisplayArea extends DisplayArea { } } + void setBackgroundColor(@ColorInt int color) { + if (mColorBackgroundLayer == null) { + return; + } + + float r = ((color >> 16) & 0xff) / 255.0f; + float g = ((color >> 8) & 0xff) / 255.0f; + float b = ((color >> 0) & 0xff) / 255.0f; + float a = ((color >> 24) & 0xff) / 255.0f; + + mColorLayerCounter++; + + getPendingTransaction().setLayer(mColorBackgroundLayer, MIN_VALUE) + .setColor(mColorBackgroundLayer, new float[]{r, g, b}) + .setAlpha(mColorBackgroundLayer, a) + .setWindowCrop(mColorBackgroundLayer, getSurfaceWidth(), getSurfaceHeight()) + .setPosition(mColorBackgroundLayer, 0, 0) + .show(mColorBackgroundLayer); + + scheduleAnimation(); + } + + void clearBackgroundColor() { + mColorLayerCounter--; + + // Only clear the color layer if we have received the same amounts of clear as set + // requests. + if (mColorLayerCounter == 0) { + getPendingTransaction().hide(mColorBackgroundLayer); + scheduleAnimation(); + } + } + @Override void migrateToNewSurfaceControl(SurfaceControl.Transaction t) { super.migrateToNewSurfaceControl(t); @@ -1006,6 +1066,7 @@ final class TaskDisplayArea extends DisplayArea { } // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces. + t.reparent(mColorBackgroundLayer, mSurfaceControl); t.reparent(mAppAnimationLayer, mSurfaceControl); t.reparent(mBoostedAppAnimationLayer, mSurfaceControl); t.reparent(mHomeAppAnimationLayer, mSurfaceControl); From cab1bbffa59ebbd7b2039f8775047c62f0c68e5f Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Fri, 17 Sep 2021 17:07:04 +0000 Subject: [PATCH 2/3] Show color layer in task animations when requested Additionally wrap AnimationAdapter to inject custom onCancelledCallback which should run when the animation is cancelled. We can't rely on the onAnimationFinished callback since that doesn't run when the animation adapter is canceled by the surface animator and it is still running (i.e. restarted due to a new animation being requested before the old one finished). Since the finishedCallbacks are not always called when an animation adapter is cancelled this cancel callback serves as a way to inject and run custom clean up code for the animation adapter, which in our case is used to clear the background color of an animation. Test: atest FlickerTests:TaskTransitionTest Bug: 199507257 Change-Id: Ic603b2711c19299f01271131062765255451310a --- .../android/server/wm/SurfaceAnimator.java | 50 ++++++++++++++----- .../android/server/wm/TaskDisplayArea.java | 5 ++ .../android/server/wm/WindowContainer.java | 41 +++++++++++++-- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 3c6c23b08091f..c7bf8ecfe949c 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -59,11 +59,30 @@ class SurfaceAnimator { @VisibleForTesting final Animatable mAnimatable; private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback; + + /** + * Static callback to run on all animations started through this SurfaceAnimator + * when an animation on a Surface is finished or cancelled without restart. + */ @VisibleForTesting @Nullable final OnAnimationFinishedCallback mStaticAnimationFinishedCallback; + + /** + * Callback unique to each animation (i.e. AnimationAdapter). To be run when an animation on a + * Surface is finished or cancelled without restart. + */ @Nullable - private OnAnimationFinishedCallback mAnimationFinishedCallback; + private OnAnimationFinishedCallback mSurfaceAnimationFinishedCallback; + + /** + * The callback is triggered after the SurfaceAnimator sends a cancel call to the underlying + * AnimationAdapter. + * NOTE: Must be called wherever we call onAnimationCancelled on mAnimation. + */ + @Nullable + private Runnable mAnimationCancelledCallback; + private boolean mAnimationStartDelayed; /** @@ -100,7 +119,7 @@ class SurfaceAnimator { return; } final OnAnimationFinishedCallback animationFinishCallback = - mAnimationFinishedCallback; + mSurfaceAnimationFinishedCallback; reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */); if (staticAnimationFinishedCallback != null) { staticAnimationFinishedCallback.onAnimationFinished(type, anim); @@ -130,15 +149,19 @@ class SurfaceAnimator { * This is important as it will start with the leash hidden or visible before * handing it to the component that is responsible to run the animation. * @param animationFinishedCallback The callback being triggered when the animation finishes. + * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a + * cancel call to the underlying AnimationAdapter. */ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type, @Nullable OnAnimationFinishedCallback animationFinishedCallback, + @Nullable Runnable animationCancelledCallback, @Nullable SurfaceFreezer freezer) { cancelAnimation(t, true /* restarting */, true /* forwardCancel */); mAnimation = anim; mAnimationType = type; - mAnimationFinishedCallback = animationFinishedCallback; + mSurfaceAnimationFinishedCallback = animationFinishedCallback; + mAnimationCancelledCallback = animationCancelledCallback; final SurfaceControl surface = mAnimatable.getSurfaceControl(); if (surface == null) { Slog.w(TAG, "Unable to start animation, surface is null or no children."); @@ -160,15 +183,10 @@ class SurfaceAnimator { mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback); } - void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, - @AnimationType int type, - @Nullable OnAnimationFinishedCallback animationFinishedCallback) { - startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */); - } - void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type) { - startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */); + startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */, + null /* animationCancelledCallback */, null /* freezer */); } /** @@ -278,7 +296,8 @@ class SurfaceAnimator { mLeash = from.mLeash; mAnimation = from.mAnimation; mAnimationType = from.mAnimationType; - mAnimationFinishedCallback = from.mAnimationFinishedCallback; + mSurfaceAnimationFinishedCallback = from.mSurfaceAnimationFinishedCallback; + mAnimationCancelledCallback = from.mAnimationCancelledCallback; // Cancel source animation, but don't let animation runner cancel the animation. from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */); @@ -306,11 +325,16 @@ class SurfaceAnimator { final SurfaceControl leash = mLeash; final AnimationAdapter animation = mAnimation; final @AnimationType int animationType = mAnimationType; - final OnAnimationFinishedCallback animationFinishedCallback = mAnimationFinishedCallback; + final OnAnimationFinishedCallback animationFinishedCallback = + mSurfaceAnimationFinishedCallback; + final Runnable animationCancelledCallback = mAnimationCancelledCallback; reset(t, false); if (animation != null) { if (!mAnimationStartDelayed && forwardCancel) { animation.onAnimationCancelled(leash); + if (animationCancelledCallback != null) { + animationCancelledCallback.run(); + } } if (!restarting) { if (mStaticAnimationFinishedCallback != null) { @@ -335,7 +359,7 @@ class SurfaceAnimator { private void reset(Transaction t, boolean destroyLeash) { mService.mAnimationTransferMap.remove(mAnimation); mAnimation = null; - mAnimationFinishedCallback = null; + mSurfaceAnimationFinishedCallback = null; mAnimationType = ANIMATION_TYPE_NONE; if (mLeash == null) { return; diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 70464fc4e0ea8..ee4c629189dc6 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -2209,6 +2209,11 @@ final class TaskDisplayArea extends DisplayArea { mPreferredTopFocusableRootTask = null; } + @Override + TaskDisplayArea getTaskDisplayArea() { + return this; + } + @Override boolean isTaskDisplayArea() { return true; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b1c7e196b70c2..d9a8883e299ed 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -850,6 +850,12 @@ class WindowContainer extends ConfigurationContainer< return parent != null ? parent.getRootDisplayArea() : null; } + @Nullable + TaskDisplayArea getTaskDisplayArea() { + WindowContainer parent = getParent(); + return parent != null ? parent.getTaskDisplayArea() : null; + } + boolean isAttached() { return getDisplayArea() != null; } @@ -2495,10 +2501,13 @@ class WindowContainer extends ConfigurationContainer< * some point but the meaning is too weird to work for all containers. * @param type The type of animation defined as {@link AnimationType}. * @param animationFinishedCallback The callback being triggered when the animation finishes. + * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a + * cancel call to the underlying AnimationAdapter. */ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type, - @Nullable OnAnimationFinishedCallback animationFinishedCallback) { + @Nullable OnAnimationFinishedCallback animationFinishedCallback, + @Nullable Runnable animationCancelledCallback) { if (DEBUG_ANIM) { Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim); } @@ -2506,7 +2515,14 @@ class WindowContainer extends ConfigurationContainer< // TODO: This should use isVisible() but because isVisible has a really weird meaning at // the moment this doesn't work for all animatable window containers. mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback, - mSurfaceFreezer); + animationCancelledCallback, mSurfaceFreezer); + } + + void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, + @AnimationType int type, + @Nullable OnAnimationFinishedCallback animationFinishedCallback) { + startAnimation(t, anim, hidden, type, animationFinishedCallback, + null /* adapterAnimationCancelledCallback */); } void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @@ -2714,8 +2730,27 @@ class WindowContainer extends ConfigurationContainer< if (sources != null) { mSurfaceAnimationSources.addAll(sources); } + + TaskDisplayArea taskDisplayArea = getTaskDisplayArea(); + int backgroundColor = adapter.getBackgroundColor(); + + boolean shouldSetBackgroundColor = taskDisplayArea != null && backgroundColor != 0; + + if (shouldSetBackgroundColor) { + taskDisplayArea.setBackgroundColor(backgroundColor); + } + + Runnable clearColorBackground = () -> { + if (shouldSetBackgroundColor) { + taskDisplayArea.clearBackgroundColor(); + } + }; + startAnimation(getPendingTransaction(), adapter, !isVisible(), - ANIMATION_TYPE_APP_TRANSITION); + ANIMATION_TYPE_APP_TRANSITION, + (type, anim) -> clearColorBackground.run(), + clearColorBackground); + if (adapter.getShowWallpaper()) { getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } From 49fa185147592e971749d62e1c7cf3db2abf1afb Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Wed, 15 Sep 2021 18:43:51 +0000 Subject: [PATCH 3/3] Update task transition animations Following designs from go/android-new-task-motion Test: atest FlickerTests:TaskTransitionTest Bug: 199507257 Change-Id: I037517d930e3425dc22ebab6f5df1968ebba5483 --- .../cross_profile_apps_thumbnail_enter.xml | 48 ++-------------- core/res/res/anim-ldrtl/task_close_enter.xml | 51 +++-------------- core/res/res/anim-ldrtl/task_close_exit.xml | 35 ++---------- core/res/res/anim-ldrtl/task_open_enter.xml | 53 +++--------------- .../task_open_enter_cross_profile_apps.xml | 53 +++--------------- core/res/res/anim-ldrtl/task_open_exit.xml | 35 ++---------- .../cross_profile_apps_thumbnail_enter.xml | 46 ++-------------- core/res/res/anim/task_close_enter.xml | 51 +++-------------- core/res/res/anim/task_close_exit.xml | 35 ++---------- core/res/res/anim/task_open_enter.xml | 55 +++---------------- .../task_open_enter_cross_profile_apps.xml | 51 +++-------------- core/res/res/anim/task_open_exit.xml | 35 ++---------- core/res/res/color/overview_background.xml | 19 +++++++ .../res/color/overview_background_dark.xml | 19 +++++++ core/res/res/values-night/colors.xml | 2 + 15 files changed, 121 insertions(+), 467 deletions(-) create mode 100644 core/res/res/color/overview_background.xml create mode 100644 core/res/res/color/overview_background_dark.xml diff --git a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml index 5add19bba51b5..941df96cb9417 100644 --- a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml +++ b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml @@ -18,19 +18,10 @@ --> - - + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> - - + android:shareInterpolator="false" + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> \ No newline at end of file diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml index 71a44ae7d2fcf..8c0aaa8174300 100644 --- a/core/res/res/anim-ldrtl/task_close_exit.xml +++ b/core/res/res/anim-ldrtl/task_close_exit.xml @@ -15,19 +15,9 @@ --> - - + android:shareInterpolator="false" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> - - + android:shareInterpolator="false" + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - - \ No newline at end of file + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> + diff --git a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml index 5fccd6df14a57..69631f6525e30 100644 --- a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml +++ b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml @@ -16,20 +16,10 @@ --> - - + android:shareInterpolator="false" + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> @@ -75,4 +38,4 @@ android:toAlpha="1.0" android:startOffset="717" android:duration="200"/> - \ No newline at end of file + diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml index 025e1bdc05c95..f455334ced788 100644 --- a/core/res/res/anim-ldrtl/task_open_exit.xml +++ b/core/res/res/anim-ldrtl/task_open_exit.xml @@ -15,19 +15,9 @@ --> - - + android:shareInterpolator="false" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> - - + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> - - + android:shareInterpolator="false" + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> \ No newline at end of file diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml index afc3256cb6173..4b1e89ca0e3a7 100644 --- a/core/res/res/anim/task_close_exit.xml +++ b/core/res/res/anim/task_close_exit.xml @@ -17,19 +17,9 @@ --> - - + android:shareInterpolator="false" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> - - + + - - + android:shareInterpolator="false" + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> \ No newline at end of file diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml index 702f7ba162aae..dc316ff20f940 100644 --- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml +++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml @@ -18,20 +18,10 @@ --> - - + android:shareInterpolator="false" + android:zAdjustment="top" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml index 691317d2e6e01..f8ab65517d5a2 100644 --- a/core/res/res/anim/task_open_exit.xml +++ b/core/res/res/anim/task_open_exit.xml @@ -17,19 +17,9 @@ --> - - + android:shareInterpolator="false" + android:hasRoundedCorners="true" + android:background="@color/overview_background"> - - + android:interpolator="@interpolator/fast_out_extra_slow_in" + android:startOffset="0" + android:duration="500"/> + + + + \ No newline at end of file diff --git a/core/res/res/color/overview_background_dark.xml b/core/res/res/color/overview_background_dark.xml new file mode 100644 index 0000000000000..84f4fdff4e1ad --- /dev/null +++ b/core/res/res/color/overview_background_dark.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 2e4578c394309..783fabe20a6d7 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -33,4 +33,6 @@ #5DBA80 #8AB4F8 + + @color/overview_background_dark