diff options
3 files changed, 89 insertions, 43 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 5b0239f6d659..02af2d06a1dd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -563,7 +563,7 @@ public class ExpandedAnimationController ? p.x - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR : p.x + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; animationForChild(child) - .translationX(fromX, p.y) + .translationX(fromX, p.x) .start(); } else { float fromY = p.y - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; @@ -634,4 +634,9 @@ public class ExpandedAnimationController .start(); } } + + /** Returns true if we're in the middle of a collapse or expand animation. */ + boolean isAnimating() { + return mAnimatingCollapse || mAnimatingExpand; + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java index c1ff260836b8..60f1d271c3af 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java @@ -16,52 +16,51 @@ package com.android.wm.shell.bubbles.animation; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.annotation.SuppressLint; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.PointF; import android.graphics.Rect; -import android.testing.AndroidTestingRunner; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleStackView; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase { - private int mDisplayWidth = 500; - private int mDisplayHeight = 1000; - - private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class); + private final Semaphore mBubbleRemovedSemaphore = new Semaphore(0); + private final Runnable mOnBubbleAnimatedOutAction = mBubbleRemovedSemaphore::release; ExpandedAnimationController mExpandedController; private int mStackOffset; private PointF mExpansionPoint; private BubblePositioner mPositioner; - private BubbleStackView.StackViewState mStackViewState = new BubbleStackView.StackViewState(); + private final BubbleStackView.StackViewState mStackViewState = + new BubbleStackView.StackViewState(); - @SuppressLint("VisibleForTests") @Before public void setUp() throws Exception { super.setUp(); @@ -70,15 +69,13 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC getContext().getSystemService(WindowManager.class)); mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT, Insets.of(0, 0, 0, 0), - new Rect(0, 0, mDisplayWidth, mDisplayHeight)); + new Rect(0, 0, 500, 1000)); BubbleStackView stackView = mock(BubbleStackView.class); - when(stackView.getState()).thenReturn(getStackViewState()); mExpandedController = new ExpandedAnimationController(mPositioner, mOnBubbleAnimatedOutAction, stackView); - spyOn(mExpandedController); addOneMoreThanBubbleLimitBubbles(); mLayout.setActiveController(mExpandedController); @@ -86,9 +83,18 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC Resources res = mLayout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mExpansionPoint = new PointF(100, 100); + + getStackViewState(); + when(stackView.getState()).thenAnswer(i -> getStackViewState()); + waitForMainThread(); } - public BubbleStackView.StackViewState getStackViewState() { + @After + public void tearDown() { + waitForMainThread(); + } + + private BubbleStackView.StackViewState getStackViewState() { mStackViewState.numberOfBubbles = mLayout.getChildCount(); mStackViewState.selectedIndex = 0; mStackViewState.onLeft = mPositioner.isStackOnLeft(mExpansionPoint); @@ -96,68 +102,71 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC } @Test - @Ignore - public void testExpansionAndCollapse() throws InterruptedException { - Runnable afterExpand = mock(Runnable.class); - mExpandedController.expandFromStack(afterExpand); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); - + public void testExpansionAndCollapse() throws Exception { + expand(); testBubblesInCorrectExpandedPositions(); - verify(afterExpand).run(); + waitForMainThread(); - Runnable afterCollapse = mock(Runnable.class); + final Semaphore semaphore = new Semaphore(0); + Runnable afterCollapse = semaphore::release; mExpandedController.collapseBackToStack(mExpansionPoint, false, afterCollapse); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); - - testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1); - verify(afterExpand).run(); + assertThat(semaphore.tryAcquire(1, 2, TimeUnit.SECONDS)).isTrue(); + waitForAnimation(); + testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y); } @Test - @Ignore - public void testOnChildAdded() throws InterruptedException { + public void testOnChildAdded() throws Exception { expand(); + waitForMainThread(); // Add another new view and wait for its animation. final View newView = new FrameLayout(getContext()); mLayout.addView(newView, 0); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + waitForAnimation(); testBubblesInCorrectExpandedPositions(); } @Test - @Ignore - public void testOnChildRemoved() throws InterruptedException { + public void testOnChildRemoved() throws Exception { expand(); + waitForMainThread(); - // Remove some views and see if the remaining child views still pass the expansion test. + // Remove some views and verify the remaining child views still pass the expansion test. mLayout.removeView(mViews.get(0)); mLayout.removeView(mViews.get(3)); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + + // Removing a view will invoke onBubbleAnimatedOutAction. Block until it gets called twice. + assertThat(mBubbleRemovedSemaphore.tryAcquire(2, 2, TimeUnit.SECONDS)).isTrue(); + + waitForAnimation(); testBubblesInCorrectExpandedPositions(); } @Test - public void testDragBubbleOutDoesntNPE() throws InterruptedException { + public void testDragBubbleOutDoesntNPE() { mExpandedController.onGestureFinished(); mExpandedController.dragBubbleOut(mViews.get(0), 1, 1); } /** Expand the stack and wait for animations to finish. */ private void expand() throws InterruptedException { - mExpandedController.expandFromStack(mock(Runnable.class)); - waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + final Semaphore semaphore = new Semaphore(0); + Runnable afterExpand = semaphore::release; + + mExpandedController.expandFromStack(afterExpand); + assertThat(semaphore.tryAcquire(1, TimeUnit.SECONDS)).isTrue(); } /** Check that children are in the correct positions for being stacked. */ - private void testStackedAtPosition(float x, float y, int offsetMultiplier) { + private void testStackedAtPosition(float x, float y) { // Make sure the rest of the stack moved again, including the first bubble not moving, and // is stacked to the right now that we're on the right side of the screen. for (int i = 0; i < mLayout.getChildCount(); i++) { - assertEquals(x + i * offsetMultiplier * mStackOffset, - mLayout.getChildAt(i).getTranslationX(), 2f); - assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f); + assertEquals(x, mLayout.getChildAt(i).getTranslationX(), 2f); + assertEquals(y + Math.min(i, 1) * mStackOffset, mLayout.getChildAt(i).getTranslationY(), + 2f); assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); } } @@ -175,4 +184,22 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC mLayout.getChildAt(i).getTranslationY(), 2f); } } + + private void waitForAnimation() throws Exception { + final Semaphore semaphore = new Semaphore(0); + boolean[] animating = new boolean[]{ true }; + for (int i = 0; i < 4; i++) { + if (animating[0]) { + mMainThreadHandler.post(() -> { + if (!mExpandedController.isAnimating()) { + animating[0] = false; + semaphore.release(); + } + }); + Thread.sleep(500); + } + } + assertThat(semaphore.tryAcquire(1, 2, TimeUnit.SECONDS)).isTrue(); + waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java index 48ae2961b4be..2ed5addd900c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java @@ -164,11 +164,17 @@ public class PhysicsAnimationLayoutTestCase extends ShellTestCase { @Override public void cancelAllAnimations() { + if (mLayout.getChildCount() == 0) { + return; + } mMainThreadHandler.post(super::cancelAllAnimations); } @Override public void cancelAnimationsOnView(View view) { + if (mLayout.getChildCount() == 0) { + return; + } mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view)); } @@ -221,6 +227,9 @@ public class PhysicsAnimationLayoutTestCase extends ShellTestCase { @Override protected void startPathAnimation() { + if (mLayout.getChildCount() == 0) { + return; + } mMainThreadHandler.post(super::startPathAnimation); } } @@ -322,4 +331,9 @@ public class PhysicsAnimationLayoutTestCase extends ShellTestCase { e.printStackTrace(); } } + + /** Waits for the main thread to finish processing all pending runnables. */ + public void waitForMainThread() { + runOnMainThreadAndBlock(() -> {}); + } } |