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