1 /*
2  * Copyright (C) 2016 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.fragment.cts;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.animation.Animator;
25 import android.animation.AnimatorListenerAdapter;
26 import android.animation.ValueAnimator;
27 import android.app.Fragment;
28 import android.app.FragmentController;
29 import android.app.FragmentManager;
30 import android.app.FragmentManagerNonConfig;
31 import android.os.Parcelable;
32 import android.util.Pair;
33 import android.view.View;
34 import android.view.animation.TranslateAnimation;
35 
36 import androidx.test.filters.MediumTest;
37 import androidx.test.rule.ActivityTestRule;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import org.junit.Before;
41 import org.junit.Rule;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.TimeUnit;
47 
48 @MediumTest
49 @RunWith(AndroidJUnit4.class)
50 public class FragmentAnimatorTest {
51     // These are pretend resource IDs for animators. We don't need real ones since we
52     // load them by overriding onCreateAnimator
53     private final static int ENTER = 1;
54     private final static int EXIT = 2;
55     private final static int POP_ENTER = 3;
56     private final static int POP_EXIT = 4;
57 
58     @Rule
59     public ActivityTestRule<FragmentTestActivity> mActivityRule =
60             new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
61 
62     @Before
setupContainer()63     public void setupContainer() {
64         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
65     }
66 
67     // Ensure that adding and popping a Fragment uses the enter and popExit animators
68     @Test
addAnimators()69     public void addAnimators() throws Throwable {
70         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
71 
72         // One fragment with a view
73         final AnimatorFragment fragment = new AnimatorFragment();
74         fm.beginTransaction()
75                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
76                 .add(R.id.fragmentContainer, fragment)
77                 .addToBackStack(null)
78                 .commit();
79         FragmentTestUtil.waitForExecution(mActivityRule);
80 
81         assertEnterPopExit(fragment);
82     }
83 
84     // Ensure that removing and popping a Fragment uses the exit and popEnter animators
85     @Test
removeAnimators()86     public void removeAnimators() throws Throwable {
87         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
88 
89         // One fragment with a view
90         final AnimatorFragment fragment = new AnimatorFragment();
91         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
92         FragmentTestUtil.waitForExecution(mActivityRule);
93 
94         fm.beginTransaction()
95                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
96                 .remove(fragment)
97                 .addToBackStack(null)
98                 .commit();
99         FragmentTestUtil.waitForExecution(mActivityRule);
100 
101         assertExitPopEnter(fragment);
102     }
103 
104     // Ensure that showing and popping a Fragment uses the enter and popExit animators
105     // This tests reordered transactions
106     @Test
showAnimatorsReordered()107     public void showAnimatorsReordered() throws Throwable {
108         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
109 
110         // One fragment with a view
111         final AnimatorFragment fragment = new AnimatorFragment();
112         fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
113         FragmentTestUtil.waitForExecution(mActivityRule);
114 
115         mActivityRule.runOnUiThread(() -> {
116             assertEquals(View.GONE, fragment.getView().getVisibility());
117         });
118 
119         fm.beginTransaction()
120                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
121                 .show(fragment)
122                 .addToBackStack(null)
123                 .commit();
124         FragmentTestUtil.waitForExecution(mActivityRule);
125 
126         mActivityRule.runOnUiThread(() -> {
127             assertEquals(View.VISIBLE, fragment.getView().getVisibility());
128         });
129         assertEnterPopExit(fragment);
130 
131         mActivityRule.runOnUiThread(() -> {
132             assertEquals(View.GONE, fragment.getView().getVisibility());
133         });
134     }
135 
136     // Ensure that showing and popping a Fragment uses the enter and popExit animators
137     // This tests ordered transactions
138     @Test
showAnimatorsOrdered()139     public void showAnimatorsOrdered() throws Throwable {
140         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
141 
142         // One fragment with a view
143         final AnimatorFragment fragment = new AnimatorFragment();
144         fm.beginTransaction()
145                 .add(R.id.fragmentContainer, fragment)
146                 .hide(fragment)
147                 .setReorderingAllowed(false)
148                 .commit();
149         FragmentTestUtil.waitForExecution(mActivityRule);
150 
151         mActivityRule.runOnUiThread(() -> {
152             assertEquals(View.GONE, fragment.getView().getVisibility());
153         });
154 
155         fm.beginTransaction()
156                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
157                 .show(fragment)
158                 .setReorderingAllowed(false)
159                 .addToBackStack(null)
160                 .commit();
161         FragmentTestUtil.waitForExecution(mActivityRule);
162 
163         mActivityRule.runOnUiThread(() -> {
164             assertEquals(View.VISIBLE, fragment.getView().getVisibility());
165         });
166         assertEnterPopExit(fragment);
167 
168         mActivityRule.runOnUiThread(() -> {
169             assertEquals(View.GONE, fragment.getView().getVisibility());
170         });
171     }
172 
173     // Ensure that hiding and popping a Fragment uses the exit and popEnter animators
174     @Test
hideAnimators()175     public void hideAnimators() throws Throwable {
176         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
177 
178         // One fragment with a view
179         final AnimatorFragment fragment = new AnimatorFragment();
180         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
181         FragmentTestUtil.waitForExecution(mActivityRule);
182 
183         fm.beginTransaction()
184                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
185                 .hide(fragment)
186                 .addToBackStack(null)
187                 .commit();
188         FragmentTestUtil.waitForExecution(mActivityRule);
189 
190         assertExitPopEnter(fragment);
191     }
192 
193     // Ensure that attaching and popping a Fragment uses the enter and popExit animators
194     @Test
attachAnimators()195     public void attachAnimators() throws Throwable {
196         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
197 
198         // One fragment with a view
199         final AnimatorFragment fragment = new AnimatorFragment();
200         fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
201         FragmentTestUtil.waitForExecution(mActivityRule);
202 
203         fm.beginTransaction()
204                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
205                 .attach(fragment)
206                 .addToBackStack(null)
207                 .commit();
208         FragmentTestUtil.waitForExecution(mActivityRule);
209 
210         assertEnterPopExit(fragment);
211     }
212 
213     // Ensure that detaching and popping a Fragment uses the exit and popEnter animators
214     @Test
detachAnimators()215     public void detachAnimators() throws Throwable {
216         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
217 
218         // One fragment with a view
219         final AnimatorFragment fragment = new AnimatorFragment();
220         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
221         FragmentTestUtil.waitForExecution(mActivityRule);
222 
223         fm.beginTransaction()
224                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
225                 .detach(fragment)
226                 .addToBackStack(null)
227                 .commit();
228         FragmentTestUtil.waitForExecution(mActivityRule);
229 
230         assertExitPopEnter(fragment);
231     }
232 
233     // Replace should exit the existing fragments and enter the added fragment, then
234     // popping should popExit the removed fragment and popEnter the added fragments
235     @Test
replaceAnimators()236     public void replaceAnimators() throws Throwable {
237         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
238 
239         // One fragment with a view
240         final AnimatorFragment fragment1 = new AnimatorFragment();
241         final AnimatorFragment fragment2 = new AnimatorFragment();
242         fm.beginTransaction()
243                 .add(R.id.fragmentContainer, fragment1, "1")
244                 .add(R.id.fragmentContainer, fragment2, "2")
245                 .commit();
246         FragmentTestUtil.waitForExecution(mActivityRule);
247 
248         final AnimatorFragment fragment3 = new AnimatorFragment();
249         fm.beginTransaction()
250                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
251                 .replace(R.id.fragmentContainer, fragment3)
252                 .addToBackStack(null)
253                 .commit();
254         FragmentTestUtil.waitForExecution(mActivityRule);
255 
256         assertFragmentAnimation(fragment1, 1, false, EXIT);
257         assertFragmentAnimation(fragment2, 1, false, EXIT);
258         assertFragmentAnimation(fragment3, 1, true, ENTER);
259 
260         fm.popBackStack();
261         FragmentTestUtil.waitForExecution(mActivityRule);
262 
263         assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
264         final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
265         final AnimatorFragment replacement2 = (AnimatorFragment) fm.findFragmentByTag("1");
266         int expectedAnimations = replacement1 == fragment1 ? 2 : 1;
267         assertFragmentAnimation(replacement1, expectedAnimations, true, POP_ENTER);
268         assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
269     }
270 
271     // Ensure that adding and popping a Fragment uses the enter and popExit animators,
272     // but the animators are delayed when an entering Fragment is postponed.
273     @Test
postponedAddAnimators()274     public void postponedAddAnimators() throws Throwable {
275         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
276 
277         final AnimatorFragment fragment = new AnimatorFragment();
278         fragment.postponeEnterTransition();
279         fm.beginTransaction()
280                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
281                 .add(R.id.fragmentContainer, fragment)
282                 .addToBackStack(null)
283                 .commit();
284         FragmentTestUtil.waitForExecution(mActivityRule);
285 
286         assertPostponed(fragment, 0);
287         fragment.startPostponedEnterTransition();
288 
289         FragmentTestUtil.waitForExecution(mActivityRule);
290         assertEnterPopExit(fragment);
291     }
292 
293     // Ensure that removing and popping a Fragment uses the exit and popEnter animators,
294     // but the animators are delayed when an entering Fragment is postponed.
295     @Test
postponedRemoveAnimators()296     public void postponedRemoveAnimators() throws Throwable {
297         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
298 
299         final AnimatorFragment fragment = new AnimatorFragment();
300         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
301         FragmentTestUtil.waitForExecution(mActivityRule);
302 
303         fm.beginTransaction()
304                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
305                 .remove(fragment)
306                 .addToBackStack(null)
307                 .commit();
308         FragmentTestUtil.waitForExecution(mActivityRule);
309 
310         assertExitPostponedPopEnter(fragment);
311     }
312 
313     // Ensure that adding and popping a Fragment is postponed in both directions
314     // when the fragments have been marked for postponing.
315     @Test
postponedAddRemove()316     public void postponedAddRemove() throws Throwable {
317         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
318 
319         final AnimatorFragment fragment1 = new AnimatorFragment();
320         fm.beginTransaction()
321                 .add(R.id.fragmentContainer, fragment1)
322                 .addToBackStack(null)
323                 .commit();
324         FragmentTestUtil.waitForExecution(mActivityRule);
325 
326         final AnimatorFragment fragment2 = new AnimatorFragment();
327         fragment2.postponeEnterTransition();
328 
329         fm.beginTransaction()
330                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
331                 .replace(R.id.fragmentContainer, fragment2)
332                 .addToBackStack(null)
333                 .commit();
334 
335         FragmentTestUtil.waitForExecution(mActivityRule);
336 
337         assertPostponed(fragment2, 0);
338         assertNotNull(fragment1.getView());
339         assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
340         assertTrue(FragmentTestUtil.isVisible(fragment1));
341         assertTrue(fragment1.getView().isAttachedToWindow());
342 
343         fragment2.startPostponedEnterTransition();
344         FragmentTestUtil.waitForExecution(mActivityRule);
345 
346         assertExitPostponedPopEnter(fragment1);
347     }
348 
349     // Popping a postponed transaction should result in no animators
350     @Test
popPostponed()351     public void popPostponed() throws Throwable {
352         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
353 
354         final AnimatorFragment fragment1 = new AnimatorFragment();
355         fm.beginTransaction()
356                 .add(R.id.fragmentContainer, fragment1)
357                 .commit();
358         FragmentTestUtil.waitForExecution(mActivityRule);
359         assertEquals(0, fragment1.numAnimators);
360 
361         final AnimatorFragment fragment2 = new AnimatorFragment();
362         fragment2.postponeEnterTransition();
363 
364         fm.beginTransaction()
365                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
366                 .replace(R.id.fragmentContainer, fragment2)
367                 .addToBackStack(null)
368                 .commit();
369 
370         FragmentTestUtil.waitForExecution(mActivityRule);
371 
372         assertPostponed(fragment2, 0);
373 
374         // Now pop the postponed transaction
375         FragmentTestUtil.popBackStackImmediate(mActivityRule);
376 
377         assertNotNull(fragment1.getView());
378         assertTrue(FragmentTestUtil.isVisible(fragment1));
379         assertTrue(fragment1.getView().isAttachedToWindow());
380         assertTrue(fragment1.isAdded());
381 
382         assertNull(fragment2.getView());
383         assertFalse(fragment2.isAdded());
384 
385         assertEquals(0, fragment1.numAnimators);
386         assertEquals(0, fragment2.numAnimators);
387         assertNull(fragment1.animator);
388         assertNull(fragment2.animator);
389     }
390 
391     // Make sure that if the state was saved while a Fragment was animating that its
392     // state is proper after restoring.
393     @Test
saveWhileAnimatingAway()394     public void saveWhileAnimatingAway() throws Throwable {
395         final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
396         FragmentTestUtil.resume(mActivityRule, fc1, null);
397 
398         final FragmentManager fm1 = fc1.getFragmentManager();
399 
400         StrictViewFragment fragment1 = new StrictViewFragment();
401         fragment1.setLayoutId(R.layout.scene1);
402         fm1.beginTransaction()
403                 .add(R.id.fragmentContainer, fragment1, "1")
404                 .commit();
405         FragmentTestUtil.waitForExecution(mActivityRule);
406 
407         StrictViewFragment fragment2 = new StrictViewFragment();
408 
409         fm1.beginTransaction()
410                 .setCustomAnimations(0, 0, 0, R.animator.slow_fade_out)
411                 .replace(R.id.fragmentContainer, fragment2, "2")
412                 .addToBackStack(null)
413                 .commit();
414         mActivityRule.runOnUiThread(fm1::executePendingTransactions);
415         FragmentTestUtil.waitForExecution(mActivityRule);
416 
417         fm1.popBackStack();
418 
419         mActivityRule.runOnUiThread(fm1::executePendingTransactions);
420         FragmentTestUtil.waitForExecution(mActivityRule);
421         // Now fragment2 should be animating away
422         assertFalse(fragment2.isAdded());
423         assertEquals(fragment2, fm1.findFragmentByTag("2")); // still exists because it is animating
424 
425         Pair<Parcelable, FragmentManagerNonConfig> state =
426                 FragmentTestUtil.destroy(mActivityRule, fc1);
427 
428         final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
429         FragmentTestUtil.resume(mActivityRule, fc2, state);
430 
431         final FragmentManager fm2 = fc2.getFragmentManager();
432         Fragment fragment2restored = fm2.findFragmentByTag("2");
433         assertNull(fragment2restored);
434 
435         Fragment fragment1restored = fm2.findFragmentByTag("1");
436         assertNotNull(fragment1restored);
437         assertNotNull(fragment1restored.getView());
438     }
439 
440     // When an animation is running on a Fragment's View, the view shouldn't be
441     // prevented from being removed. There's no way to directly test this, so we have to
442     // test to see if the animation is still running.
443     @Test
clearAnimations()444     public void clearAnimations() throws Throwable {
445         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
446 
447         final StrictViewFragment fragment1 = new StrictViewFragment();
448         fm.beginTransaction()
449                 .add(R.id.fragmentContainer, fragment1)
450                 .addToBackStack(null)
451                 .commit();
452         FragmentTestUtil.waitForExecution(mActivityRule);
453 
454         final View fragmentView = fragment1.getView();
455 
456         final TranslateAnimation xAnimation = new TranslateAnimation(0, 1000, 0, 0);
457         xAnimation.setDuration(10000);
458         mActivityRule.runOnUiThread(() -> {
459             fragmentView.startAnimation(xAnimation);
460             assertEquals(xAnimation, fragmentView.getAnimation());
461         });
462 
463         FragmentTestUtil.waitForExecution(mActivityRule);
464         FragmentTestUtil.popBackStackImmediate(mActivityRule);
465         mActivityRule.runOnUiThread(() -> {
466             assertNull(fragmentView.getAnimation());
467         });
468     }
469 
470     /**
471      * When a fragment container is null, you shouldn't see an NPE even with an animation.
472      */
473     @Test
animationOnNullContainer()474     public void animationOnNullContainer() throws Throwable {
475         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
476 
477         // One fragment with a view
478         final AnimatorFragment fragment = new AnimatorFragment();
479         fm.beginTransaction()
480                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
481                 .add(fragment, "1")
482                 .addToBackStack(null)
483                 .commit();
484         FragmentTestUtil.waitForExecution(mActivityRule);
485 
486         fm.beginTransaction()
487                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
488                 .hide(fragment)
489                 .commit();
490         FragmentTestUtil.waitForExecution(mActivityRule);
491 
492         fm.beginTransaction()
493                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
494                 .show(fragment)
495                 .commit();
496 
497         FragmentTestUtil.waitForExecution(mActivityRule);
498 
499         FragmentTestUtil.popBackStackImmediate(mActivityRule);
500     }
501 
assertEnterPopExit(AnimatorFragment fragment)502     private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
503         assertFragmentAnimation(fragment, 1, true, ENTER);
504 
505         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
506         fm.popBackStack();
507         FragmentTestUtil.waitForExecution(mActivityRule);
508 
509         assertFragmentAnimation(fragment, 2, false, POP_EXIT);
510     }
511 
assertExitPopEnter(AnimatorFragment fragment)512     private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
513         assertFragmentAnimation(fragment, 1, false, EXIT);
514 
515         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
516         fm.popBackStack();
517         FragmentTestUtil.waitForExecution(mActivityRule);
518 
519         AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
520 
521         boolean isSameFragment = replacement == fragment;
522         int expectedAnimators = isSameFragment ? 2 : 1;
523         assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
524     }
525 
assertExitPostponedPopEnter(AnimatorFragment fragment)526     private void assertExitPostponedPopEnter(AnimatorFragment fragment) throws Throwable {
527         assertFragmentAnimation(fragment, 1, false, EXIT);
528 
529         fragment.postponeEnterTransition();
530         FragmentTestUtil.popBackStackImmediate(mActivityRule);
531 
532         assertPostponed(fragment, 1);
533 
534         fragment.startPostponedEnterTransition();
535         FragmentTestUtil.waitForExecution(mActivityRule);
536         assertFragmentAnimation(fragment, 2, true, POP_ENTER);
537     }
538 
assertFragmentAnimation(AnimatorFragment fragment, int numAnimators, boolean isEnter, int animatorResourceId)539     private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
540             boolean isEnter, int animatorResourceId) throws InterruptedException {
541         assertEquals(numAnimators, fragment.numAnimators);
542         assertEquals(isEnter, fragment.enter);
543         assertEquals(animatorResourceId, fragment.resourceId);
544         assertNotNull(fragment.animator);
545         assertTrue(fragment.wasStarted);
546         assertTrue(fragment.endLatch.await(1, TimeUnit.SECONDS));
547     }
548 
assertPostponed(AnimatorFragment fragment, int expectedAnimators)549     private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
550             throws InterruptedException {
551         assertTrue(fragment.mOnCreateViewCalled);
552         assertEquals(View.VISIBLE, fragment.getView().getVisibility());
553         assertFalse(FragmentTestUtil.isVisible(fragment));
554         assertEquals(expectedAnimators, fragment.numAnimators);
555     }
556 
557     public static class AnimatorFragment extends StrictViewFragment {
558         int numAnimators;
559         Animator animator;
560         boolean enter;
561         int resourceId;
562         boolean wasStarted;
563         CountDownLatch endLatch;
564 
565         @Override
onCreateAnimator(int transit, boolean enter, int nextAnim)566         public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
567             if (nextAnim == 0) {
568                 return null;
569             }
570             this.numAnimators++;
571             this.wasStarted = false;
572             this.animator = ValueAnimator.ofFloat(0, 1).setDuration(1);
573             this.endLatch = new CountDownLatch(1);
574             this.animator.addListener(new AnimatorListenerAdapter() {
575                 @Override
576                 public void onAnimationStart(Animator animation) {
577                     wasStarted = true;
578                 }
579 
580                 @Override
581                 public void onAnimationEnd(Animator animation) {
582                     endLatch.countDown();
583                 }
584             });
585             this.resourceId = nextAnim;
586             this.enter = enter;
587             return this.animator;
588         }
589     }
590 }
591