1/*
2* Copyright (C) 2015 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*/
16package android.animation;
17
18import android.os.Handler;
19import android.os.Looper;
20import android.os.Message;
21import android.os.SystemClock;
22import android.test.ActivityInstrumentationTestCase2;
23import android.test.suitebuilder.annotation.SmallTest;
24import android.view.Choreographer;
25import android.view.animation.LinearInterpolator;
26
27import java.util.ArrayList;
28
29import static android.test.MoreAsserts.assertNotEqual;
30
31public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
32    private static final long WAIT_TIME_OUT = 5000;
33    private ValueAnimator a1;
34    private ValueAnimator a2;
35
36    // Tolerance of error in calculations related to duration, frame time, etc. due to frame delay.
37    private final static long TOLERANCE = 100; // ms
38    private final static long POLL_INTERVAL = 100; // ms
39
40    private final static float A1_START_VALUE = 0f;
41    private final static float A1_END_VALUE = 1f;
42    private final static int A2_START_VALUE = 100;
43    private final static int A2_END_VALUE = 200;
44
45    private final static long DEFAULT_FRAME_INTERVAL = 5; //ms
46    private final static long COMMIT_DELAY = 3; //ms
47
48    public ValueAnimatorTests() {
49        super(BasicAnimatorActivity.class);
50    }
51
52    @Override
53    public void setUp() throws Exception {
54        super.setUp();
55        a1 = ValueAnimator.ofFloat(A1_START_VALUE, A1_END_VALUE).setDuration(300);
56        a2 = ValueAnimator.ofInt(A2_START_VALUE, A2_END_VALUE).setDuration(500);
57    }
58
59    @Override
60    public void tearDown() throws Exception {
61        a1 = null;
62        a2 = null;
63        super.tearDown();
64    }
65
66    @SmallTest
67    public void testStartDelay() throws Throwable {
68        final ValueAnimator a = ValueAnimator.ofFloat(5f, 20f);
69        assertEquals(a.getStartDelay(), 0);
70        final long delay = 200;
71        a.setStartDelay(delay);
72        assertEquals(a.getStartDelay(), delay);
73
74        final MyUpdateListener listener = new MyUpdateListener();
75        a.addUpdateListener(listener);
76        final long[] startTime = new long[1];
77
78        runTestOnUiThread(new Runnable() {
79            @Override
80            public void run() {
81                // Test the time between isRunning() and isStarted()
82                assertFalse(a.isStarted());
83                assertFalse(a.isRunning());
84                a.start();
85                startTime[0] = SystemClock.uptimeMillis();
86                assertTrue(a.isStarted());
87                assertFalse(a.isRunning());
88            }
89        });
90
91        Thread.sleep(a.getTotalDuration());
92        runTestOnUiThread(new Runnable() {
93            @Override
94            public void run() {
95                assertTrue(listener.wasRunning);
96                assertTrue(listener.firstRunningFrameTime - startTime[0] >= delay);
97            }
98        });
99
100        Thread.sleep(a.getTotalDuration());
101        runTestOnUiThread(new Runnable() {
102            @Override
103            public void run() {
104                assertFalse(a.isStarted());
105            }
106        });
107    }
108
109    @SmallTest
110    public void testListenerCallbacks() throws Throwable {
111        final MyListener l1 = new MyListener();
112        final MyListener l2 = new MyListener();
113        a1.addListener(l1);
114        a2.addListener(l2);
115        a2.setStartDelay(400);
116
117        assertFalse(l1.startCalled);
118        assertFalse(l1.cancelCalled);
119        assertFalse(l1.endCalled);
120        assertFalse(l2.startCalled);
121        assertFalse(l2.cancelCalled);
122        assertFalse(l2.endCalled);
123
124        runTestOnUiThread(new Runnable() {
125            @Override
126            public void run() {
127                a1.start();
128                a2.start();
129            }
130        });
131
132        long wait = 0;
133        Thread.sleep(POLL_INTERVAL);
134        wait += POLL_INTERVAL;
135
136        runTestOnUiThread(new Runnable() {
137            @Override
138            public void run() {
139                assertFalse(l1.cancelCalled);
140                a1.cancel();
141                assertTrue(l1.cancelCalled);
142                assertTrue(l1.endCalled);
143            }
144        });
145
146        while (wait < a2.getStartDelay()) {
147            runTestOnUiThread(new Runnable() {
148                @Override
149                public void run() {
150                    // Make sure a2's start listener isn't called during start delay.
151                    assertTrue(l1.startCalled);
152                    assertFalse(l2.startCalled);
153                }
154            });
155            Thread.sleep(POLL_INTERVAL);
156            wait += POLL_INTERVAL;
157        }
158
159        long delay = Math.max(a1.getTotalDuration(), a2.getTotalDuration()) + TOLERANCE;
160        Thread.sleep(delay);
161
162        runTestOnUiThread(new Runnable() {
163            @Override
164            public void run() {
165                // a1 is canceled.
166                assertTrue(l1.startCalled);
167                assertTrue(l1.cancelCalled);
168                assertTrue(l1.endCalled);
169
170                // a2 is supposed to finish normally
171                assertTrue(l2.startCalled);
172                assertFalse(l2.cancelCalled);
173                assertTrue(l2.endCalled);
174            }
175        });
176    }
177
178    @SmallTest
179    public void testIsStarted() throws Throwable {
180        assertFalse(a1.isStarted());
181        assertFalse(a2.isStarted());
182        assertFalse(a1.isRunning());
183        assertFalse(a2.isRunning());
184        final long startDelay = 150;
185        a1.setStartDelay(startDelay);
186        final long[] startTime = new long[1];
187
188        runTestOnUiThread(new Runnable() {
189            @Override
190            public void run() {
191                a1.start();
192                a2.start();
193                startTime[0] = SystemClock.uptimeMillis();
194                assertTrue(a1.isStarted());
195                assertTrue(a2.isStarted());
196            }
197        });
198        long delayMs = 0;
199        while (delayMs < startDelay) {
200            Thread.sleep(POLL_INTERVAL);
201            delayMs += POLL_INTERVAL;
202            runTestOnUiThread(new Runnable() {
203                @Override
204                public void run() {
205                    if (SystemClock.uptimeMillis() - startTime[0] < startDelay) {
206                        assertFalse(a1.isRunning());
207                    }
208                }
209            });
210        }
211
212        Thread.sleep(startDelay);
213        runTestOnUiThread(new Runnable() {
214            @Override
215            public void run() {
216                assertTrue(a1.isRunning());
217                assertTrue(a2.isRunning());
218            }
219        });
220
221        long delay = Math.max(a1.getTotalDuration(), a2.getTotalDuration()) * 2;
222        Thread.sleep(delay);
223        runTestOnUiThread(new Runnable() {
224            @Override
225            public void run() {
226                assertFalse(a1.isStarted());
227                assertFalse(a1.isRunning());
228                assertFalse(a2.isStarted());
229                assertFalse(a2.isRunning());
230            }
231        });
232    }
233
234    @SmallTest
235    public void testPause() throws Throwable {
236        runTestOnUiThread(new Runnable() {
237            @Override
238            public void run() {
239                assertFalse(a1.isPaused());
240                assertFalse(a2.isPaused());
241
242                a1.start();
243                a2.start();
244
245                assertFalse(a1.isPaused());
246                assertFalse(a2.isPaused());
247                assertTrue(a1.isStarted());
248                assertTrue(a2.isStarted());
249            }
250        });
251
252        Thread.sleep(POLL_INTERVAL);
253        runTestOnUiThread(new Runnable() {
254            @Override
255            public void run() {
256                assertTrue(a1.isRunning());
257                assertTrue(a2.isRunning());
258                a1.pause();
259                assertTrue(a1.isPaused());
260                assertFalse(a2.isPaused());
261                assertTrue(a1.isRunning());
262            }
263        });
264
265        Thread.sleep(a2.getTotalDuration());
266        runTestOnUiThread(new Runnable() {
267            @Override
268            public void run() {
269                // By this time, a2 should have finished, and a1 is still paused
270                assertFalse(a2.isStarted());
271                assertFalse(a2.isRunning());
272                assertTrue(a1.isStarted());
273                assertTrue(a1.isRunning());
274                assertTrue(a1.isPaused());
275
276                a1.resume();
277            }
278        });
279
280        Thread.sleep(POLL_INTERVAL);
281        runTestOnUiThread(new Runnable() {
282            @Override
283            public void run() {
284                assertTrue(a1.isRunning());
285                assertTrue(a1.isStarted());
286                assertFalse(a1.isPaused());
287            }
288        });
289
290        Thread.sleep(a1.getTotalDuration());
291        runTestOnUiThread(new Runnable() {
292            @Override
293            public void run() {
294                // a1 should finish by now.
295                assertFalse(a1.isRunning());
296                assertFalse(a1.isStarted());
297                assertFalse(a1.isPaused());
298            }
299        });
300
301    }
302
303    @SmallTest
304    public void testPauseListener() throws Throwable {
305        MyPauseListener l1 = new MyPauseListener();
306        MyPauseListener l2 = new MyPauseListener();
307        a1.addPauseListener(l1);
308        a2.addPauseListener(l2);
309
310        assertFalse(l1.pauseCalled);
311        assertFalse(l1.resumeCalled);
312        assertFalse(l2.pauseCalled);
313        assertFalse(l2.resumeCalled);
314
315        runTestOnUiThread(new Runnable() {
316            @Override
317            public void run() {
318                a1.start();
319                a2.start();
320            }
321        });
322
323        Thread.sleep(a1.getTotalDuration() / 2);
324        a1.pause();
325
326        Thread.sleep(a2.getTotalDuration());
327
328        // Only a1's pause listener should be called.
329        assertTrue(l1.pauseCalled);
330        assertFalse(l1.resumeCalled);
331        runTestOnUiThread(new Runnable() {
332            @Override
333            public void run() {
334                a1.resume();
335            }
336        });
337
338        Thread.sleep(a1.getTotalDuration());
339
340        assertTrue(l1.pauseCalled);
341        assertTrue(l1.resumeCalled);
342        assertFalse(l2.pauseCalled);
343        assertFalse(l2.resumeCalled);
344    }
345
346    @SmallTest
347    public void testResume() throws Throwable {
348        final MyUpdateListener l1 = new MyUpdateListener();
349        final long totalDuration = a1.getTotalDuration();
350        a1.addUpdateListener(l1);
351        // Set a longer duration on a1 for this test
352        a1.setDuration(1000);
353        assertTrue(l1.firstRunningFrameTime < 0);
354        assertTrue(l1.lastUpdateTime < 0);
355
356        final long[] lastUpdate = new long[1];
357
358        runTestOnUiThread(new Runnable() {
359            @Override
360            public void run() {
361                a1.start();
362            }
363        });
364
365        Thread.sleep(totalDuration / 2);
366
367        runTestOnUiThread(new Runnable() {
368            @Override
369            public void run() {
370                assertTrue(l1.firstRunningFrameTime > 0);
371                assertTrue(l1.lastUpdateTime > l1.firstRunningFrameTime);
372                lastUpdate[0] = l1.lastUpdateTime;
373                a1.pause();
374            }
375        });
376
377        Thread.sleep(totalDuration);
378
379        runTestOnUiThread(new Runnable() {
380            @Override
381            public void run() {
382                // There should be no update after pause()
383                assertEquals(lastUpdate[0], l1.lastUpdateTime);
384                a1.resume();
385            }
386        });
387
388        do {
389            Thread.sleep(POLL_INTERVAL);
390            runTestOnUiThread(new Runnable() {
391                @Override
392                public void run() {
393                    assertTrue(l1.lastUpdateTime > lastUpdate[0]);
394                    lastUpdate[0] = l1.lastUpdateTime;
395                }
396            });
397        } while (!a1.isStarted());
398
399        // Time between pause and resume: totalDuration
400        long entireSpan = totalDuration * 2;
401        long frameDelta = l1.lastUpdateTime - l1.firstRunningFrameTime;
402        assertTrue(Math.abs(entireSpan - frameDelta) < TOLERANCE);
403    }
404
405    @SmallTest
406    public void testEnd() throws Throwable {
407        final MyListener l1 = new MyListener();
408        final MyListener l2 = new MyListener();
409        a1.addListener(l1);
410        a2.addListener(l2);
411        a1.addListener(new MyListener() {
412            @Override
413            public void onAnimationEnd(Animator anim) {
414                anim.cancel();
415            }
416        });
417        a2.addListener(new MyListener() {
418            @Override
419            public void onAnimationCancel(Animator anim) {
420                anim.end();
421            }
422        });
423
424        runTestOnUiThread(new Runnable() {
425            @Override
426            public void run() {
427                assertFalse(l1.cancelCalled);
428                assertFalse(l1.endCalled);
429                assertFalse(l2.cancelCalled);
430                assertFalse(l2.endCalled);
431                a1.start();
432                a2.start();
433            }
434        });
435        Thread.sleep(POLL_INTERVAL);
436        runTestOnUiThread(new Runnable() {
437            @Override
438            public void run() {
439                a1.end();
440                a2.cancel();
441            }
442        });
443        Thread.sleep(POLL_INTERVAL);
444        runTestOnUiThread(new Runnable() {
445            @Override
446            public void run() {
447                // Calling cancel from onAnimationEnd will be ignored.
448                assertFalse(l1.cancelCalled);
449                assertTrue(l1.endCalled);
450                assertTrue(l2.cancelCalled);
451                assertTrue(l2.endCalled);
452
453                float value1 = (Float) a1.getAnimatedValue();
454                int value2 = (Integer) a2.getAnimatedValue();
455                assertEquals(A1_END_VALUE, value1);
456                assertEquals(A2_END_VALUE, value2);
457            }
458        });
459
460    }
461
462    @SmallTest
463    public void testEndValue() throws Throwable {
464        final MyListener l1 = new MyListener();
465        a1.addListener(l1);
466
467        final MyListener l2 = new MyListener();
468        a2.addListener(l2);
469
470        runTestOnUiThread(new Runnable() {
471            @Override
472            public void run() {
473                a1.start();
474                a2.start();
475            }
476        });
477
478        Thread.sleep(POLL_INTERVAL);
479        runTestOnUiThread(new Runnable() {
480            @Override
481            public void run() {
482                // Animation has started but not finished, check animated values against end values
483                assertFalse(l1.endCalled);
484                assertFalse(l2.endCalled);
485                assertNotEqual(A1_END_VALUE, a1.getAnimatedValue());
486                assertNotEqual(A1_END_VALUE, a2.getAnimatedValue());
487
488                // Force a2 to end.
489                a2.end();
490            }
491        });
492
493        Thread.sleep(a1.getTotalDuration());
494
495        runTestOnUiThread(new Runnable() {
496            @Override
497            public void run() {
498                assertFalse(l1.cancelCalled);
499                assertTrue(l1.endCalled);
500                assertFalse(l2.cancelCalled);
501                assertTrue(l2.endCalled);
502
503                // By now a1 should have finished normally and a2 has skipped to the end, check
504                // their end values.
505                assertEquals(A1_END_VALUE, ((Float) (a1.getAnimatedValue())).floatValue());
506                assertEquals(A2_END_VALUE, ((Integer) (a2.getAnimatedValue())).intValue());
507            }
508        });
509    }
510
511    @SmallTest
512    public void testUpdateListener() throws InterruptedException {
513
514        final MyFrameCallbackProvider provider = new MyFrameCallbackProvider();
515        long sleep = 0;
516        while (provider.mHandler == null) {
517            Thread.sleep(POLL_INTERVAL);
518            sleep += POLL_INTERVAL;
519            if (sleep > WAIT_TIME_OUT) {
520                break;
521            }
522        }
523        // Either the looper has started, or timed out
524        assertNotNull(provider.mHandler);
525
526        final MyListener listener = new MyListener();
527        final MyUpdateListener l1 = new MyUpdateListener() {
528            @Override
529            public void onAnimationUpdate(ValueAnimator animation) {
530                long currentTime = SystemClock.uptimeMillis();
531                long frameDelay = provider.getFrameDelay();
532                if (lastUpdateTime > 0) {
533                    // Error tolerance here is one frame.
534                    assertTrue((currentTime - lastUpdateTime) < frameDelay * 2);
535                } else {
536                    // First frame:
537                    assertTrue(listener.startCalled);
538                    assertTrue(listener.startTime > 0);
539                    assertTrue(currentTime - listener.startTime < frameDelay * 2);
540                }
541                super.onAnimationUpdate(animation);
542            }
543        };
544        a1.addUpdateListener(l1);
545        a1.addListener(listener);
546        a1.setStartDelay(100);
547
548        provider.mHandler.post(new Runnable() {
549            @Override
550            public void run() {
551                AnimationHandler.getInstance().setProvider(provider);
552                a1.start();
553            }
554        });
555        Thread.sleep(POLL_INTERVAL);
556        assertTrue(a1.isStarted());
557        Thread.sleep(a1.getTotalDuration() + TOLERANCE);
558        // Finished by now.
559        assertFalse(a1.isStarted());
560        assertTrue(listener.endTime > 0);
561
562        // Check the time difference between last frame and end time.
563        assertTrue(listener.endTime >= l1.lastUpdateTime);
564        assertTrue(listener.endTime - l1.lastUpdateTime < 2 * provider.getFrameDelay());
565    }
566
567
568    @SmallTest
569    public void testConcurrentModification() throws Throwable {
570        // Attempt to modify list of animations as the list is being iterated
571        final ValueAnimator a0 = ValueAnimator.ofInt(100, 200).setDuration(500);
572        final ValueAnimator a3 = ValueAnimator.ofFloat(0, 1).setDuration(500);
573        final ValueAnimator a4 = ValueAnimator.ofInt(200, 300).setDuration(500);
574        final MyListener listener = new MyListener() {
575            @Override
576            public void onAnimationEnd(Animator anim) {
577                super.onAnimationEnd(anim);
578                // AnimationHandler should be iterating the list at the moment, end/cancel all
579                // the other animations. No ConcurrentModificationException should happen.
580                a0.cancel();
581                a1.end();
582                a3.end();
583                a4.cancel();
584            }
585        };
586        a2.addListener(listener);
587
588        runTestOnUiThread(new Runnable() {
589            @Override
590            public void run() {
591                a0.start();
592                a1.start();
593                a2.start();
594                a3.start();
595                a4.start();
596            }
597        });
598        runTestOnUiThread(new Runnable() {
599            @Override
600            public void run() {
601                assertTrue(a0.isStarted());
602                assertTrue(a1.isStarted());
603                assertTrue(a2.isStarted());
604                assertTrue(a3.isStarted());
605                assertTrue(a4.isStarted());
606            }
607        });
608        Thread.sleep(POLL_INTERVAL);
609        runTestOnUiThread(new Runnable() {
610            @Override
611            public void run() {
612                // End the animator that should be in the middle of the list.
613                a2.end();
614            }
615        });
616        Thread.sleep(POLL_INTERVAL);
617        assertTrue(listener.endCalled);
618        assertFalse(a0.isStarted());
619        assertFalse(a1.isStarted());
620        assertFalse(a2.isStarted());
621        assertFalse(a3.isStarted());
622        assertFalse(a4.isStarted());
623    }
624
625    @SmallTest
626    public void testSeek() throws Throwable {
627        final MyListener l1 = new MyListener();
628        final MyListener l2 = new MyListener();
629        final MyUpdateListener updateListener1 = new MyUpdateListener();
630        final MyUpdateListener updateListener2 = new MyUpdateListener();
631        final float a1StartFraction = 0.2f;
632        final float a2StartFraction = 0.3f;
633
634        // Extend duration so we have plenty of latitude to manipulate the animations when they
635        // are running.
636        a1.setDuration(1000);
637        a2.setDuration(1000);
638        a1.addListener(l1);
639        a2.addListener(l2);
640        a1.addUpdateListener(updateListener1);
641        a2.addUpdateListener(updateListener2);
642        TimeInterpolator interpolator = new LinearInterpolator();
643        a1.setInterpolator(interpolator);
644        a2.setInterpolator(interpolator);
645
646        runTestOnUiThread(new Runnable() {
647            @Override
648            public void run() {
649                assertFalse(a1.isStarted());
650                assertFalse(a1.isRunning());
651                assertFalse(a2.isStarted());
652                assertFalse(a2.isRunning());
653
654                // Test isRunning() and isStarted() before and after seek
655                a1.setCurrentFraction(a1StartFraction);
656                a2.setCurrentFraction(a2StartFraction);
657
658                assertFalse(a1.isStarted());
659                assertFalse(a1.isRunning());
660                assertFalse(a2.isStarted());
661                assertFalse(a2.isRunning());
662            }
663        });
664        Thread.sleep(POLL_INTERVAL);
665
666        // Start animation and seek during the animation.
667        runTestOnUiThread(new Runnable() {
668            @Override
669            public void run() {
670                assertFalse(a1.isStarted());
671                assertFalse(a1.isRunning());
672                assertFalse(a2.isStarted());
673                assertFalse(a2.isRunning());
674                assertEquals(a1StartFraction, a1.getAnimatedFraction());
675                assertEquals(a2StartFraction, a2.getAnimatedFraction());
676
677                a1.start();
678                a2.start();
679            }
680        });
681
682        Thread.sleep(POLL_INTERVAL);
683        final float halfwayFraction = 0.5f;
684        runTestOnUiThread(new Runnable() {
685            @Override
686            public void run() {
687                assertTrue(l1.startCalled);
688                assertTrue(l2.startCalled);
689                assertFalse(l1.endCalled);
690                assertFalse(l2.endCalled);
691
692                // Check whether the animations start from the seeking fraction
693                assertTrue(updateListener1.startFraction >= a1StartFraction);
694                assertTrue(updateListener2.startFraction >= a2StartFraction);
695
696                assertTrue(a1.isStarted());
697                assertTrue(a1.isRunning());
698                assertTrue(a2.isStarted());
699                assertTrue(a2.isRunning());
700
701                a1.setCurrentFraction(halfwayFraction);
702                a2.setCurrentFraction(halfwayFraction);
703            }
704        });
705
706        Thread.sleep(POLL_INTERVAL);
707
708        // Check that seeking during running doesn't change animation's internal state
709        runTestOnUiThread(new Runnable() {
710            @Override
711            public void run() {
712                assertTrue(l1.startCalled);
713                assertTrue(l2.startCalled);
714                assertFalse(l1.endCalled);
715                assertFalse(l2.endCalled);
716
717                assertTrue(a1.isStarted());
718                assertTrue(a1.isRunning());
719                assertTrue(a2.isStarted());
720                assertTrue(a2.isRunning());
721            }
722        });
723
724        // Wait until the animators finish successfully.
725        long wait = Math.max(a1.getTotalDuration(), a2.getTotalDuration());
726        Thread.sleep(wait);
727
728        runTestOnUiThread(new Runnable() {
729            @Override
730            public void run() {
731                // Verify that the animators have finished.
732                assertTrue(l1.endCalled);
733                assertTrue(l2.endCalled);
734
735                assertFalse(a1.isStarted());
736                assertFalse(a2.isStarted());
737                assertFalse(a1.isRunning());
738                assertFalse(a2.isRunning());
739            }
740        });
741
742        // Re-start animator a1 after it ends normally, and check that seek value from last run
743        // does not affect the new run.
744        updateListener1.reset();
745        runTestOnUiThread(new Runnable() {
746            @Override
747            public void run() {
748                a1.start();
749            }
750        });
751
752        Thread.sleep(POLL_INTERVAL);
753        runTestOnUiThread(new Runnable() {
754            @Override
755            public void run() {
756                assertTrue(updateListener1.wasRunning);
757                assertTrue(updateListener1.startFraction >= 0);
758                assertTrue(updateListener1.startFraction < halfwayFraction);
759                a1.end();
760            }
761        });
762
763    }
764
765    @SmallTest
766    public void testSeekWhileRunning() throws Throwable {
767        // Seek one animator to the beginning and the other one to the end when they are running.
768        final MyListener l1 = new MyListener();
769        final MyListener l2 = new MyListener();
770        a1.addListener(l1);
771        a2.addListener(l2);
772        runTestOnUiThread(new Runnable() {
773            @Override
774            public void run() {
775                assertFalse(l1.startCalled);
776                assertFalse(l2.startCalled);
777                assertEquals(0f, a1.getAnimatedFraction());
778                assertEquals(0f, a2.getAnimatedFraction());
779                a1.start();
780                a2.start();
781            }
782        });
783        Thread.sleep(POLL_INTERVAL);
784        runTestOnUiThread(new Runnable() {
785            @Override
786            public void run() {
787                assertFalse(l1.endCalled);
788                assertFalse(l2.endCalled);
789                assertTrue(a1.isRunning());
790                assertTrue(a2.isRunning());
791                // During the run, seek one to the beginning, the other to the end
792                a1.setCurrentFraction(0f);
793                a2.setCurrentFraction(1f);
794            }
795        });
796        Thread.sleep(POLL_INTERVAL);
797        runTestOnUiThread(new Runnable() {
798            @Override
799            public void run() {
800                // Check that a2 has finished due to the seeking, but a1 hasn't finished.
801                assertFalse(l1.endCalled);
802                assertTrue(l2.endCalled);
803                assertEquals(1f, a2.getAnimatedFraction());
804            }
805        });
806
807        Thread.sleep(a1.getTotalDuration());
808        runTestOnUiThread(new Runnable() {
809            @Override
810            public void run() {
811                // By now a1 should finish also.
812                assertTrue(l1.endCalled);
813                assertEquals(1f, a1.getAnimatedFraction());
814            }
815        });
816    }
817
818    @SmallTest
819    public void testEndBeforeStart() throws Throwable {
820        // This test calls two animators that are not yet started. One animator has completed a
821        // previous run but hasn't started since then, the other one has never run. When end() is
822        // called on these two animators, we expected their animation listeners to receive both
823        // onAnimationStarted(Animator) and onAnimationEnded(Animator) callbacks, in that sequence.
824
825        a1.setStartDelay(20);
826
827        // First start a1's first run.
828        final MyListener normalEndingListener = new MyListener();
829        a1.addListener(normalEndingListener);
830        runTestOnUiThread(new Runnable() {
831            @Override
832            public void run() {
833                assertFalse(a1.isStarted());
834                assertFalse(normalEndingListener.startCalled);
835                assertFalse(normalEndingListener.endCalled);
836                // Start normally
837                a1.start();
838            }
839        });
840
841        Thread.sleep(a1.getTotalDuration() + POLL_INTERVAL);
842
843        // a1 should have finished by now.
844        runTestOnUiThread(new Runnable() {
845            @Override
846            public void run() {
847                // Call end() on both a1 and a2 without calling start()
848                final MyListener l1 = new MyListener();
849                a1.addListener(l1);
850                final MyListener l2 = new MyListener();
851                a2.addListener(l2);
852
853                assertFalse(a1.isStarted());
854                assertFalse(l1.startCalled);
855                assertFalse(l1.endCalled);
856                assertFalse(a2.isStarted());
857                assertFalse(l2.startCalled);
858                assertFalse(l1.endCalled);
859
860                a1.end();
861                a2.end();
862
863                // Check that both animators' listeners have received the animation callbacks.
864                assertTrue(l1.startCalled);
865                assertTrue(l1.endCalled);
866                assertFalse(a1.isStarted());
867                assertTrue(l1.endTime >= l1.startTime);
868
869                assertTrue(l2.startCalled);
870                assertTrue(l2.endCalled);
871                assertFalse(a2.isStarted());
872                assertTrue(l2.endTime >= l1.startTime);
873            }
874        });
875    }
876
877    @SmallTest
878    public void testZeroDuration() throws Throwable {
879        // Run two animators with zero duration, with one running forward and the other one
880        // backward. Check that the animations start and finish with the correct end fractions.
881        a1.setDuration(0);
882        a2.setDuration(0);
883
884        // Set a fraction on an animation with 0-duration
885        final ValueAnimator a3 = ValueAnimator.ofInt(0, 100);
886        a3.setDuration(0);
887        a3.setCurrentFraction(1.0f);
888        assertEquals(1.0f, a3.getAnimatedFraction());
889
890        final MyListener l1 = new MyListener();
891        final MyListener l2 = new MyListener();
892        final MyListener l3 = new MyListener();
893        a1.addListener(l1);
894        a2.addListener(l2);
895        a3.addListener(l3);
896        runTestOnUiThread(new Runnable() {
897            @Override
898            public void run() {
899                assertFalse(l1.startCalled);
900                assertFalse(l2.startCalled);
901                assertFalse(l3.startCalled);
902                assertFalse(l1.endCalled);
903                assertFalse(l2.endCalled);
904                assertFalse(l3.endCalled);
905                a1.start();
906                a2.reverse();
907                a3.start();
908                // Check that the animators' values are immediately set to end value in the case of
909                // 0-duration.
910                assertEquals(A1_END_VALUE, a1.getAnimatedValue());
911                assertEquals(A2_START_VALUE, a2.getAnimatedValue());
912            }
913        });
914        Thread.sleep(POLL_INTERVAL);
915        runTestOnUiThread(new Runnable() {
916            @Override
917            public void run() {
918                // Check that the animators have started and finished with the right values.
919                assertTrue(l1.startCalled);
920                assertTrue(l2.startCalled);
921                assertTrue(l3.startCalled);
922                assertTrue(l1.endCalled);
923                assertTrue(l2.endCalled);
924                assertTrue(l3.endCalled);
925                assertEquals(1.0f, a1.getAnimatedFraction());
926                assertEquals(0f, a2.getAnimatedFraction());
927                assertEquals(1f, a3.getAnimatedFraction());
928                assertEquals(A1_END_VALUE, a1.getAnimatedValue());
929                assertEquals(A2_START_VALUE, a2.getAnimatedValue());
930                assertEquals(100, a3.getAnimatedValue());
931            }
932        });
933    }
934
935    @SmallTest
936    public void testZeroScale() throws Throwable {
937        // Test whether animations would end properly when the scale is forced to be zero
938        float scale = ValueAnimator.getDurationScale();
939        ValueAnimator.setDurationScale(0f);
940
941        // Run two animators, one of which has a start delay, after setting the duration scale to 0
942        a1.setStartDelay(200);
943        final MyListener l1 =  new MyListener();
944        final MyListener l2 = new MyListener();
945        a1.addListener(l1);
946        a2.addListener(l2);
947
948        runTestOnUiThread(new Runnable() {
949            @Override
950            public void run() {
951                assertFalse(l1.startCalled);
952                assertFalse(l2.startCalled);
953                assertFalse(l1.endCalled);
954                assertFalse(l2.endCalled);
955
956                a1.start();
957                a2.start();
958
959                // In the case of 0 duration scale applied to a non-0 duration, check that the
960                // value is immediately set to the start value.
961                assertEquals(A2_START_VALUE, a2.getAnimatedValue());
962            }
963        });
964        Thread.sleep(POLL_INTERVAL);
965
966        runTestOnUiThread(new Runnable() {
967            @Override
968            public void run() {
969                assertTrue(l1.startCalled);
970                assertTrue(l2.startCalled);
971                assertTrue(l1.endCalled);
972                assertTrue(l2.endCalled);
973                assertEquals(A1_END_VALUE, a1.getAnimatedValue());
974                assertEquals(A2_END_VALUE, a2.getAnimatedValue());
975            }
976        });
977
978        // Restore duration scale
979        ValueAnimator.setDurationScale(scale);
980    }
981
982    @SmallTest
983    public void testReverse() throws Throwable {
984        // Prolong animators duration so that we can do multiple checks during their run
985        final ValueAnimator a3 = ValueAnimator.ofInt(0, 100);
986        a1.setDuration(400);
987        a2.setDuration(600);
988        a3.setDuration(400);
989        final MyListener l1 = new MyListener();
990        final MyListener l2 = new MyListener();
991        final MyListener l3 = new MyListener();
992        a1.addListener(l1);
993        a2.addListener(l2);
994        a3.addListener(l3);
995
996        // Reverse three animators, seek one to the beginning and another to the end, and force
997        // to end the third one during reversing.
998        runTestOnUiThread(new Runnable() {
999            @Override
1000            public void run() {
1001                assertFalse(l1.startCalled);
1002                assertFalse(l2.startCalled);
1003                assertFalse(l3.startCalled);
1004                assertFalse(l1.endCalled);
1005                assertFalse(l2.endCalled);
1006                assertFalse(l3.endCalled);
1007                a1.reverse();
1008                a2.reverse();
1009                a3.reverse();
1010            }
1011        });
1012        Thread.sleep(POLL_INTERVAL);
1013        runTestOnUiThread(new Runnable() {
1014            @Override
1015            public void run() {
1016                assertTrue(l1.startCalled);
1017                assertTrue(l2.startCalled);
1018                assertTrue(l3.startCalled);
1019
1020                a1.setCurrentFraction(0f);
1021                a2.setCurrentFraction(1f);
1022                a3.end();
1023
1024                // Check that the fraction has been set, and the getter returns the correct values.
1025                assertEquals(1f, a1.getAnimatedFraction());
1026                assertEquals(0f, a2.getAnimatedFraction());
1027            }
1028        });
1029        Thread.sleep(POLL_INTERVAL);
1030
1031        // By now, a2 should have finished due to the seeking. It wouldn't have finished otherwise.
1032        runTestOnUiThread(new Runnable() {
1033            @Override
1034            public void run() {
1035                // Check that both animations have started, and a2 has finished.
1036                assertFalse(l1.endCalled);
1037                assertTrue(l2.endCalled);
1038                assertTrue(l3.endCalled);
1039            }
1040        });
1041        Thread.sleep(a1.getTotalDuration());
1042
1043        runTestOnUiThread(new Runnable() {
1044            @Override
1045            public void run() {
1046                // Verify that a1 has finished as well.
1047                assertTrue(l1.endCalled);
1048                assertEquals(0f, a1.getAnimatedFraction());
1049                assertEquals(0f, a2.getAnimatedFraction());
1050                assertEquals(0f, a3.getAnimatedFraction());
1051            }
1052        });
1053    }
1054
1055    class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener {
1056        boolean wasRunning = false;
1057        long firstRunningFrameTime = -1;
1058        long lastUpdateTime = -1;
1059        float startFraction = 0;
1060
1061        @Override
1062        public void onAnimationUpdate(ValueAnimator animation) {
1063            lastUpdateTime = SystemClock.uptimeMillis();
1064            if (animation.isRunning() && !wasRunning) {
1065                // Delay has passed
1066                firstRunningFrameTime = lastUpdateTime;
1067                startFraction = animation.getAnimatedFraction();
1068                wasRunning = animation.isRunning();
1069            }
1070        }
1071
1072        void reset() {
1073            wasRunning = false;
1074            firstRunningFrameTime = -1;
1075            lastUpdateTime = -1;
1076            startFraction = 0;
1077        }
1078    }
1079
1080    class MyListener implements Animator.AnimatorListener {
1081        boolean startCalled = false;
1082        boolean cancelCalled = false;
1083        boolean endCalled = false;
1084        long startTime = -1;
1085        long endTime = -1;
1086
1087        @Override
1088        public void onAnimationStart(Animator animation) {
1089            startCalled = true;
1090            startTime = SystemClock.uptimeMillis();
1091        }
1092
1093        @Override
1094        public void onAnimationEnd(Animator animation) {
1095            endCalled = true;
1096            endTime = SystemClock.uptimeMillis();
1097        }
1098
1099        @Override
1100        public void onAnimationCancel(Animator animation) {
1101            cancelCalled = true;
1102        }
1103
1104        @Override
1105        public void onAnimationRepeat(Animator animation) {
1106
1107        }
1108    }
1109
1110    class MyPauseListener implements Animator.AnimatorPauseListener {
1111        boolean pauseCalled = false;
1112        boolean resumeCalled = false;
1113
1114        @Override
1115        public void onAnimationPause(Animator animation) {
1116            pauseCalled = true;
1117        }
1118
1119        @Override
1120        public void onAnimationResume(Animator animation) {
1121            resumeCalled = true;
1122        }
1123    }
1124
1125    class MyFrameCallbackProvider implements AnimationHandler.AnimationFrameCallbackProvider {
1126
1127        Handler mHandler = null;
1128        private final static int MSG_FRAME = 0;
1129        private long mFrameDelay = DEFAULT_FRAME_INTERVAL;
1130        private ArrayList<Choreographer.FrameCallback> mFrameCallbacks = new ArrayList<>();
1131
1132        final LooperThread mThread = new LooperThread();
1133
1134        public MyFrameCallbackProvider() {
1135            mThread.start();
1136        }
1137
1138        @Override
1139        public void postFrameCallback(Choreographer.FrameCallback callback) {
1140            mHandler.sendEmptyMessageDelayed(MSG_FRAME, mFrameDelay);
1141            if (!mFrameCallbacks.contains(callback)) {
1142                mFrameCallbacks.add(callback);
1143            }
1144        }
1145
1146        @Override
1147        public void postCommitCallback(Runnable runnable) {
1148            // Run the runnable after a commit delay
1149            mHandler.postDelayed(runnable, COMMIT_DELAY);
1150        }
1151
1152        @Override
1153        public long getFrameTime() {
1154            return SystemClock.uptimeMillis();
1155        }
1156
1157        @Override
1158        public long getFrameDelay() {
1159            return mFrameDelay;
1160        }
1161
1162        @Override
1163        public void setFrameDelay(long delay) {
1164            mFrameDelay = delay;
1165            if (mFrameCallbacks.size() != 0) {
1166                mHandler.removeMessages(MSG_FRAME);
1167                mHandler.sendEmptyMessageDelayed(MSG_FRAME, mFrameDelay);
1168            }
1169        }
1170
1171        class LooperThread extends Thread {
1172            public void run() {
1173                Looper.prepare();
1174                mHandler = new Handler() {
1175                    public void handleMessage(Message msg) {
1176                        // Handle message here.
1177                        switch (msg.what) {
1178                            case MSG_FRAME:
1179                                for (int i = 0; i < mFrameCallbacks.size(); i++) {
1180                                    mFrameCallbacks.get(i).doFrame(SystemClock.uptimeMillis());
1181                                }
1182                                break;
1183                            default:
1184                                break;
1185                        }
1186                    }
1187                };
1188                Looper.loop();
1189            }
1190        }
1191    }
1192}
1193