1 /*
2  * Copyright (C) 2011 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 package android.animation;
17 
18 import android.os.Handler;
19 import android.test.ActivityInstrumentationTestCase2;
20 import android.test.UiThreadTest;
21 import android.test.suitebuilder.annotation.MediumTest;
22 import android.test.suitebuilder.annotation.SmallTest;
23 
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.TimeoutException;
26 
27 /**
28  * Tests for the various lifecycle events of Animators. This abstract class is subclassed by
29  * concrete implementations that provide the actual Animator objects being tested. All of the
30  * testing mechanisms are in this class; the subclasses are only responsible for providing
31  * the mAnimator object.
32  *
33  * This test is more complicated than a typical synchronous test because much of the functionality
34  * must happen on the UI thread. Some tests do this by using the UiThreadTest annotation to
35  * automatically run the whole test on that thread. Other tests must run on the UI thread and also
36  * wait for some later event to occur before ending. These tests use a combination of an
37  * AbstractFuture mechanism and a delayed action to release that Future later.
38  */
39 public abstract class EventsTest
40         extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
41 
42     protected static final int ANIM_DURATION = 400;
43     protected static final int ANIM_DELAY = 100;
44     protected static final int ANIM_MID_DURATION = ANIM_DURATION / 2;
45     protected static final int ANIM_MID_DELAY = ANIM_DELAY / 2;
46     protected static final int ANIM_PAUSE_DURATION = ANIM_DELAY;
47     protected static final int ANIM_PAUSE_DELAY = ANIM_DELAY / 2;
48     protected static final int FUTURE_RELEASE_DELAY = 50;
49     protected static final int ANIM_FULL_DURATION_SLOP = 100;
50 
51     private boolean mStarted;  // tracks whether we've received the onAnimationStart() callback
52     protected boolean mRunning;  // tracks whether we've started the animator
53     private boolean mCanceled; // tracks whether we've canceled the animator
54     protected Animator.AnimatorListener mFutureListener; // mechanism for delaying end of the test
55     protected FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
56     private Animator.AnimatorListener mListener; // Listener that handles/tests the events
57 
58     protected Animator mAnimator; // The animator used in the tests. Must be set in subclass
59                                   // setup() method prior to calling the superclass setup()
60 
61     /**
62      * Cancels the given animator. Used to delay cancellation until some later time (after the
63      * animator has started playing).
64      */
65     protected static class Canceler implements Runnable {
66         Animator mAnim;
67         FutureWaiter mFuture;
Canceler(Animator anim, FutureWaiter future)68         public Canceler(Animator anim, FutureWaiter future) {
69             mAnim = anim;
70             mFuture = future;
71         }
72         @Override
run()73         public void run() {
74             try {
75                 mAnim.cancel();
76             } catch (junit.framework.AssertionFailedError e) {
77                 mFuture.setException(new RuntimeException(e));
78             }
79         }
80     };
81 
82     /**
83      * Timeout length, based on when the animation should reasonably be complete.
84      */
getTimeout()85     protected long getTimeout() {
86         return ANIM_DURATION + ANIM_DELAY + FUTURE_RELEASE_DELAY;
87     }
88 
89     /**
90      * Ends the given animator. Used to delay ending until some later time (after the
91      * animator has started playing).
92      */
93     static class Ender implements Runnable {
94         Animator mAnim;
95         FutureWaiter mFuture;
Ender(Animator anim, FutureWaiter future)96         public Ender(Animator anim, FutureWaiter future) {
97             mAnim = anim;
98             mFuture = future;
99         }
100         @Override
run()101         public void run() {
102             try {
103                 mAnim.end();
104             } catch (junit.framework.AssertionFailedError e) {
105                 mFuture.setException(new RuntimeException(e));
106             }
107         }
108     };
109 
110     /**
111      * Pauses the given animator. Used to delay pausing until some later time (after the
112      * animator has started playing).
113      */
114     static class Pauser implements Runnable {
115         Animator mAnim;
116         FutureWaiter mFuture;
Pauser(Animator anim, FutureWaiter future)117         public Pauser(Animator anim, FutureWaiter future) {
118             mAnim = anim;
119             mFuture = future;
120         }
121         @Override
run()122         public void run() {
123             try {
124                 mAnim.pause();
125             } catch (junit.framework.AssertionFailedError e) {
126                 mFuture.setException(new RuntimeException(e));
127             }
128         }
129     };
130 
131     /**
132      * Resumes the given animator. Used to delay resuming until some later time (after the
133      * animator has paused for some duration).
134      */
135     static class Resumer implements Runnable {
136         Animator mAnim;
137         FutureWaiter mFuture;
Resumer(Animator anim, FutureWaiter future)138         public Resumer(Animator anim, FutureWaiter future) {
139             mAnim = anim;
140             mFuture = future;
141         }
142         @Override
run()143         public void run() {
144             try {
145                 mAnim.resume();
146             } catch (junit.framework.AssertionFailedError e) {
147                 mFuture.setException(new RuntimeException(e));
148             }
149         }
150     };
151 
152     /**
153      * Releases the given Future object when the listener's end() event is called. Specifically,
154      * it releases it after some further delay, to give the test time to do other things right
155      * after an animation ends.
156      */
157     protected static class FutureReleaseListener extends AnimatorListenerAdapter {
158         FutureWaiter mFuture;
159 
FutureReleaseListener(FutureWaiter future)160         public FutureReleaseListener(FutureWaiter future) {
161             mFuture = future;
162         }
163 
164         /**
165          * Variant constructor that auto-releases the FutureWaiter after the specified timeout.
166          * @param future
167          * @param timeout
168          */
FutureReleaseListener(FutureWaiter future, long timeout)169         public FutureReleaseListener(FutureWaiter future, long timeout) {
170             mFuture = future;
171             Handler handler = new Handler();
172             handler.postDelayed(new Runnable() {
173                 @Override
174                 public void run() {
175                     mFuture.release();
176                 }
177             }, timeout);
178         }
179 
180         @Override
onAnimationEnd(Animator animation)181         public void onAnimationEnd(Animator animation) {
182             Handler handler = new Handler();
183             handler.postDelayed(new Runnable() {
184                 @Override
185                 public void run() {
186                     mFuture.release();
187                 }
188             }, FUTURE_RELEASE_DELAY);
189         }
190     };
191 
EventsTest()192     public EventsTest() {
193         super(BasicAnimatorActivity.class);
194     }
195 
196     /**
197      * Sets up the fields used by each test. Subclasses must override this method to create
198      * the protected mAnimator object used in all tests. Overrides must create that animator
199      * and then call super.setup(), where further properties are set on that animator.
200      * @throws Exception
201      */
202     @Override
setUp()203     public void setUp() throws Exception {
204         super.setUp();
205 
206         // mListener is the main testing mechanism of this file. The asserts of each test
207         // are embedded in the listener callbacks that it implements.
208         mListener = new AnimatorListenerAdapter() {
209             @Override
210             public void onAnimationStart(Animator animation) {
211                 // This should only be called on an animation that has not yet been started
212                 assertFalse(mStarted);
213                 assertTrue(mRunning);
214                 mStarted = true;
215             }
216 
217             @Override
218             public void onAnimationCancel(Animator animation) {
219                 // This should only be called on an animation that has been started and not
220                 // yet canceled or ended
221                 assertFalse(mCanceled);
222                 assertTrue(mRunning || mStarted);
223                 mCanceled = true;
224             }
225 
226             @Override
227             public void onAnimationEnd(Animator animation) {
228                 // This should only be called on an animation that has been started and not
229                 // yet ended
230                 assertTrue(mRunning || mStarted);
231                 mRunning = false;
232                 mStarted = false;
233                 super.onAnimationEnd(animation);
234             }
235         };
236 
237         mAnimator.addListener(mListener);
238         mAnimator.setDuration(ANIM_DURATION);
239 
240         mFuture = new FutureWaiter();
241 
242         mRunning = false;
243         mCanceled = false;
244         mStarted = false;
245     }
246 
247     /**
248      * Verify that calling cancel on an unstarted animator does nothing.
249      */
250     @UiThreadTest
251     @SmallTest
testCancel()252     public void testCancel() throws Exception {
253         mAnimator.cancel();
254     }
255 
256     /**
257      * Verify that calling end on an unstarted animator starts/ends an animator.
258      */
259     @UiThreadTest
260     @SmallTest
testEnd()261     public void testEnd() throws Exception {
262         mRunning = true; // end() implicitly starts an unstarted animator
263         mAnimator.end();
264     }
265 
266     /**
267      * Verify that calling cancel on a started animator does the right thing.
268      */
269     @UiThreadTest
270     @SmallTest
testStartCancel()271     public void testStartCancel() throws Exception {
272         mFutureListener = new FutureReleaseListener(mFuture);
273         getActivity().runOnUiThread(new Runnable() {
274             @Override
275             public void run() {
276                 try {
277                     mRunning = true;
278                     mAnimator.start();
279                     mAnimator.cancel();
280                     mFuture.release();
281                 } catch (junit.framework.AssertionFailedError e) {
282                     mFuture.setException(new RuntimeException(e));
283                 }
284             }
285         });
286         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
287     }
288 
289     /**
290      * Verify that calling end on a started animator does the right thing.
291      */
292     @UiThreadTest
293     @SmallTest
testStartEnd()294     public void testStartEnd() throws Exception {
295         mFutureListener = new FutureReleaseListener(mFuture);
296         getActivity().runOnUiThread(new Runnable() {
297             @Override
298             public void run() {
299                 try {
300                     mRunning = true;
301                     mAnimator.start();
302                     mAnimator.end();
303                     mFuture.release();
304                 } catch (junit.framework.AssertionFailedError e) {
305                     mFuture.setException(new RuntimeException(e));
306                 }
307             }
308         });
309         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
310     }
311 
312     /**
313      * Same as testStartCancel, but with a startDelayed animator
314      */
315     @SmallTest
testStartDelayedCancel()316     public void testStartDelayedCancel() throws Exception {
317         mFutureListener = new FutureReleaseListener(mFuture);
318         mAnimator.setStartDelay(ANIM_DELAY);
319         getActivity().runOnUiThread(new Runnable() {
320             @Override
321             public void run() {
322                 try {
323                     mRunning = true;
324                     mAnimator.start();
325                     mAnimator.cancel();
326                     mFuture.release();
327                 } catch (junit.framework.AssertionFailedError e) {
328                     mFuture.setException(new RuntimeException(e));
329                 }
330             }
331         });
332         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
333     }
334 
335     /**
336      * Same as testStartEnd, but with a startDelayed animator
337      */
338     @SmallTest
testStartDelayedEnd()339     public void testStartDelayedEnd() throws Exception {
340         mFutureListener = new FutureReleaseListener(mFuture);
341         mAnimator.setStartDelay(ANIM_DELAY);
342         getActivity().runOnUiThread(new Runnable() {
343             @Override
344             public void run() {
345                 try {
346                     mRunning = true;
347                     mAnimator.start();
348                     mAnimator.end();
349                     mFuture.release();
350                 } catch (junit.framework.AssertionFailedError e) {
351                     mFuture.setException(new RuntimeException(e));
352                 }
353             }
354         });
355         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
356     }
357 
358     /**
359      * Verify that canceling an animator that is playing does the right thing.
360      */
361     @MediumTest
testPlayingCancel()362     public void testPlayingCancel() throws Exception {
363         mFutureListener = new FutureReleaseListener(mFuture);
364         getActivity().runOnUiThread(new Runnable() {
365             @Override
366             public void run() {
367                 try {
368                     Handler handler = new Handler();
369                     mAnimator.addListener(mFutureListener);
370                     mRunning = true;
371                     mAnimator.start();
372                     handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
373                 } catch (junit.framework.AssertionFailedError e) {
374                     mFuture.setException(new RuntimeException(e));
375                 }
376             }
377         });
378         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
379     }
380 
381     /**
382      * Verify that ending an animator that is playing does the right thing.
383      */
384     @MediumTest
testPlayingEnd()385     public void testPlayingEnd() throws Exception {
386         mFutureListener = new FutureReleaseListener(mFuture);
387         getActivity().runOnUiThread(new Runnable() {
388             @Override
389             public void run() {
390                 try {
391                     Handler handler = new Handler();
392                     mAnimator.addListener(mFutureListener);
393                     mRunning = true;
394                     mAnimator.start();
395                     handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
396                 } catch (junit.framework.AssertionFailedError e) {
397                     mFuture.setException(new RuntimeException(e));
398                 }
399             }
400         });
401         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
402     }
403 
404     /**
405      * Same as testPlayingCancel, but with a startDelayed animator
406      */
407     @MediumTest
testPlayingDelayedCancel()408     public void testPlayingDelayedCancel() throws Exception {
409         mAnimator.setStartDelay(ANIM_DELAY);
410         mFutureListener = new FutureReleaseListener(mFuture);
411         getActivity().runOnUiThread(new Runnable() {
412             @Override
413             public void run() {
414                 try {
415                     Handler handler = new Handler();
416                     mAnimator.addListener(mFutureListener);
417                     mRunning = true;
418                     mAnimator.start();
419                     handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
420                 } catch (junit.framework.AssertionFailedError e) {
421                     mFuture.setException(new RuntimeException(e));
422                 }
423             }
424         });
425         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
426     }
427 
428     /**
429      * Same as testPlayingEnd, but with a startDelayed animator
430      */
431     @MediumTest
testPlayingDelayedEnd()432     public void testPlayingDelayedEnd() throws Exception {
433         mAnimator.setStartDelay(ANIM_DELAY);
434         mFutureListener = new FutureReleaseListener(mFuture);
435         getActivity().runOnUiThread(new Runnable() {
436             @Override
437             public void run() {
438                 try {
439                     Handler handler = new Handler();
440                     mAnimator.addListener(mFutureListener);
441                     mRunning = true;
442                     mAnimator.start();
443                     handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
444                 } catch (junit.framework.AssertionFailedError e) {
445                     mFuture.setException(new RuntimeException(e));
446                 }
447             }
448         });
449         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
450     }
451 
452     /**
453      * Same as testPlayingDelayedCancel, but cancel during the startDelay period
454      */
455     @MediumTest
testPlayingDelayedCancelMidDelay()456     public void testPlayingDelayedCancelMidDelay() throws Exception {
457         mAnimator.setStartDelay(ANIM_DELAY);
458         getActivity().runOnUiThread(new Runnable() {
459             @Override
460             public void run() {
461                 try {
462                     // Set the listener to automatically timeout after an uncanceled animation
463                     // would have finished. This tests to make sure that we're not calling
464                     // the listeners with cancel/end callbacks since they won't be called
465                     // with the start event.
466                     mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
467                     Handler handler = new Handler();
468                     mRunning = true;
469                     mAnimator.start();
470                     handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
471                 } catch (junit.framework.AssertionFailedError e) {
472                     mFuture.setException(new RuntimeException(e));
473                 }
474             }
475         });
476         mFuture.get(getTimeout() + 100,  TimeUnit.MILLISECONDS);
477     }
478 
479     /**
480      * Same as testPlayingDelayedEnd, but end during the startDelay period
481      */
482     @MediumTest
testPlayingDelayedEndMidDelay()483     public void testPlayingDelayedEndMidDelay() throws Exception {
484         mAnimator.setStartDelay(ANIM_DELAY);
485         getActivity().runOnUiThread(new Runnable() {
486             @Override
487             public void run() {
488                 try {
489                     // Set the listener to automatically timeout after an uncanceled animation
490                     // would have finished. This tests to make sure that we're not calling
491                     // the listeners with cancel/end callbacks since they won't be called
492                     // with the start event.
493                     mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
494                     Handler handler = new Handler();
495                     mRunning = true;
496                     mAnimator.start();
497                     handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
498                 } catch (junit.framework.AssertionFailedError e) {
499                     mFuture.setException(new RuntimeException(e));
500                 }
501             }
502         });
503         mFuture.get(getTimeout() + 100,  TimeUnit.MILLISECONDS);
504     }
505 
506     /**
507      * Verifies that canceling a started animation after it has already been canceled
508      * does nothing.
509      */
510     @MediumTest
testStartDoubleCancel()511     public void testStartDoubleCancel() throws Exception {
512         mFutureListener = new FutureReleaseListener(mFuture);
513         getActivity().runOnUiThread(new Runnable() {
514             @Override
515             public void run() {
516                 try {
517                     mRunning = true;
518                     mAnimator.start();
519                     mAnimator.cancel();
520                     mAnimator.cancel();
521                     mFuture.release();
522                 } catch (junit.framework.AssertionFailedError e) {
523                     mFuture.setException(new RuntimeException(e));
524                 }
525             }
526         });
527         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
528     }
529 
530     /**
531      * Verifies that ending a started animation after it has already been ended
532      * does nothing.
533      */
534     @MediumTest
testStartDoubleEnd()535     public void testStartDoubleEnd() throws Exception {
536         mFutureListener = new FutureReleaseListener(mFuture);
537         getActivity().runOnUiThread(new Runnable() {
538             @Override
539             public void run() {
540                 try {
541                     mRunning = true;
542                     mAnimator.start();
543                     mAnimator.end();
544                     mRunning = true; // end() implicitly starts an unstarted animator
545                     mAnimator.end();
546                     mFuture.release();
547                 } catch (junit.framework.AssertionFailedError e) {
548                     mFuture.setException(new RuntimeException(e));
549                 }
550             }
551         });
552         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
553     }
554 
555     /**
556      * Same as testStartDoubleCancel, but with a startDelayed animator
557      */
558     @MediumTest
testStartDelayedDoubleCancel()559     public void testStartDelayedDoubleCancel() throws Exception {
560         mAnimator.setStartDelay(ANIM_DELAY);
561         mFutureListener = new FutureReleaseListener(mFuture);
562         getActivity().runOnUiThread(new Runnable() {
563             @Override
564             public void run() {
565                 try {
566                     mRunning = true;
567                     mAnimator.start();
568                     mAnimator.cancel();
569                     mAnimator.cancel();
570                     mFuture.release();
571                 } catch (junit.framework.AssertionFailedError e) {
572                     mFuture.setException(new RuntimeException(e));
573                 }
574             }
575         });
576         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
577      }
578 
579     /**
580      * Same as testStartDoubleEnd, but with a startDelayed animator
581      */
582     @MediumTest
testStartDelayedDoubleEnd()583     public void testStartDelayedDoubleEnd() throws Exception {
584         mAnimator.setStartDelay(ANIM_DELAY);
585         mFutureListener = new FutureReleaseListener(mFuture);
586         getActivity().runOnUiThread(new Runnable() {
587             @Override
588             public void run() {
589                 try {
590                     mRunning = true;
591                     mAnimator.start();
592                     mAnimator.end();
593                     mRunning = true; // end() implicitly starts an unstarted animator
594                     mAnimator.end();
595                     mFuture.release();
596                 } catch (junit.framework.AssertionFailedError e) {
597                     mFuture.setException(new RuntimeException(e));
598                 }
599             }
600         });
601         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
602      }
603 
604     /**
605      * Verify that pausing and resuming an animator ends within
606      * the appropriate timeout duration.
607      */
608     @MediumTest
testPauseResume()609     public void testPauseResume() throws Exception {
610         mFutureListener = new FutureReleaseListener(mFuture);
611         getActivity().runOnUiThread(new Runnable() {
612             @Override
613             public void run() {
614                 try {
615                     Handler handler = new Handler();
616                     mAnimator.addListener(mFutureListener);
617                     mRunning = true;
618                     mAnimator.start();
619                     handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
620                     handler.postDelayed(new Resumer(mAnimator, mFuture),
621                             ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
622                 } catch (junit.framework.AssertionFailedError e) {
623                     mFuture.setException(new RuntimeException(e));
624                 }
625             }
626         });
627         mFuture.get(getTimeout() + ANIM_PAUSE_DURATION, TimeUnit.MILLISECONDS);
628     }
629 
630     /**
631      * Verify that pausing and resuming a startDelayed animator ends within
632      * the appropriate timeout duration.
633      */
634     @MediumTest
testPauseResumeDelayed()635     public void testPauseResumeDelayed() throws Exception {
636         mAnimator.setStartDelay(ANIM_DELAY);
637         mFutureListener = new FutureReleaseListener(mFuture);
638         getActivity().runOnUiThread(new Runnable() {
639             @Override
640             public void run() {
641                 try {
642                     Handler handler = new Handler();
643                     mAnimator.addListener(mFutureListener);
644                     mRunning = true;
645                     mAnimator.start();
646                     handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
647                     handler.postDelayed(new Resumer(mAnimator, mFuture),
648                             ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
649                 } catch (junit.framework.AssertionFailedError e) {
650                     mFuture.setException(new RuntimeException(e));
651                 }
652             }
653         });
654         mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
655                 TimeUnit.MILLISECONDS);
656     }
657 
658     /**
659      * Verify that pausing an animator without resuming it causes a timeout.
660      */
661     @MediumTest
testPauseTimeout()662     public void testPauseTimeout() throws Exception {
663         mFutureListener = new FutureReleaseListener(mFuture);
664         getActivity().runOnUiThread(new Runnable() {
665             @Override
666             public void run() {
667                 try {
668                     Handler handler = new Handler();
669                     mAnimator.addListener(mFutureListener);
670                     mRunning = true;
671                     mAnimator.start();
672                     handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
673                 } catch (junit.framework.AssertionFailedError e) {
674                     mFuture.setException(new RuntimeException(e));
675                 }
676             }
677         });
678         try {
679             mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
680                     TimeUnit.MILLISECONDS);
681         } catch (TimeoutException e) {
682             // Expected behavior, swallow the exception
683         }
684     }
685 
686     /**
687      * Verify that pausing a startDelayed animator without resuming it causes a timeout.
688      */
689     @MediumTest
testPauseTimeoutDelayed()690     public void testPauseTimeoutDelayed() throws Exception {
691         mAnimator.setStartDelay(ANIM_DELAY);
692         mFutureListener = new FutureReleaseListener(mFuture);
693         getActivity().runOnUiThread(new Runnable() {
694             @Override
695             public void run() {
696                 try {
697                     Handler handler = new Handler();
698                     mAnimator.addListener(mFutureListener);
699                     mRunning = true;
700                     mAnimator.start();
701                     handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
702                 } catch (junit.framework.AssertionFailedError e) {
703                     mFuture.setException(new RuntimeException(e));
704                 }
705             }
706         });
707         try {
708             mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
709                     TimeUnit.MILLISECONDS);
710         } catch (TimeoutException e) {
711             // Expected behavior, swallow the exception
712         }
713     }
714 }
715