blob: 1c4b00941e7373ed268dd398cff4fe908e4d6107 [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;
Joshua Tsuji4accf5982019-04-22 17:36:11 -040020import static org.junit.Assert.assertNotEquals;
21import static org.mockito.Mockito.verify;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080022
23import android.content.res.Resources;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080024import android.graphics.Point;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080025import android.graphics.PointF;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080026import android.testing.AndroidTestingRunner;
Joshua Tsuji442b6272019-02-08 13:23:43 -050027import android.view.View;
28import android.widget.FrameLayout;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080029
30import androidx.dynamicanimation.animation.DynamicAnimation;
Brett Chabot84151d92019-02-27 15:37:59 -080031import androidx.test.filters.SmallTest;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080032
33import com.android.systemui.R;
34
35import org.junit.Before;
Joshua Tsuji15e72622019-05-09 21:31:29 -040036import org.junit.Ignore;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080037import org.junit.Test;
38import org.junit.runner.RunWith;
39import org.mockito.Mockito;
40import org.mockito.Spy;
41
42@SmallTest
43@RunWith(AndroidTestingRunner.class)
44public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
45
Lyn Han522e9ff2019-05-17 13:26:13 -070046 private int mDisplayWidth = 500;
47 private int mDisplayHeight = 1000;
48
Joshua Tsujib1a796b2019-01-16 15:43:12 -080049 @Spy
Mady Mellor44ee2fe2019-01-30 17:51:16 -080050 private ExpandedAnimationController mExpandedController =
Lyn Han6f6b3ae2019-05-16 14:17:30 -070051 new ExpandedAnimationController(
Lyn Han522e9ff2019-05-17 13:26:13 -070052 new Point(mDisplayWidth, mDisplayHeight) /* displaySize */,
53 0 /* expandedViewPadding */);
Joshua Tsujib1a796b2019-01-16 15:43:12 -080054 private int mStackOffset;
55 private float mBubblePadding;
56 private float mBubbleSize;
57
58 private PointF mExpansionPoint;
59
60 @Before
61 public void setUp() throws Exception {
62 super.setUp();
Joshua Tsujicb97a112019-05-29 16:20:41 -040063 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujic36ee6f2019-05-28 17:00:16 -040064 mLayout.setActiveController(mExpandedController);
Lyn Han522e9ff2019-05-17 13:26:13 -070065
Joshua Tsujib1a796b2019-01-16 15:43:12 -080066 Resources res = mLayout.getResources();
67 mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
68 mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
69 mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
Joshua Tsuji1575e6b2019-01-30 13:43:28 -050070 mExpansionPoint = new PointF(100, 100);
Joshua Tsujib1a796b2019-01-16 15:43:12 -080071 }
72
73 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -040074 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -080075 public void testExpansionAndCollapse() throws InterruptedException {
Joshua Tsujib1a796b2019-01-16 15:43:12 -080076 Runnable afterExpand = Mockito.mock(Runnable.class);
Joshua Tsujif49ee142019-05-29 16:32:01 -040077 mExpandedController.expandFromStack(afterExpand);
Joshua Tsujib1a796b2019-01-16 15:43:12 -080078 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
79
Joshua Tsuji442b6272019-02-08 13:23:43 -050080 testBubblesInCorrectExpandedPositions();
Joshua Tsuji4accf5982019-04-22 17:36:11 -040081 verify(afterExpand).run();
Joshua Tsujib1a796b2019-01-16 15:43:12 -080082
83 Runnable afterCollapse = Mockito.mock(Runnable.class);
Joshua Tsujif49ee142019-05-29 16:32:01 -040084 mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse);
Joshua Tsujib1a796b2019-01-16 15:43:12 -080085 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
86
87 testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
Joshua Tsuji4accf5982019-04-22 17:36:11 -040088 verify(afterExpand).run();
Joshua Tsujib1a796b2019-01-16 15:43:12 -080089 }
90
Joshua Tsuji1575e6b2019-01-30 13:43:28 -050091 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -040092 @Ignore
Joshua Tsuji442b6272019-02-08 13:23:43 -050093 public void testOnChildAdded() throws InterruptedException {
94 expand();
95
96 // Add another new view and wait for its animation.
97 final View newView = new FrameLayout(getContext());
98 mLayout.addView(newView, 0);
Joshua Tsuji1575e6b2019-01-30 13:43:28 -050099 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
Joshua Tsuji442b6272019-02-08 13:23:43 -0500100
101 testBubblesInCorrectExpandedPositions();
102 }
103
104 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400105 @Ignore
Joshua Tsuji442b6272019-02-08 13:23:43 -0500106 public void testOnChildRemoved() throws InterruptedException {
107 expand();
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500108
109 // Remove some views and see if the remaining child views still pass the expansion test.
110 mLayout.removeView(mViews.get(0));
111 mLayout.removeView(mViews.get(3));
112 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
Joshua Tsuji442b6272019-02-08 13:23:43 -0500113 testBubblesInCorrectExpandedPositions();
114 }
115
116 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400117 @Ignore
Joshua Tsuji442b6272019-02-08 13:23:43 -0500118 public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException {
119 expand();
120
121 final View draggedBubble = mViews.get(0);
122 mExpandedController.prepareForBubbleDrag(draggedBubble);
123 mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
124
125 assertEquals(500f, draggedBubble.getTranslationX(), 1f);
126 assertEquals(500f, draggedBubble.getTranslationY(), 1f);
127
128 // Snap it back and make sure it made it back correctly.
129 mExpandedController.snapBubbleBack(draggedBubble, 0f, 0f);
130 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
131 testBubblesInCorrectExpandedPositions();
132 }
133
134 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400135 @Ignore
Joshua Tsuji442b6272019-02-08 13:23:43 -0500136 public void testBubbleDismissed() throws InterruptedException {
137 expand();
138
139 final View draggedBubble = mViews.get(0);
140 mExpandedController.prepareForBubbleDrag(draggedBubble);
141 mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
142
143 assertEquals(500f, draggedBubble.getTranslationX(), 1f);
144 assertEquals(500f, draggedBubble.getTranslationY(), 1f);
145
146 // Snap it back and make sure it made it back correctly.
Joshua Tsuji442b6272019-02-08 13:23:43 -0500147 mLayout.removeView(draggedBubble);
148 waitForLayoutMessageQueue();
149 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
150
151 assertEquals(-1, mLayout.indexOfChild(draggedBubble));
152 testBubblesInCorrectExpandedPositions();
153 }
154
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400155 @Test
Joshua Tsuji16a4abf2019-05-22 13:03:58 -0400156 @Ignore("Flaky")
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400157 public void testMagnetToDismiss_dismiss() throws InterruptedException {
158 expand();
159
160 final View draggedOutView = mViews.get(0);
161 final Runnable after = Mockito.mock(Runnable.class);
162
163 mExpandedController.prepareForBubbleDrag(draggedOutView);
164 mExpandedController.dragBubbleOut(draggedOutView, 25, 25);
165
166 // Magnet to dismiss, verify the bubble is at the dismiss target and the callback was
167 // called.
168 mExpandedController.magnetBubbleToDismiss(
169 mViews.get(0), 100 /* velX */, 100 /* velY */, 1000 /* destY */, after);
170 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
171 verify(after).run();
172 assertEquals(1000, mViews.get(0).getTranslationY(), .1f);
173
174 // Dismiss the now-magneted bubble, verify that the callback was called.
175 final Runnable afterDismiss = Mockito.mock(Runnable.class);
Joshua Tsujif49ee142019-05-29 16:32:01 -0400176 mExpandedController.dismissDraggedOutBubble(draggedOutView, afterDismiss);
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400177 waitForPropertyAnimations(DynamicAnimation.ALPHA);
178 verify(after).run();
179
180 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
181
182 assertEquals(mBubblePadding, mViews.get(1).getTranslationX(), 1f);
183 }
184
185 @Test
Joshua Tsuji15e72622019-05-09 21:31:29 -0400186 @Ignore("Flaky")
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400187 public void testMagnetToDismiss_demagnetizeThenDrag() throws InterruptedException {
188 expand();
189
190 final View draggedOutView = mViews.get(0);
191 final Runnable after = Mockito.mock(Runnable.class);
192
193 mExpandedController.prepareForBubbleDrag(draggedOutView);
194 mExpandedController.dragBubbleOut(draggedOutView, 25, 25);
195
196 // Magnet to dismiss, verify the bubble is at the dismiss target and the callback was
197 // called.
198 mExpandedController.magnetBubbleToDismiss(
199 draggedOutView, 100 /* velX */, 100 /* velY */, 1000 /* destY */, after);
200 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
201 verify(after).run();
202 assertEquals(1000, mViews.get(0).getTranslationY(), .1f);
203
204 // Demagnetize the bubble towards (25, 25).
205 mExpandedController.demagnetizeBubbleTo(25 /* x */, 25 /* y */, 100, 100);
206
207 // Start dragging towards (20, 20).
208 mExpandedController.dragBubbleOut(draggedOutView, 20, 20);
209
210 // Since we just demagnetized, the bubble shouldn't be at (20, 20), it should be animating
211 // towards it.
212 assertNotEquals(20, draggedOutView.getTranslationX());
213 assertNotEquals(20, draggedOutView.getTranslationY());
214 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
215
216 // Waiting for the animations should result in the bubble ending at (20, 20) since the
217 // animation end value was updated.
218 assertEquals(20, draggedOutView.getTranslationX(), 1f);
219 assertEquals(20, draggedOutView.getTranslationY(), 1f);
220
221 // Drag to (30, 30).
222 mExpandedController.dragBubbleOut(draggedOutView, 30, 30);
223
224 // It should go there instantly since the animations finished.
225 assertEquals(30, draggedOutView.getTranslationX(), 1f);
226 assertEquals(30, draggedOutView.getTranslationY(), 1f);
227 }
228
Joshua Tsuji442b6272019-02-08 13:23:43 -0500229 /** Expand the stack and wait for animations to finish. */
230 private void expand() throws InterruptedException {
Joshua Tsujif49ee142019-05-29 16:32:01 -0400231 mExpandedController.expandFromStack(Mockito.mock(Runnable.class));
Joshua Tsuji442b6272019-02-08 13:23:43 -0500232 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500233 }
234
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800235 /** Check that children are in the correct positions for being stacked. */
236 private void testStackedAtPosition(float x, float y, int offsetMultiplier) {
237 // Make sure the rest of the stack moved again, including the first bubble not moving, and
238 // is stacked to the right now that we're on the right side of the screen.
239 for (int i = 0; i < mLayout.getChildCount(); i++) {
240 assertEquals(x + i * offsetMultiplier * mStackOffset,
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500241 mLayout.getChildAt(i).getTranslationX(), 2f);
242 assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400243 assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800244 }
245 }
246
247 /** Check that children are in the correct positions for being expanded. */
Joshua Tsuji442b6272019-02-08 13:23:43 -0500248 private void testBubblesInCorrectExpandedPositions() {
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500249 // Check all the visible bubbles to see if they're in the right place.
Joshua Tsujicb97a112019-05-29 16:20:41 -0400250 for (int i = 0; i < mLayout.getChildCount(); i++) {
Lyn Han522e9ff2019-05-17 13:26:13 -0700251 assertEquals(getBubbleLeft(i),
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500252 mLayout.getChildAt(i).getTranslationX(),
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800253 2f);
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800254 assertEquals(mExpandedController.getExpandedY(),
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500255 mLayout.getChildAt(i).getTranslationY(), 2f);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800256 }
257 }
Lyn Han522e9ff2019-05-17 13:26:13 -0700258
259 /**
260 * @param index Bubble index in row.
261 * @return Bubble left x from left edge of screen.
262 */
263 public float getBubbleLeft(int index) {
264 float bubbleLeftFromRowLeft = index * (mBubbleSize + mBubblePadding);
265 return getRowLeft() + bubbleLeftFromRowLeft;
266 }
267
268 private float getRowLeft() {
269 if (mLayout == null) {
270 return 0;
271 }
272 int bubbleCount = mLayout.getChildCount();
Joshua Tsujicb97a112019-05-29 16:20:41 -0400273
Lyn Han522e9ff2019-05-17 13:26:13 -0700274 // Width calculations.
275 double bubble = bubbleCount * mBubbleSize;
276 float gap = (bubbleCount - 1) * mBubblePadding;
277 float row = gap + (float) bubble;
278
279 float halfRow = row / 2f;
280 float centerScreen = mDisplayWidth / 2;
281 float rowLeftFromScreenLeft = centerScreen - halfRow;
282
283 return rowLeftFromScreenLeft;
284 }
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800285}