1 /*
2  * Copyright (C) 2009 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 
17 package android.view.animation.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNotSame;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertSame;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.Mockito.atLeastOnce;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.reset;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.verifyZeroInteractions;
33 
34 import android.app.Activity;
35 import android.app.Instrumentation;
36 import android.content.res.XmlResourceParser;
37 import android.os.SystemClock;
38 import android.util.AttributeSet;
39 import android.util.Xml;
40 import android.view.View;
41 import android.view.animation.AccelerateDecelerateInterpolator;
42 import android.view.animation.AccelerateInterpolator;
43 import android.view.animation.Animation;
44 import android.view.animation.Animation.AnimationListener;
45 import android.view.animation.AnimationUtils;
46 import android.view.animation.DecelerateInterpolator;
47 import android.view.animation.Interpolator;
48 import android.view.animation.Transformation;
49 import android.view.cts.R;
50 
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.LargeTest;
53 import androidx.test.filters.MediumTest;
54 import androidx.test.rule.ActivityTestRule;
55 import androidx.test.runner.AndroidJUnit4;
56 
57 import com.android.compatibility.common.util.PollingCheck;
58 
59 import org.junit.Before;
60 import org.junit.Rule;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 
64 import java.util.concurrent.CountDownLatch;
65 import java.util.concurrent.TimeUnit;
66 
67 /**
68  * Test {@link Animation}.
69  */
70 @MediumTest
71 @RunWith(AndroidJUnit4.class)
72 public class AnimationTest {
73     private static final float COMPARISON_DELTA = 0.001f;
74 
75     /** It is defined in R.anim.accelerate_alpha */
76     private static final int ACCELERATE_ALPHA_DURATION = 1000;
77 
78     /** It is defined in R.anim.decelerate_alpha */
79     private static final int DECELERATE_ALPHA_DURATION = 2000;
80 
81     private static final int CANCELATION_TIMEOUT = 5000;
82 
83     private Instrumentation mInstrumentation;
84     private Activity mActivity;
85 
86     @Rule
87     public ActivityTestRule<AnimationTestCtsActivity> mActivityRule =
88             new ActivityTestRule<>(AnimationTestCtsActivity.class);
89 
90     @Before
setup()91     public void setup() {
92         mInstrumentation = InstrumentationRegistry.getInstrumentation();
93         mActivity = mActivityRule.getActivity();
94     }
95 
96     @Test
testConstructor()97     public void testConstructor() {
98         XmlResourceParser parser = mActivity.getResources().getAnimation(R.anim.alpha);
99         AttributeSet attrs = Xml.asAttributeSet(parser);
100         new Animation(mActivity, attrs) {
101         };
102 
103         new Animation() {
104         };
105     }
106 
107     @Test
testAccessInterpolator()108     public void testAccessInterpolator() {
109         // check default interpolator
110         MyAnimation myAnimation = new MyAnimation();
111         Interpolator interpolator = myAnimation.getInterpolator();
112         assertTrue(interpolator instanceof AccelerateDecelerateInterpolator); // issue 1561186.
113 
114         myAnimation.ensureInterpolator();
115         assertSame(interpolator, myAnimation.getInterpolator());
116 
117         myAnimation.setInterpolator(null);
118         assertNull(myAnimation.getInterpolator());
119         myAnimation.ensureInterpolator();
120         interpolator = myAnimation.getInterpolator();
121         assertTrue(interpolator instanceof AccelerateDecelerateInterpolator);
122 
123         Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha);
124         interpolator = animation.getInterpolator();
125         assertNotNull(interpolator);
126         assertTrue(interpolator instanceof DecelerateInterpolator);
127 
128         animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
129         interpolator = animation.getInterpolator();
130         assertNotNull(interpolator);
131         assertTrue(interpolator instanceof AccelerateInterpolator);
132     }
133 
134     @Test
testDefaultFill()135     public void testDefaultFill() {
136         Animation animation = new Animation() {
137         };
138         assertTrue(animation.getFillBefore());
139         assertFalse(animation.getFillAfter());
140     }
141 
142     @Test
testAccessFill()143     public void testAccessFill() throws Throwable {
144         View animWindow = mActivity.findViewById(R.id.anim_window);
145         // XML file of R.anim.accelerate_alpha
146         // <alpha xmlns:android="http://schemas.android.com/apk/res/android"
147         //     android:interpolator="@android:anim/accelerate_interpolator"
148         //     android:fromAlpha="0.1"
149         //     android:toAlpha="0.9"
150         //     android:duration="1000" />
151         Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
152         assertFalse(animation.isFillEnabled());
153         assertTrue(animation.getFillBefore());
154         assertFalse(animation.getFillAfter());
155 
156         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
157                 animation);
158 
159         // fillBefore and fillAfter are ignored when fillEnabled is false
160         Transformation transformation = new Transformation();
161         // check alpha before start
162         animation.getTransformation(animation.getStartTime() - 1, transformation);
163         float alpha = transformation.getAlpha();
164         assertEquals(0.1f, alpha, COMPARISON_DELTA);  // issue 1698355
165 
166         transformation = new Transformation();
167         // check alpha after the end
168         animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1,
169                 transformation);
170         alpha = transformation.getAlpha();
171         assertEquals(0.9f, alpha, COMPARISON_DELTA);  // issue 1698355
172 
173         animation.setFillEnabled(true);
174         animation.setFillBefore(false);
175         assertTrue(animation.isFillEnabled());
176         assertFalse(animation.getFillBefore());
177         assertFalse(animation.getFillAfter());
178         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
179                 animation);
180 
181         transformation = new Transformation();
182         animation.getTransformation(animation.getStartTime() - 1, transformation);
183         alpha = transformation.getAlpha();
184         assertEquals(1.0f, alpha, COMPARISON_DELTA);
185 
186         transformation = new Transformation();
187         animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1,
188                 transformation);
189         alpha = transformation.getAlpha();
190         assertEquals(1.0f, alpha, COMPARISON_DELTA);
191 
192         animation.setFillBefore(true);
193         animation.setFillAfter(true);
194         assertTrue(animation.isFillEnabled());
195         assertTrue(animation.getFillBefore());
196         assertTrue(animation.getFillAfter());
197         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
198                 animation);
199 
200         transformation = new Transformation();
201         animation.getTransformation(animation.getStartTime() - 1, transformation);
202         alpha = transformation.getAlpha();
203         assertEquals(0.1f, alpha, COMPARISON_DELTA);
204 
205         transformation = new Transformation();
206         animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1,
207                 transformation);
208         alpha = transformation.getAlpha();
209         assertEquals(0.9f, alpha, COMPARISON_DELTA);
210     }
211 
212     @Test
testComputeDurationHint()213     public void testComputeDurationHint() {
214         // start offset is 0, duration is 2000, repeat count is 0.
215         Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha);
216         assertEquals(2000, animation.computeDurationHint());
217 
218         // start offset is 0, duration is 2000, repeat count is 2.
219         animation.setRepeatCount(2);
220         assertEquals(6000, animation.computeDurationHint());
221 
222         // start offset is 800, duration is 2000, repeat count is 2.
223         animation.setStartOffset(800);
224         assertEquals(8400, animation.computeDurationHint());
225     }
226 
227     @Test
testRepeatAnimation()228     public void testRepeatAnimation() throws Throwable {
229         // check default repeatMode
230         Animation animation = new Animation() {
231         };
232         assertEquals(Animation.RESTART, animation.getRepeatMode());
233 
234         final View animWindow = mActivity.findViewById(R.id.anim_window);
235 
236         // XML file of R.anim.decelerate_alpha
237         // <alpha xmlns:android="http://schemas.android.com/apk/res/android"
238         //      android:interpolator="@android:anim/decelerate_interpolator"
239         //      android:fromAlpha="0.0"
240         //      android:toAlpha="1.0"
241         //      android:duration="2000" />
242         final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha);
243         assertEquals(Animation.RESTART, animation.getRepeatMode());
244         long duration = anim.getDuration();
245         assertEquals(DECELERATE_ALPHA_DURATION, duration);
246         // repeat count is 0, repeat mode does not make sense.
247         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim);
248 
249         // test repeat mode REVERSE
250         anim.setRepeatCount(1);
251         anim.setRepeatMode(Animation.REVERSE);
252         // we have to PollingCheck the animation status on test thread,
253         // it cannot be done on UI thread, so we invoke runOnUiThread method here.
254         mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
255 
256         // check whether animation has started
257         PollingCheck.waitFor(anim::hasStarted);
258 
259         Transformation transformation = new Transformation();
260         long startTime = anim.getStartTime();
261         anim.getTransformation(startTime, transformation);
262         float alpha1 = transformation.getAlpha();
263         assertEquals(0.0f, alpha1, COMPARISON_DELTA);
264 
265         anim.getTransformation(startTime + 1000, transformation);
266         float alpha2 = transformation.getAlpha();
267 
268         anim.getTransformation(startTime + 2000, transformation);
269         float alpha3 = transformation.getAlpha();
270         assertEquals(1.0f, alpha3, COMPARISON_DELTA);
271 
272         // wait for animation has ended.
273         // timeout is larger than duration, in case the system is sluggish
274         PollingCheck.waitFor(duration * 2 + 1000, anim::hasEnded);
275 
276         // get start time of reversing.
277         startTime = anim.getStartTime();
278         anim.getTransformation(startTime + 3000, transformation);
279         float alpha4 = transformation.getAlpha();
280 
281         anim.getTransformation(startTime + 4000, transformation);
282         float alpha5 = transformation.getAlpha();
283         assertEquals(0.0f, alpha5, COMPARISON_DELTA);
284 
285         // check decelerating delta alpha when reverse. alpha should change form 0.0f to 1.0f
286         // and then from 1.0f to 0.0f
287         float delta1 = alpha2 - alpha1;
288         float delta2 = alpha3 - alpha2;
289         // the animation plays backward
290         float delta3 = alpha3 - alpha4;
291         float delta4 = alpha4 - alpha5;
292         assertTrue(delta1 > delta2);
293         assertTrue(delta3 > delta4);
294 
295         // test repeat mode RESTART
296         anim.setRepeatMode(Animation.RESTART);
297         // we have to PollingCheck the animation status on test thread,
298         // it cannot be done on UI thread, so we invoke runOnUiThread method here.
299         mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
300 
301         // check whether animation has started
302         PollingCheck.waitFor(anim::hasStarted);
303 
304         transformation = new Transformation();
305         startTime = anim.getStartTime();
306         anim.getTransformation(startTime, transformation);
307         alpha1 = transformation.getAlpha();
308         assertEquals(0.0f, alpha1, COMPARISON_DELTA);
309 
310         anim.getTransformation(startTime + 1000, transformation);
311         alpha2 = transformation.getAlpha();
312 
313         anim.getTransformation(startTime + 2000, transformation);
314         alpha3 = transformation.getAlpha();
315         assertEquals(1.0f, alpha3, COMPARISON_DELTA);
316 
317         // wait for animation has ended.
318         // timeout is larger than duration, in case the system is sluggish
319         PollingCheck.waitFor(duration * 2 + 1000, anim::hasEnded);
320 
321         // get start time of restarting.
322         startTime = anim.getStartTime();
323         anim.getTransformation(startTime + 3000, transformation);
324         alpha4 = transformation.getAlpha();
325 
326         anim.getTransformation(startTime + 4000, transformation);
327         alpha5 = transformation.getAlpha();
328         assertEquals(1.0f, alpha5, COMPARISON_DELTA);
329 
330         // check decelerating delta alpha when restart. alpha should change form 0.0f to 1.0f
331         // and then from 0.0f to 1.0f again
332         delta1 = alpha2 - 0.0f;
333         delta2 = alpha3 - alpha2;
334         // the animation restarts from the beginning
335         delta3 = alpha4 - 0.0f;
336         delta4 = alpha5 - alpha4;
337         assertTrue(delta1 > delta2);
338         assertTrue(delta3 > delta4);
339     }
340 
341     @Test
testAccessStartOffset()342     public void testAccessStartOffset() throws Throwable {
343         final long startOffset = 800;
344         // check default startOffset
345         Animation animation = new Animation() {
346         };
347         assertEquals(0, animation.getStartOffset());
348 
349         View animWindow = mActivity.findViewById(R.id.anim_window);
350         // XML file of R.anim.accelerate_alpha
351         // <alpha xmlns:android="http://schemas.android.com/apk/res/android"
352         //      android:interpolator="@android:anim/accelerate_interpolator"
353         //      android:fromAlpha="0.1"
354         //      android:toAlpha="0.9"
355         //      android:duration="1000" />
356         animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
357         animation.setStartOffset(startOffset);
358         assertEquals(startOffset, animation.getStartOffset());
359 
360         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
361                 animation, ACCELERATE_ALPHA_DURATION + startOffset);
362 
363         Transformation transformation = new Transformation();
364         long startTime = animation.getStartTime();
365         animation.getTransformation(startTime, transformation);
366         float alpha1 = transformation.getAlpha();
367         assertEquals(0.1f, alpha1, COMPARISON_DELTA);
368 
369         animation.getTransformation(startTime + 400, transformation);
370         float alpha2 = transformation.getAlpha();
371         // alpha is 0.1f during start offset
372         assertEquals(0.1f, alpha2, COMPARISON_DELTA);
373 
374         animation.getTransformation(startTime + startOffset, transformation);
375         float alpha3 = transformation.getAlpha();
376         // alpha is 0.1f during start offset
377         assertEquals(0.1f, alpha3, COMPARISON_DELTA);
378 
379         animation.getTransformation(startTime + startOffset + 1, transformation);
380         float alpha4 = transformation.getAlpha();
381         // alpha is lager than 0.1f after start offset
382         assertTrue(alpha4 > 0.1f);
383     }
384 
385     @Test
testRunAccelerateAlpha()386     public void testRunAccelerateAlpha() throws Throwable {
387         // check default startTime
388         Animation animation = new Animation() {
389         };
390         assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime());
391 
392         long currentTime = AnimationUtils.currentAnimationTimeMillis();
393         animation.setStartTime(currentTime);
394         assertEquals(currentTime, animation.getStartTime());
395 
396         View animWindow = mActivity.findViewById(R.id.anim_window);
397 
398         // XML file of R.anim.accelerate_alpha
399         // <alpha xmlns:android="http://schemas.android.com/apk/res/android"
400         //      android:interpolator="@android:anim/accelerate_interpolator"
401         //      android:fromAlpha="0.1"
402         //      android:toAlpha="0.9"
403         //      android:duration="1000" />
404         Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
405         assertEquals(Animation.START_ON_FIRST_FRAME, anim.getStartTime());
406         assertFalse(anim.hasStarted());
407         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim);
408     }
409 
410     @Test
testGetTransformation()411     public void testGetTransformation() throws Throwable {
412         final View animWindow = mActivity.findViewById(R.id.anim_window);
413 
414         // XML file of R.anim.accelerate_alpha
415         // <alpha xmlns:android="http://schemas.android.com/apk/res/android"
416         //      android:interpolator="@android:anim/accelerate_interpolator"
417         //      android:fromAlpha="0.1"
418         //      android:toAlpha="0.9"
419         //      android:duration="1000" />
420         final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
421         assertFalse(anim.hasStarted());
422 
423         // we have to PollingCheck the animation status on test thread,
424         // it cannot be done on UI thread, so we invoke runOnUiThread method here.
425         mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
426 
427         // check whether animation has started
428         PollingCheck.waitFor(anim::hasStarted);
429 
430         // check transformation objects that is provided by the
431         // caller and will be filled in by the animation.
432         Transformation transformation = new Transformation();
433         long startTime = anim.getStartTime();
434         assertTrue(anim.getTransformation(startTime, transformation));
435         float alpha1 = transformation.getAlpha();
436         assertEquals(0.1f, alpha1, COMPARISON_DELTA);
437 
438         assertTrue(anim.getTransformation(startTime + 250, transformation));
439         float alpha2 = transformation.getAlpha();
440 
441         assertTrue(anim.getTransformation(startTime + 500, transformation));
442         float alpha3 = transformation.getAlpha();
443 
444         assertTrue(anim.getTransformation(startTime + 750, transformation));
445         float alpha4 = transformation.getAlpha();
446 
447         // wait for animation has ended.
448         // timeout is larger than duration, in case the system is sluggish
449         PollingCheck.waitFor(2000, anim::hasEnded);
450 
451         assertFalse(anim.getTransformation(startTime + 1000, transformation));
452         float alpha5 = transformation.getAlpha();
453         assertEquals(0.9f, alpha5, COMPARISON_DELTA);
454 
455         // check decelerating delta alpha
456         float delta1 = alpha2 - alpha1;
457         float delta2 = alpha3 - alpha2;
458         float delta3 = alpha4 - alpha3;
459         float delta4 = alpha5 - alpha4;
460         assertTrue(delta1 < delta2);
461         assertTrue(delta2 < delta3);
462         assertTrue(delta3 < delta4);
463     }
464 
465     @Test
466     public void testAccessZAdjustment() {
467         // check default zAdjustment
468         Animation animation = new Animation() {
469         };
470         assertEquals(Animation.ZORDER_NORMAL, animation.getZAdjustment());
471 
472         animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
473         assertEquals(Animation.ZORDER_NORMAL, animation.getZAdjustment());
474 
475         animation.setZAdjustment(Animation.ZORDER_TOP);
476         assertEquals(Animation.ZORDER_TOP, animation.getZAdjustment());
477 
478         animation.setZAdjustment(Animation.ZORDER_BOTTOM);
479         assertEquals(Animation.ZORDER_BOTTOM, animation.getZAdjustment());
480     }
481 
482     @Test
483     public void testInitialize() {
484         Animation animation = new Animation() {
485         };
486 
487         assertFalse(animation.isInitialized());
488         animation.initialize(320, 480, 320, 480);
489         assertTrue(animation.isInitialized());
490     }
491 
492     @Test
493     public void testResolveSize() {
494         MyAnimation myAnimation = new MyAnimation();
495 
496         assertEquals(1.0f, myAnimation.resolveSize(Animation.ABSOLUTE, 1.0f, 0, 0),
497                 COMPARISON_DELTA);
498         assertEquals(2.0f, myAnimation.resolveSize(Animation.ABSOLUTE, 2.0f, 0, 0),
499                 COMPARISON_DELTA);
500 
501         assertEquals(6.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_SELF, 3.0f, 2, 0),
502                 COMPARISON_DELTA);
503         assertEquals(9.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_SELF, 3.0f, 3, 0),
504                 COMPARISON_DELTA);
505 
506         assertEquals(18.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_PARENT, 3.0f, 0, 6),
507                 COMPARISON_DELTA);
508         assertEquals(12.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_PARENT, 3.0f, 0, 4),
509                 COMPARISON_DELTA);
510 
511         int unknownType = 7;
512         assertEquals(8.0f, myAnimation.resolveSize(unknownType, 8.0f, 3, 4), COMPARISON_DELTA);
513         assertEquals(10.0f, myAnimation.resolveSize(unknownType, 10.0f, 3, 4), COMPARISON_DELTA);
514     }
515 
516     @Test
517     public void testRestrictDuration() {
518         Animation animation = new Animation() {
519         };
520 
521         animation.setStartOffset(1000);
522         animation.restrictDuration(500);
523         assertEquals(500, animation.getStartOffset());
524         assertEquals(0, animation.getDuration());
525         assertEquals(0, animation.getRepeatCount());
526 
527         animation.setStartOffset(1000);
528         animation.setDuration(1000);
529         animation.restrictDuration(1500);
530         assertEquals(500, animation.getDuration());
531 
532         animation.setStartOffset(1000);
533         animation.setDuration(1000);
534         animation.setRepeatCount(3);
535         animation.restrictDuration(4500);
536         assertEquals(1, animation.getRepeatCount());
537     }
538 
539     @Test
540     public void testScaleCurrentDuration() {
541         Animation animation = new Animation() {
542         };
543 
544         animation.setDuration(10);
545         animation.scaleCurrentDuration(0);
546         assertEquals(0, animation.getDuration());
547 
548         animation.setDuration(10);
549         animation.scaleCurrentDuration(2);
550         assertEquals(20, animation.getDuration());
551 
552         animation.setDuration(10);
553         animation.scaleCurrentDuration(-1);
554         assertEquals(-10, animation.getDuration());
555     }
556 
557     @LargeTest
558     @Test
559     public void testSetAnimationListener() throws Throwable {
560         final View animWindow = mActivity.findViewById(R.id.anim_window);
561 
562         // XML file of R.anim.accelerate_alpha
563         // <alpha xmlns:android="http://schemas.android.com/apk/res/android"
564         //      android:interpolator="@android:anim/accelerate_interpolator"
565         //      android:fromAlpha="0.1"
566         //      android:toAlpha="0.9"
567         //      android:duration="1000" />
568         final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
569         final AnimationListener listener = mock(AnimationListener.class);
570         anim.setAnimationListener(listener);
571         verifyZeroInteractions(listener);
572 
573         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim);
574         verify(listener, times(1)).onAnimationStart(anim);
575         verify(listener, times(1)).onAnimationEnd(anim);
576         verify(listener, never()).onAnimationRepeat(anim);
577 
578         reset(listener);
579         anim.setRepeatCount(2);
580         anim.setRepeatMode(Animation.REVERSE);
581 
582         AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim,
583                 3 * ACCELERATE_ALPHA_DURATION);
584         verify(listener, times(1)).onAnimationStart(anim);
585         verify(listener, times(2)).onAnimationRepeat(anim);
586         verify(listener, times(1)).onAnimationEnd(anim);
587 
588         reset(listener);
589         // onAnimationEnd will not be invoked and animation should not end
590         anim.setRepeatCount(Animation.INFINITE);
591 
592         mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
593         // Verify that our animation doesn't call listener's onAnimationEnd even after a long
594         // period of time. We just sleep and then verify what's happened with the listener.
595         SystemClock.sleep(4 * ACCELERATE_ALPHA_DURATION);
596 
597         verify(listener, times(1)).onAnimationStart(anim);
598         verify(listener, atLeastOnce()).onAnimationRepeat(anim);
599         verify(listener, never()).onAnimationEnd(anim);
600     }
601 
602     @Test
603     public void testStart() {
604         Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
605         animation.setStartTime(0);
606 
607         animation.start();
608         assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime());
609     }
610 
611     @Test
612     public void testStartNow() {
613         Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
614         animation.setStartTime(0);
615 
616         long currentTime = AnimationUtils.currentAnimationTimeMillis();
617         animation.startNow();
618         assertEquals(currentTime, animation.getStartTime(), 100);
619     }
620 
621     @Test
622     public void testWillChangeBounds() {
623         Animation animation = new Animation() {
624         };
625 
626         assertTrue(animation.willChangeBounds());
627     }
628 
629     @Test
630     public void testWillChangeTransformationMatrix() {
631         Animation animation = new Animation() {
632         };
633 
634         assertTrue(animation.willChangeTransformationMatrix());
635     }
636 
637     @Test
638     public void testClone() throws CloneNotSupportedException {
639         MyAnimation myAnimation = new MyAnimation();
640         myAnimation.setDuration(3000);
641         myAnimation.setFillAfter(true);
642         myAnimation.setFillBefore(false);
643         myAnimation.setFillEnabled(true);
644         myAnimation.setStartTime(1000);
645         myAnimation.setRepeatCount(10);
646         myAnimation.setRepeatMode(Animation.REVERSE);
647 
648         Animation cloneAnimation = myAnimation.clone();
649         assertNotSame(myAnimation, cloneAnimation);
650         assertEquals(myAnimation.getDuration(), cloneAnimation.getDuration());
651         assertEquals(myAnimation.getFillAfter(), cloneAnimation.getFillAfter());
652         assertEquals(myAnimation.getFillBefore(), cloneAnimation.getFillBefore());
653         assertEquals(myAnimation.isFillEnabled(), cloneAnimation.isFillEnabled());
654         assertEquals(myAnimation.getStartOffset(), cloneAnimation.getStartOffset());
655         assertEquals(myAnimation.getRepeatCount(), cloneAnimation.getRepeatCount());
656         assertEquals(myAnimation.getRepeatMode(), cloneAnimation.getRepeatMode());
657     }
658 
659     @Test
660     public void testCancelImmediately() throws Throwable {
661         MyAnimation anim = new MyAnimation();
662         final CountDownLatch latch1 = new CountDownLatch(1);
663         runCanceledAnimation(anim, latch1, false, false);
664         assertTrue(latch1.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
665         assertFalse(anim.isStillAnimating());
666     }
667 
668     @Test
669     public void testRepeatingCancelImmediately() throws Throwable {
670         MyAnimation anim = new MyAnimation();
671         final CountDownLatch latch2 = new CountDownLatch(1);
672         runCanceledAnimation(anim, latch2, true, false);
673         assertTrue(latch2.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
674         assertFalse(anim.isStillAnimating());
675     }
676 
677     @Test
678     public void testCancelDelayed() throws Throwable {
679         MyAnimation anim = new MyAnimation();
680         final CountDownLatch latch3 = new CountDownLatch(1);
681         runCanceledAnimation(anim, latch3, false, true);
682         assertTrue(latch3.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
683         assertFalse(anim.isStillAnimating());
684     }
685 
686     @Test
687     public void testRepeatingCancelDelayed() throws Throwable {
688         MyAnimation anim = new MyAnimation();
689         final CountDownLatch latch4 = new CountDownLatch(1);
690         runCanceledAnimation(anim, latch4, true, true);
691         assertTrue(latch4.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
692         assertFalse(anim.isStillAnimating());
693     }
694 
695     private void runCanceledAnimation(final MyAnimation anim, final CountDownLatch latch,
696             final boolean repeating, final boolean delayed) throws Throwable {
697         // The idea behind this test is that canceling an Animation should result in
698         // it ending, which means not having its getTransformation() method called
699         // anymore. The trick is that cancel() will still allow one more frame to run,
700         // so we have to insert some delay between when we cancel and when we can check
701         // whether it is still animating.
702         final View view = mActivity.findViewById(R.id.anim_window);
703         mActivityRule.runOnUiThread(() -> {
704             anim.setDuration(delayed ? 300 : 200);
705             if (repeating) {
706                 anim.setRepeatCount(Animation.INFINITE);
707             }
708             view.startAnimation(anim);
709             if (!delayed) {
710                 anim.cancel();
711             } else {
712                 view.postDelayed(anim::cancel, 100);
713             }
714             view.postDelayed(() -> {
715                 anim.setStillAnimating(false);
716                 view.postDelayed(latch::countDown, 200);
717             }, delayed ? 300 : 200);
718         });
719     }
720 
721     private class MyAnimation extends Animation {
722         boolean mStillAnimating = false;
723 
724         @Override
ensureInterpolator()725         protected void ensureInterpolator() {
726             super.ensureInterpolator();
727         }
728 
729         @Override
resolveSize(int type, float value, int size, int parentSize)730         protected float resolveSize(int type, float value, int size, int parentSize) {
731             return super.resolveSize(type, value, size, parentSize);
732         }
733 
734         @Override
clone()735         protected Animation clone() throws CloneNotSupportedException {
736             return super.clone();
737         }
738 
setStillAnimating(boolean value)739         public void setStillAnimating(boolean value) {
740             mStillAnimating = value;
741         }
742 
isStillAnimating()743         public boolean isStillAnimating() {
744             return mStillAnimating;
745         }
746 
747         @Override
getTransformation(long currentTime, Transformation outTransformation)748         public boolean getTransformation(long currentTime, Transformation outTransformation) {
749             mStillAnimating = true;
750             return super.getTransformation(currentTime, outTransformation);
751         }
752     }
753 }
754