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