blob: db819d57417b0207fb0f99302beed7749ef40939 [file] [log] [blame]
Joshua Tsujib1a796b2019-01-16 15:43:12 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.bubbles.animation;
18
19import static org.junit.Assert.assertEquals;
20
21import android.graphics.PointF;
22import android.support.test.filters.SmallTest;
23import android.testing.AndroidTestingRunner;
24import android.view.View;
25import android.widget.FrameLayout;
26
27import androidx.dynamicanimation.animation.DynamicAnimation;
28import androidx.dynamicanimation.animation.SpringForce;
29
30import com.android.systemui.R;
31
32import org.junit.Before;
Joshua Tsuji87ebd742019-01-25 16:01:26 -050033import org.junit.Ignore;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080034import org.junit.Test;
35import org.junit.runner.RunWith;
36import org.mockito.Spy;
37
38@SmallTest
39@RunWith(AndroidTestingRunner.class)
40public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
41
42 @Spy
43 private TestableStackController mStackController = new TestableStackController();
44
45 private int mStackOffset;
46
47 @Before
48 public void setUp() throws Exception {
49 super.setUp();
50 addOneMoreThanRenderLimitBubbles();
51 mLayout.setController(mStackController);
52 mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
53 }
54
55 /**
56 * Test moving around the stack, and make sure the position is updated correctly, and the stack
57 * direction is correct.
58 */
59 @Test
60 public void testMoveFirstBubbleWithStackFollowing() throws InterruptedException {
61 mStackController.moveFirstBubbleWithStackFollowing(200, 100);
62
63 // The first bubble should have moved instantly, the rest should be waiting for animation.
64 assertEquals(200, mViews.get(0).getTranslationX(), .1f);
65 assertEquals(100, mViews.get(0).getTranslationY(), .1f);
66 assertEquals(0, mViews.get(1).getTranslationX(), .1f);
67 assertEquals(0, mViews.get(1).getTranslationY(), .1f);
68
69 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
70
71 // Make sure the rest of the stack got moved to the right place and is stacked to the left.
72 testStackedAtPosition(200, 100, -1);
73 assertEquals(new PointF(200, 100), mStackController.getStackPosition());
74
75 mStackController.moveFirstBubbleWithStackFollowing(1000, 500);
76
77 // The first bubble again should have moved instantly while the rest remained where they
78 // were until the animation takes over.
79 assertEquals(1000, mViews.get(0).getTranslationX(), .1f);
80 assertEquals(500, mViews.get(0).getTranslationY(), .1f);
81 assertEquals(200 + -mStackOffset, mViews.get(1).getTranslationX(), .1f);
82 assertEquals(100, mViews.get(1).getTranslationY(), .1f);
83
84 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
85
86 // Make sure the rest of the stack moved again, including the first bubble not moving, and
87 // is stacked to the right now that we're on the right side of the screen.
88 testStackedAtPosition(1000, 500, 1);
89 assertEquals(new PointF(1000, 500), mStackController.getStackPosition());
90 }
91
92 @Test
Joshua Tsuji87ebd742019-01-25 16:01:26 -050093 @Ignore("Sporadically failing due to DynamicAnimation not settling.")
Joshua Tsujib1a796b2019-01-16 15:43:12 -080094 public void testFlingSideways() throws InterruptedException {
95 // Hard fling directly upwards, no X velocity. The X fling should terminate pretty much
96 // immediately, and spring to 0f, the y fling is hard enough that it will overshoot the top
97 // but should bounce back down.
98 mStackController.flingThenSpringFirstBubbleWithStackFollowing(
99 DynamicAnimation.TRANSLATION_X,
100 5000f, 1.15f, new SpringForce(), mWidth * 1f);
101 mStackController.flingThenSpringFirstBubbleWithStackFollowing(
102 DynamicAnimation.TRANSLATION_Y,
103 0f, 1.15f, new SpringForce(), 0f);
104
105 // Nothing should move initially since the animations haven't begun, including the first
106 // view.
107 assertEquals(0f, mViews.get(0).getTranslationX(), 1f);
108 assertEquals(0f, mViews.get(0).getTranslationY(), 1f);
109
110 // Wait for the flinging.
111 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X,
112 DynamicAnimation.TRANSLATION_Y);
113
114 // Wait for the springing.
115 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X,
116 DynamicAnimation.TRANSLATION_Y);
117
118 // Once the dust has settled, we should have flung all the way to the right side, with the
119 // stack stacked off to the right now.
120 testStackedAtPosition(mWidth * 1f, 0f, 1);
121 }
122
123 @Test
Joshua Tsuji87ebd742019-01-25 16:01:26 -0500124 @Ignore("Sporadically failing due to DynamicAnimation not settling.")
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800125 public void testFlingUpFromBelowBottomCenter() throws InterruptedException {
126 // Move to the center of the screen, just past the bottom.
127 mStackController.moveFirstBubbleWithStackFollowing(mWidth / 2f, mHeight + 100);
128 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
129
130 // Hard fling directly upwards, no X velocity. The X fling should terminate pretty much
131 // immediately, and spring to 0f, the y fling is hard enough that it will overshoot the top
132 // but should bounce back down.
133 mStackController.flingThenSpringFirstBubbleWithStackFollowing(
134 DynamicAnimation.TRANSLATION_X,
135 0, 1.15f, new SpringForce(), 27f);
136 mStackController.flingThenSpringFirstBubbleWithStackFollowing(
137 DynamicAnimation.TRANSLATION_Y,
138 5000f, 1.15f, new SpringForce(), 27f);
139
140 // Nothing should move initially since the animations haven't begun.
141 assertEquals(mWidth / 2f, mViews.get(0).getTranslationX(), .1f);
142 assertEquals(mHeight + 100, mViews.get(0).getTranslationY(), .1f);
143
144 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X,
145 DynamicAnimation.TRANSLATION_Y);
146
147 // Once the dust has settled, we should have flung a bit but then sprung to the final
148 // destination which is (27, 27).
149 testStackedAtPosition(27, 27, -1);
150 }
151
152 @Test
153 public void testChildAdded() throws InterruptedException {
154 // Move the stack to y = 500.
155 mStackController.moveFirstBubbleWithStackFollowing(0f, 500f);
156 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X,
157 DynamicAnimation.TRANSLATION_Y);
158
159 final View newView = new FrameLayout(mContext);
160 mLayout.addView(
161 newView,
162 0,
163 new FrameLayout.LayoutParams(50, 50));
164
165 waitForPropertyAnimations(
166 DynamicAnimation.TRANSLATION_X,
167 DynamicAnimation.TRANSLATION_Y,
168 DynamicAnimation.SCALE_X,
169 DynamicAnimation.SCALE_Y);
170
171 // The new view should be at the top of the stack, in the correct position.
172 assertEquals(0f, newView.getTranslationX(), .1f);
173 assertEquals(500f, newView.getTranslationY(), .1f);
174 assertEquals(1f, newView.getScaleX(), .1f);
175 assertEquals(1f, newView.getScaleY(), .1f);
176 assertEquals(1f, newView.getAlpha(), .1f);
177 }
178
179 @Test
180 public void testChildRemoved() throws InterruptedException {
181 final View firstView = mLayout.getChildAt(0);
182 mLayout.removeView(firstView);
183
184 // The view should still be there, since the controller is animating it out and hasn't yet
185 // actually removed it from the parent view.
186 assertEquals(0, mLayout.indexOfChild(firstView));
187
188 waitForPropertyAnimations(DynamicAnimation.ALPHA);
189 waitForLayoutMessageQueue();
190
191 assertEquals(-1, mLayout.indexOfChild(firstView));
192
193 // The subsequent view should have been translated over to 0, not stacked off to the left.
194 assertEquals(0, mLayout.getChildAt(0).getTranslationX(), .1f);
195 }
196
197 /**
198 * Checks every child view to make sure it's stacked at the given coordinates, off to the left
199 * or right side depending on offset multiplier.
200 */
201 private void testStackedAtPosition(float x, float y, int offsetMultiplier) {
202 // Make sure the rest of the stack moved again, including the first bubble not moving, and
203 // is stacked to the right now that we're on the right side of the screen.
204 for (int i = 0; i < mLayout.getChildCount(); i++) {
205 assertEquals(x + i * offsetMultiplier * mStackOffset,
206 mViews.get(i).getTranslationX(), 2f);
207 assertEquals(y, mViews.get(i).getTranslationY(), 2f);
208 }
209 }
210
211 /**
212 * Testable version of the stack controller that dispatches its animations on the main thread.
213 */
214 private class TestableStackController extends StackAnimationController {
215 @Override
216 public void flingThenSpringFirstBubbleWithStackFollowing(
217 DynamicAnimation.ViewProperty property, float vel, float friction,
218 SpringForce spring, Float finalPosition) {
219 mMainThreadHandler.post(() ->
220 super.flingThenSpringFirstBubbleWithStackFollowing(
221 property, vel, friction, spring, finalPosition));
222 }
223 }
224}