diff options
5 files changed, 211 insertions, 35 deletions
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index 0d742cc6e382..9a17693dd75e 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles.bar import android.app.ActivityManager +import android.content.ComponentName import android.content.Context import android.content.pm.ShortcutInfo import android.graphics.Insets @@ -24,6 +25,7 @@ import android.graphics.Rect import android.view.LayoutInflater import android.view.View import android.view.WindowManager +import android.widget.FrameLayout import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -110,9 +112,9 @@ class BubbleBarExpandedViewTest { regionSamplingProvider = TestRegionSamplingProvider() - bubbleExpandedView = (inflater.inflate( + bubbleExpandedView = inflater.inflate( R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ - ) as BubbleBarExpandedView) + ) as BubbleBarExpandedView bubbleExpandedView.initialize( expandedViewManager, positioner, @@ -124,11 +126,11 @@ class BubbleBarExpandedViewTest { regionSamplingProvider, ) - getInstrumentation().runOnMainSync(Runnable { + getInstrumentation().runOnMainSync { bubbleExpandedView.onAttachedToWindow() // Helper should be created once attached to window testableRegionSamplingHelper = regionSamplingProvider!!.helper - }) + } bubble = Bubble( "key", @@ -254,6 +256,93 @@ class BubbleBarExpandedViewTest { assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble) } + @Test + fun animateExpansion_waitsUntilTaskCreated() { + var animated = false + bubbleExpandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + bubbleExpandedView.onTaskCreated() + assertThat(animated).isTrue() + } + + @Test + fun animateExpansion_taskViewAttachedAndVisible() { + val inflater = LayoutInflater.from(context) + val expandedView = inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView + val taskView = FakeBubbleTaskViewFactory().create() + val taskViewParent = FrameLayout(context) + taskViewParent.addView(taskView.taskView) + taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) + assertThat(taskView.isVisible).isTrue() + + expandedView.initialize( + expandedViewManager, + positioner, + BubbleLogger(uiEventLoggerFake), + false /* isOverflow */, + taskView, + mainExecutor, + bgExecutor, + regionSamplingProvider, + ) + + // the task view should be removed from its parent + assertThat(taskView.taskView.parent).isNull() + + var animated = false + expandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + + // send an invisible signal to simulate the surface getting destroyed + expandedView.onContentVisibilityChanged(false) + + // send a visible signal to simulate a new surface getting created + expandedView.onContentVisibilityChanged(true) + + assertThat(taskView.taskView.parent).isEqualTo(expandedView) + assertThat(animated).isTrue() + } + + @Test + fun animateExpansion_taskViewAttachedAndInvisible() { + val inflater = LayoutInflater.from(context) + val expandedView = inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView + val taskView = FakeBubbleTaskViewFactory().create() + val taskViewParent = FrameLayout(context) + taskViewParent.addView(taskView.taskView) + taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) + assertThat(taskView.isVisible).isTrue() + taskView.listener.onTaskVisibilityChanged(666, false) + assertThat(taskView.isVisible).isFalse() + + expandedView.initialize( + expandedViewManager, + positioner, + BubbleLogger(uiEventLoggerFake), + false /* isOverflow */, + taskView, + mainExecutor, + bgExecutor, + regionSamplingProvider, + ) + + // the task view should be added to the expanded view + assertThat(taskView.taskView.parent).isEqualTo(expandedView) + + var animated = false + expandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + + // send a visible signal to simulate a new surface getting created + expandedView.onContentVisibilityChanged(true) + + assertThat(animated).isTrue() + } + private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView { return findViewByPredicate { it is BubbleBarMenuView } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt index 00d9a931cebe..6fca79876f12 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt @@ -253,6 +253,7 @@ class BubbleBarLayerViewTest { getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) + bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true) } waitForExpandedViewAnimation() @@ -276,6 +277,7 @@ class BubbleBarLayerViewTest { getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) + bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true) } waitForExpandedViewAnimation() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt index 68fc0c99abee..a517a2d550b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt @@ -42,6 +42,16 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { var componentName: ComponentName? = null private set + /** + * Whether the task view is visible and has a surface. Note that this does not check the alpha + * value of the task view. + * + * When this is `true` it is safe to start showing the task view. Otherwise if this is `false` + * callers should wait for it to be visible which will be indicated either by a call to + * [TaskView.Listener.onTaskCreated] or [TaskView.Listener.onTaskVisibilityChanged]. */ + var isVisible = false + private set + /** [TaskView.Listener] for users of this class. */ var delegateListener: TaskView.Listener? = null @@ -61,9 +71,12 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { this@BubbleTaskView.taskId = taskId isCreated = true componentName = name + // when the task is created it is visible + isVisible = true } override fun onTaskVisibilityChanged(taskId: Int, visible: Boolean) { + this@BubbleTaskView.isVisible = visible delegateListener?.onTaskVisibilityChanged(taskId, visible) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 74c3748dccaf..a313bd004a51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -161,6 +161,7 @@ public class BubbleBarAnimationHelper { updateExpandedView(); bbev.setAnimating(true); + bbev.setSurfaceZOrderedOnTop(true); bbev.setContentVisibility(false); bbev.setAlpha(0f); bbev.setTaskViewAlpha(0f); @@ -171,28 +172,29 @@ public class BubbleBarAnimationHelper { bbev.setAnimationMatrix(mExpandedViewContainerMatrix); - mExpandedViewAlphaAnimator.start(); - - PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); - PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) - .spring(AnimatableScaleMatrix.SCALE_X, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .spring(AnimatableScaleMatrix.SCALE_Y, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .addUpdateListener((target, values) -> { - bbev.setAnimationMatrix(mExpandedViewContainerMatrix); - }) - .withEndActions(() -> { - bbev.setAnimationMatrix(null); - updateExpandedView(); - bbev.setSurfaceZOrderedOnTop(false); - if (afterAnimation != null) { - afterAnimation.run(); - } - }) - .start(); + bbev.animateExpansionWhenTaskViewVisible(() -> { + mExpandedViewAlphaAnimator.start(); + + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .addUpdateListener((target, values) -> { + bbev.setAnimationMatrix(mExpandedViewContainerMatrix); + }) + .withEndActions(() -> { + bbev.setAnimationMatrix(null); + updateExpandedView(); + if (afterAnimation != null) { + afterAnimation.run(); + } + }) + .start(); + }); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 272dfecb0bf9..e1dabdcc7f9c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -131,6 +131,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView /** Current corner radius */ private float mCurrentCornerRadius = 0f; + /** A runnable to start the expansion animation as soon as the task view is made visible. */ + @Nullable + private Runnable mAnimateExpansion = null; + private TaskViewVisibilityState mVisibilityState = TaskViewVisibilityState.INVISIBLE; + /** * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha @@ -140,6 +145,18 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsAnimating; private boolean mIsDragging; + /** An enum value that tracks the visibility state of the task view */ + private enum TaskViewVisibilityState { + /** The task view is going away, and we're waiting for the surface to be destroyed. */ + PENDING_INVISIBLE, + /** The task view is invisible and does not have a surface. */ + INVISIBLE, + /** The task view is in the process of being added to a surface. */ + PENDING_VISIBLE, + /** The task view is visible and has a surface. */ + VISIBLE + } + public BubbleBarExpandedView(Context context) { this(context, null); } @@ -206,16 +223,27 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager, /* listener= */ this, bubbleTaskView, /* viewParent= */ this); + + // if the task view is already attached to a parent we need to remove it if (mTaskView.getParent() != null) { + // it's possible that the task view is visible, e.g. if we're unfolding, in which + // case removing it will trigger a visibility change. we have to wait for that + // signal before we can add it to this expanded view, otherwise the signal will be + // incorrect because the task view will have a surface. + // if the task view is not visible, then it has no surface and removing it will not + // trigger any visibility change signals. + if (bubbleTaskView.isVisible()) { + mVisibilityState = TaskViewVisibilityState.PENDING_INVISIBLE; + } ((ViewGroup) mTaskView.getParent()).removeView(mTaskView); } - FrameLayout.LayoutParams lp = - new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); - addView(mTaskView, lp); - mTaskView.setEnableSurfaceClipping(true); - mTaskView.setCornerRadius(mCurrentCornerRadius); - mTaskView.setVisibility(VISIBLE); - mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + + // if we're invisible it's safe to setup the task view and then await on the visibility + // signal. + if (mVisibilityState == TaskViewVisibilityState.INVISIBLE) { + mVisibilityState = TaskViewVisibilityState.PENDING_VISIBLE; + setupTaskView(); + } // Handle view needs to draw on top of task view. bringChildToFront(mHandleView); @@ -269,6 +297,16 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView }); } + private void setupTaskView() { + FrameLayout.LayoutParams lp = + new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); + addView(mTaskView, lp); + mTaskView.setEnableSurfaceClipping(true); + mTaskView.setCornerRadius(mCurrentCornerRadius); + mTaskView.setVisibility(VISIBLE); + mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + } + public BubbleBarHandleView getHandleView() { return mHandleView; } @@ -326,15 +364,28 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onTaskCreated() { - setContentVisibility(true); + if (mTaskView != null) { + mTaskView.setAlpha(0); + } if (mListener != null) { mListener.onTaskCreated(); } + // when the task is created we're visible + onTaskViewVisible(); } @Override public void onContentVisibilityChanged(boolean visible) { - setContentVisibility(visible); + if (mVisibilityState == TaskViewVisibilityState.PENDING_INVISIBLE && !visible) { + // the surface is now destroyed. set up the task view and wait for the visibility + // signal. + mVisibilityState = TaskViewVisibilityState.PENDING_VISIBLE; + setupTaskView(); + return; + } + if (visible) { + onTaskViewVisible(); + } } @Override @@ -350,6 +401,25 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mListener.onBackPressed(); } + void animateExpansionWhenTaskViewVisible(Runnable animateExpansion) { + if (mVisibilityState == TaskViewVisibilityState.VISIBLE) { + animateExpansion.run(); + } else { + mAnimateExpansion = animateExpansion; + } + } + + private void onTaskViewVisible() { + // if we're waiting to be visible, start the expansion animation if it's pending. + if (mVisibilityState == TaskViewVisibilityState.PENDING_VISIBLE) { + mVisibilityState = TaskViewVisibilityState.VISIBLE; + if (mAnimateExpansion != null) { + mAnimateExpansion.run(); + mAnimateExpansion = null; + } + } + } + /** * Set whether this view is currently being dragged. * |