blob: 5650e1e930f0488b48ee23a43072544857e208ed [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;
20import static org.junit.Assert.assertFalse;
21import static org.junit.Assert.assertTrue;
22import static org.mockito.ArgumentMatchers.any;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080023import static org.mockito.ArgumentMatchers.anyInt;
24import static org.mockito.ArgumentMatchers.eq;
25import static org.mockito.Mockito.inOrder;
Joshua Tsujic1108432019-02-22 16:10:12 -050026import static org.mockito.Mockito.times;
Joshua Tsujid54e8e02019-04-05 14:21:53 -040027import static org.mockito.Mockito.verify;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080028
29import android.os.SystemClock;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080030import android.testing.AndroidTestingRunner;
31import android.view.View;
32import android.widget.FrameLayout;
33
34import androidx.dynamicanimation.animation.DynamicAnimation;
35import androidx.dynamicanimation.animation.SpringForce;
Brett Chabot84151d92019-02-27 15:37:59 -080036import androidx.test.filters.SmallTest;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080037
38import com.google.android.collect.Sets;
39
40import org.junit.Before;
Joshua Tsujic5fab412019-09-12 15:16:40 -040041import org.junit.Ignore;
Joshua Tsujib1a796b2019-01-16 15:43:12 -080042import org.junit.Test;
43import org.junit.runner.RunWith;
44import org.mockito.InOrder;
45import org.mockito.Mockito;
46import org.mockito.Spy;
47
48import java.util.HashMap;
49import java.util.HashSet;
50import java.util.Set;
51import java.util.concurrent.CountDownLatch;
52import java.util.concurrent.TimeUnit;
53
54@SmallTest
55@RunWith(AndroidTestingRunner.class)
56/** Tests the PhysicsAnimationLayout itself, with a basic test animation controller. */
57public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
58 static final float TEST_TRANSLATION_X_OFFSET = 15f;
59
60 @Spy
61 private TestableAnimationController mTestableController = new TestableAnimationController();
62
63 @Before
64 public void setUp() throws Exception {
65 super.setUp();
66
67 // By default, use translation animations, chain the X animations with the default
68 // offset, and don't actually remove views immediately (since most implementations will wait
69 // to animate child views out before actually removing them).
70 mTestableController.setAnimatedProperties(Sets.newHashSet(
Joshua Tsujic1108432019-02-22 16:10:12 -050071 DynamicAnimation.TRANSLATION_X,
72 DynamicAnimation.TRANSLATION_Y));
Joshua Tsujib1a796b2019-01-16 15:43:12 -080073 mTestableController.setChainedProperties(Sets.newHashSet(DynamicAnimation.TRANSLATION_X));
74 mTestableController.setOffsetForProperty(
75 DynamicAnimation.TRANSLATION_X, TEST_TRANSLATION_X_OFFSET);
76 mTestableController.setRemoveImmediately(false);
77 }
78
79 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -040080 @Ignore
Joshua Tsujia08b6d32019-01-29 16:15:52 -050081 public void testHierarchyChanges() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -040082 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -040083 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujib1a796b2019-01-16 15:43:12 -080084
85 // Make sure the controller was notified of all the views we added.
86 for (View mView : mViews) {
87 Mockito.verify(mTestableController).onChildAdded(mView, 0);
88 }
89
90 // Remove some views and ensure the controller was notified, with the proper indices.
91 mTestableController.setRemoveImmediately(true);
92 mLayout.removeView(mViews.get(1));
93 mLayout.removeView(mViews.get(2));
Joshua Tsuji1575e6b2019-01-30 13:43:28 -050094 Mockito.verify(mTestableController).onChildRemoved(
Joshua Tsujib1a796b2019-01-16 15:43:12 -080095 eq(mViews.get(1)), eq(1), any());
Joshua Tsuji1575e6b2019-01-30 13:43:28 -050096 Mockito.verify(mTestableController).onChildRemoved(
Joshua Tsujib1a796b2019-01-16 15:43:12 -080097 eq(mViews.get(2)), eq(1), any());
98
99 // Make sure we still get view added notifications after doing some removals.
100 final View newBubble = new FrameLayout(mContext);
101 mLayout.addView(newBubble, 0);
102 Mockito.verify(mTestableController).onChildAdded(newBubble, 0);
103 }
104
105 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400106 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800107 public void testUpdateValueNotChained() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400108 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400109 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800110
111 // Don't chain any values.
112 mTestableController.setChainedProperties(Sets.newHashSet());
113
114 // Child views should not be translated.
115 assertEquals(0, mLayout.getChildAt(0).getTranslationX(), .1f);
116 assertEquals(0, mLayout.getChildAt(1).getTranslationX(), .1f);
117
118 // Animate the first child's translation X.
119 final CountDownLatch animLatch = new CountDownLatch(1);
Joshua Tsujic1108432019-02-22 16:10:12 -0500120
121 mTestableController
122 .animationForChildAtIndex(0)
123 .translationX(100)
124 .start(animLatch::countDown);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800125 animLatch.await(1, TimeUnit.SECONDS);
126
127 // Ensure that the first view has been translated, but not the second one.
128 assertEquals(100, mLayout.getChildAt(0).getTranslationX(), .1f);
129 assertEquals(0, mLayout.getChildAt(1).getTranslationX(), .1f);
130 }
131
132 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400133 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800134 public void testUpdateValueXChained() throws InterruptedException {
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800135 testChainedTranslationAnimations();
136 }
137
138 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400139 @Ignore
Joshua Tsujic1108432019-02-22 16:10:12 -0500140 public void testSetEndActions() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400141 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400142 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800143 mTestableController.setChainedProperties(Sets.newHashSet());
144
145 final CountDownLatch xLatch = new CountDownLatch(1);
Joshua Tsujic1108432019-02-22 16:10:12 -0500146 Runnable xEndAction = Mockito.spy(new Runnable() {
147
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800148 @Override
Joshua Tsujic1108432019-02-22 16:10:12 -0500149 public void run() {
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800150 xLatch.countDown();
151 }
152 });
153
154 final CountDownLatch yLatch = new CountDownLatch(1);
Joshua Tsujic1108432019-02-22 16:10:12 -0500155 Runnable yEndAction = Mockito.spy(new Runnable() {
156
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800157 @Override
Joshua Tsujic1108432019-02-22 16:10:12 -0500158 public void run() {
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800159 yLatch.countDown();
160 }
161 });
162
163 // Set end listeners for both x and y.
Joshua Tsujic1108432019-02-22 16:10:12 -0500164 mLayout.setEndActionForProperty(xEndAction, DynamicAnimation.TRANSLATION_X);
165 mLayout.setEndActionForProperty(yEndAction, DynamicAnimation.TRANSLATION_Y);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800166
167 // Animate x, and wait for it to finish.
Joshua Tsujic1108432019-02-22 16:10:12 -0500168 mTestableController.animationForChildAtIndex(0)
169 .translationX(100)
170 .start();
171
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800172 xLatch.await();
173 yLatch.await(1, TimeUnit.SECONDS);
174
175 // Make sure the x end listener was called only one time, and the y listener was never
176 // called since we didn't animate y. Wait 1 second after the original animation end trigger
177 // to make sure it doesn't get called again.
Joshua Tsujic1108432019-02-22 16:10:12 -0500178 Mockito.verify(xEndAction, Mockito.after(1000).times(1)).run();
179 Mockito.verify(yEndAction, Mockito.after(1000).never()).run();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800180 }
181
182 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400183 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800184 public void testRemoveEndListeners() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400185 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400186 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800187 mTestableController.setChainedProperties(Sets.newHashSet());
188
189 final CountDownLatch xLatch = new CountDownLatch(1);
Joshua Tsujic1108432019-02-22 16:10:12 -0500190 Runnable xEndListener = Mockito.spy(new Runnable() {
191
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800192 @Override
Joshua Tsujic1108432019-02-22 16:10:12 -0500193 public void run() {
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800194 xLatch.countDown();
195 }
196 });
197
198 // Set the end listener.
Joshua Tsujic1108432019-02-22 16:10:12 -0500199 mLayout.setEndActionForProperty(xEndListener, DynamicAnimation.TRANSLATION_X);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800200
201 // Animate x, and wait for it to finish.
Joshua Tsujic1108432019-02-22 16:10:12 -0500202 mTestableController.animationForChildAtIndex(0)
203 .translationX(100)
204 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800205 xLatch.await();
206
207 InOrder endListenerCalls = inOrder(xEndListener);
Joshua Tsujic1108432019-02-22 16:10:12 -0500208 endListenerCalls.verify(xEndListener, Mockito.times(1)).run();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800209
210 // Animate X again, remove the end listener.
Joshua Tsujic1108432019-02-22 16:10:12 -0500211 mTestableController.animationForChildAtIndex(0)
212 .translationX(1000)
213 .start();
214 mLayout.removeEndActionForProperty(DynamicAnimation.TRANSLATION_X);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800215 xLatch.await(1, TimeUnit.SECONDS);
216
217 // Make sure the end listener was not called.
218 endListenerCalls.verifyNoMoreInteractions();
219 }
220
221 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400222 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800223 public void testSetController() throws InterruptedException {
224 // Add the bubbles, then set the controller, to make sure that a controller added to an
225 // already-initialized view works correctly.
Joshua Tsujicb97a112019-05-29 16:20:41 -0400226 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400227 mLayout.setActiveController(mTestableController);
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800228 testChainedTranslationAnimations();
229
230 TestableAnimationController secondController =
231 Mockito.spy(new TestableAnimationController());
232 secondController.setAnimatedProperties(Sets.newHashSet(
233 DynamicAnimation.SCALE_X, DynamicAnimation.SCALE_Y));
234 secondController.setChainedProperties(Sets.newHashSet(
235 DynamicAnimation.SCALE_X));
236 secondController.setOffsetForProperty(
237 DynamicAnimation.SCALE_X, 10f);
238 secondController.setRemoveImmediately(true);
239
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400240 mLayout.setActiveController(secondController);
Joshua Tsujic1108432019-02-22 16:10:12 -0500241 mTestableController.animationForChildAtIndex(0)
242 .scaleX(1.5f)
243 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800244
245 waitForPropertyAnimations(DynamicAnimation.SCALE_X);
246
247 // Make sure we never asked the original controller about any SCALE animations, that would
248 // mean the controller wasn't switched over properly.
249 Mockito.verify(mTestableController, Mockito.never())
250 .getNextAnimationInChain(eq(DynamicAnimation.SCALE_X), anyInt());
251 Mockito.verify(mTestableController, Mockito.never())
252 .getOffsetForChainedPropertyAnimation(eq(DynamicAnimation.SCALE_X));
253
254 // Make sure we asked the new controller about its animated properties, and configuration
255 // options.
256 Mockito.verify(secondController, Mockito.atLeastOnce())
257 .getAnimatedProperties();
258 Mockito.verify(secondController, Mockito.atLeastOnce())
259 .getNextAnimationInChain(eq(DynamicAnimation.SCALE_X), anyInt());
260 Mockito.verify(secondController, Mockito.atLeastOnce())
261 .getOffsetForChainedPropertyAnimation(eq(DynamicAnimation.SCALE_X));
262
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400263 mLayout.setActiveController(mTestableController);
Joshua Tsujic1108432019-02-22 16:10:12 -0500264 mTestableController.animationForChildAtIndex(0)
265 .translationX(100f)
266 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800267
268 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X);
269
270 // Make sure we never asked the second controller about the TRANSLATION_X animation.
271 Mockito.verify(secondController, Mockito.never())
272 .getNextAnimationInChain(eq(DynamicAnimation.TRANSLATION_X), anyInt());
273 Mockito.verify(secondController, Mockito.never())
274 .getOffsetForChainedPropertyAnimation(eq(DynamicAnimation.TRANSLATION_X));
275
276 }
277
278 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400279 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800280 public void testArePropertiesAnimating() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400281 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400282 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800283
284 assertFalse(mLayout.arePropertiesAnimating(
285 DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y));
286
Joshua Tsujic1108432019-02-22 16:10:12 -0500287 mTestableController.animationForChildAtIndex(0)
288 .translationX(100f)
289 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800290
291 // Wait for the animations to get underway.
292 SystemClock.sleep(50);
293
294 assertTrue(mLayout.arePropertiesAnimating(DynamicAnimation.TRANSLATION_X));
295 assertFalse(mLayout.arePropertiesAnimating(DynamicAnimation.TRANSLATION_Y));
296
297 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X);
298
299 assertFalse(mLayout.arePropertiesAnimating(
300 DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y));
301 }
302
303 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400304 @Ignore
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800305 public void testCancelAllAnimations() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400306 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400307 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800308
Joshua Tsujic1108432019-02-22 16:10:12 -0500309 mTestableController.animationForChildAtIndex(0)
310 .position(1000, 1000)
311 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800312
313 mLayout.cancelAllAnimations();
314
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800315 // Animations should be somewhere before their end point.
316 assertTrue(mViews.get(0).getTranslationX() < 1000);
317 assertTrue(mViews.get(0).getTranslationY() < 1000);
318 }
319
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800320 /** Standard test of chained translation animations. */
321 private void testChainedTranslationAnimations() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400322 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400323 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujic1108432019-02-22 16:10:12 -0500324
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800325 assertEquals(0, mLayout.getChildAt(0).getTranslationX(), .1f);
326 assertEquals(0, mLayout.getChildAt(1).getTranslationX(), .1f);
327
Joshua Tsujic1108432019-02-22 16:10:12 -0500328 mTestableController.animationForChildAtIndex(0)
329 .translationX(100f)
330 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800331
332 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X);
333
Joshua Tsujicb97a112019-05-29 16:20:41 -0400334 for (int i = 0; i < mLayout.getChildCount(); i++) {
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800335 assertEquals(
336 100 + i * TEST_TRANSLATION_X_OFFSET,
337 mLayout.getChildAt(i).getTranslationX(), .1f);
338 }
339
340 // Ensure that the Y translations were unaffected.
341 assertEquals(0, mLayout.getChildAt(0).getTranslationY(), .1f);
342 assertEquals(0, mLayout.getChildAt(1).getTranslationY(), .1f);
343
344 // Animate the first child's Y translation.
Joshua Tsujic1108432019-02-22 16:10:12 -0500345 mTestableController.animationForChildAtIndex(0)
346 .translationY(100f)
347 .start();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800348
349 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_Y);
350
351 // Ensure that only the first view's Y translation chained, since we only chained X
352 // translations.
353 assertEquals(100, mLayout.getChildAt(0).getTranslationY(), .1f);
354 assertEquals(0, mLayout.getChildAt(1).getTranslationY(), .1f);
355 }
356
Joshua Tsujic1108432019-02-22 16:10:12 -0500357 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400358 @Ignore
Joshua Tsujic1108432019-02-22 16:10:12 -0500359 public void testPhysicsAnimator() throws InterruptedException {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400360 mLayout.setActiveController(mTestableController);
Joshua Tsujicb97a112019-05-29 16:20:41 -0400361 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujic1108432019-02-22 16:10:12 -0500362
363 Runnable afterAll = Mockito.mock(Runnable.class);
364 Runnable after = Mockito.spy(new Runnable() {
365 int mCallCount = 0;
366
367 @Override
368 public void run() {
369 // Make sure that if only one of the animations has finished, we didn't already call
370 // afterAll.
371 if (mCallCount == 1) {
372 Mockito.verifyNoMoreInteractions(afterAll);
373 }
374 }
375 });
376
377 // Animate from x = 7 to x = 100, and from y = 100 to 7 = 200, calling 'after' after each
378 // property's animation completes, then call afterAll when they're all complete.
379 mTestableController.animationForChildAtIndex(0)
380 .translationX(7, 100, after)
381 .translationY(100, 200, after)
382 .start(afterAll);
383
384 // We should have immediately set the 'from' values.
385 assertEquals(7, mViews.get(0).getTranslationX(), .01f);
386 assertEquals(100, mViews.get(0).getTranslationY(), .01f);
387
388 waitForPropertyAnimations(
389 DynamicAnimation.TRANSLATION_X,
390 DynamicAnimation.TRANSLATION_Y);
391
392 // We should have called the after callback twice, and afterAll once. We verify in the
393 // mocked callback that afterAll isn't called before both finish.
394 Mockito.verify(after, times(2)).run();
395 Mockito.verify(afterAll).run();
396
397 // Make sure we actually animated the views.
398 assertEquals(100, mViews.get(0).getTranslationX(), .01f);
399 assertEquals(200, mViews.get(0).getTranslationY(), .01f);
400 }
401
402 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400403 @Ignore
Joshua Tsujic1108432019-02-22 16:10:12 -0500404 public void testAnimationsForChildrenFromIndex() throws InterruptedException {
405 // Don't chain since we're going to invoke each animation independently.
406 mTestableController.setChainedProperties(new HashSet<>());
407
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400408 mLayout.setActiveController(mTestableController);
Joshua Tsujic1108432019-02-22 16:10:12 -0500409
Joshua Tsujicb97a112019-05-29 16:20:41 -0400410 addOneMoreThanBubbleLimitBubbles();
Joshua Tsujic1108432019-02-22 16:10:12 -0500411
412 Runnable allEnd = Mockito.mock(Runnable.class);
413
414 mTestableController.animationsForChildrenFromIndex(
415 1, (index, animation) -> animation.translationX((index - 1) * 50))
416 .startAll(allEnd);
417
418 waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X);
419
420 assertEquals(0, mViews.get(0).getTranslationX(), .1f);
421 assertEquals(0, mViews.get(1).getTranslationX(), .1f);
422 assertEquals(50, mViews.get(2).getTranslationX(), .1f);
423 assertEquals(100, mViews.get(3).getTranslationX(), .1f);
424
425 Mockito.verify(allEnd, times(1)).run();
426 }
427
Joshua Tsujid54e8e02019-04-05 14:21:53 -0400428 @Test
Joshua Tsujic5fab412019-09-12 15:16:40 -0400429 @Ignore
Joshua Tsujid54e8e02019-04-05 14:21:53 -0400430 public void testAnimationsForChildrenFromIndex_noChildren() {
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400431 mLayout.setActiveController(mTestableController);
Joshua Tsujid54e8e02019-04-05 14:21:53 -0400432
433 final Runnable after = Mockito.mock(Runnable.class);
434 mTestableController
435 .animationsForChildrenFromIndex(0, (index, animation) -> { })
436 .startAll(after);
437
438 verify(after, Mockito.times(1)).run();
439 }
440
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800441 /**
442 * Animation controller with configuration methods whose return values can be set by individual
443 * tests.
444 */
445 private class TestableAnimationController
446 extends PhysicsAnimationLayout.PhysicsAnimationController {
447 private Set<DynamicAnimation.ViewProperty> mAnimatedProperties = new HashSet<>();
448 private Set<DynamicAnimation.ViewProperty> mChainedProperties = new HashSet<>();
449 private HashMap<DynamicAnimation.ViewProperty, Float> mOffsetForProperty = new HashMap<>();
450 private boolean mRemoveImmediately = false;
451
452 void setAnimatedProperties(
453 Set<DynamicAnimation.ViewProperty> animatedProperties) {
454 mAnimatedProperties = animatedProperties;
455 }
456
457 void setChainedProperties(
458 Set<DynamicAnimation.ViewProperty> chainedProperties) {
459 mChainedProperties = chainedProperties;
460 }
461
462 void setOffsetForProperty(
463 DynamicAnimation.ViewProperty property, float offset) {
464 mOffsetForProperty.put(property, offset);
465 }
466
467 public void setRemoveImmediately(boolean removeImmediately) {
468 mRemoveImmediately = removeImmediately;
469 }
470
471 @Override
472 Set<DynamicAnimation.ViewProperty> getAnimatedProperties() {
473 return mAnimatedProperties;
474 }
475
476 @Override
477 int getNextAnimationInChain(DynamicAnimation.ViewProperty property, int index) {
478 return mChainedProperties.contains(property) ? index + 1 : NONE;
479 }
480
481 @Override
482 float getOffsetForChainedPropertyAnimation(DynamicAnimation.ViewProperty property) {
483 return mOffsetForProperty.getOrDefault(property, 0f);
484 }
485
486 @Override
487 SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
488 return new SpringForce();
489 }
490
491 @Override
492 void onChildAdded(View child, int index) {}
493
494 @Override
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500495 void onChildRemoved(View child, int index, Runnable finishRemoval) {
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800496 if (mRemoveImmediately) {
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500497 finishRemoval.run();
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800498 }
499 }
Joshua Tsuji1575e6b2019-01-30 13:43:28 -0500500
501 @Override
Joshua Tsujif49ee142019-05-29 16:32:01 -0400502 void onChildReordered(View child, int oldIndex, int newIndex) {}
503
504 @Override
Joshua Tsujic36ee6f2019-05-28 17:00:16 -0400505 void onActiveControllerForLayout(PhysicsAnimationLayout layout) {}
Joshua Tsujib1a796b2019-01-16 15:43:12 -0800506 }
507}