• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 
17 package com.android.launcher3;
18 
19 import static com.android.launcher3.LauncherState.NORMAL;
20 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
21 
22 import android.animation.Animator;
23 import android.animation.AnimatorListenerAdapter;
24 import android.animation.AnimatorSet;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.util.Log;
28 
29 import com.android.launcher3.anim.AnimationSuccessListener;
30 import com.android.launcher3.anim.AnimatorPlaybackController;
31 import com.android.launcher3.anim.AnimatorSetBuilder;
32 import com.android.launcher3.anim.PropertySetter;
33 import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
34 import com.android.launcher3.compat.AccessibilityManagerCompat;
35 import com.android.launcher3.testing.TestProtocol;
36 import com.android.launcher3.uioverrides.UiFactory;
37 
38 import java.io.PrintWriter;
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 import java.util.ArrayList;
42 
43 import androidx.annotation.IntDef;
44 
45 /**
46  * TODO: figure out what kind of tests we can write for this
47  *
48  * Things to test when changing the following class.
49  *   - Home from workspace
50  *          - from center screen
51  *          - from other screens
52  *   - Home from all apps
53  *          - from center screen
54  *          - from other screens
55  *   - Back from all apps
56  *          - from center screen
57  *          - from other screens
58  *   - Launch app from workspace and quit
59  *          - with back
60  *          - with home
61  *   - Launch app from all apps and quit
62  *          - with back
63  *          - with home
64  *   - Go to a screen that's not the default, then all
65  *     apps, and launch and app, and go back
66  *          - with back
67  *          -with home
68  *   - On workspace, long press power and go back
69  *          - with back
70  *          - with home
71  *   - On all apps, long press power and go back
72  *          - with back
73  *          - with home
74  *   - On workspace, power off
75  *   - On all apps, power off
76  *   - Launch an app and turn off the screen while in that app
77  *          - Go back with home key
78  *          - Go back with back key  TODO: make this not go to workspace
79  *          - From all apps
80  *          - From workspace
81  *   - Enter and exit car mode (becase it causes an extra configuration changed)
82  *          - From all apps
83  *          - From the center workspace
84  *          - From another workspace
85  */
86 public class LauncherStateManager {
87 
88     public static final String TAG = "StateManager";
89 
90     // We separate the state animations into "atomic" and "non-atomic" components. The atomic
91     // components may be run atomically - that is, all at once, instead of user-controlled. However,
92     // atomic components are not restricted to this purpose; they can be user-controlled alongside
93     // non atomic components as well. Note that each gesture model has exactly one atomic component,
94     // ATOMIC_OVERVIEW_SCALE_COMPONENT *or* ATOMIC_OVERVIEW_PEEK_COMPONENT.
95     @IntDef(flag = true, value = {
96             NON_ATOMIC_COMPONENT,
97             ATOMIC_OVERVIEW_SCALE_COMPONENT,
98             ATOMIC_OVERVIEW_PEEK_COMPONENT,
99     })
100     @Retention(RetentionPolicy.SOURCE)
101     public @interface AnimationComponents {}
102     public static final int NON_ATOMIC_COMPONENT = 1 << 0;
103     public static final int ATOMIC_OVERVIEW_SCALE_COMPONENT = 1 << 1;
104     public static final int ATOMIC_OVERVIEW_PEEK_COMPONENT = 1 << 2;
105 
106     public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_OVERVIEW_SCALE_COMPONENT
107             | ATOMIC_OVERVIEW_PEEK_COMPONENT;
108 
109     private final AnimationConfig mConfig = new AnimationConfig();
110     private final Handler mUiHandler;
111     private final Launcher mLauncher;
112     private final ArrayList<StateListener> mListeners = new ArrayList<>();
113 
114     // Animators which are run on properties also controlled by state animations.
115     private Animator[] mStateElementAnimators;
116 
117     private StateHandler[] mStateHandlers;
118     private LauncherState mState = NORMAL;
119 
120     private LauncherState mLastStableState = NORMAL;
121     private LauncherState mCurrentStableState = NORMAL;
122 
123     private LauncherState mRestState;
124 
LauncherStateManager(Launcher l)125     public LauncherStateManager(Launcher l) {
126         mUiHandler = new Handler(Looper.getMainLooper());
127         mLauncher = l;
128     }
129 
getState()130     public LauncherState getState() {
131         return mState;
132     }
133 
getCurrentStableState()134     public LauncherState getCurrentStableState() {
135         return mCurrentStableState;
136     }
137 
dump(String prefix, PrintWriter writer)138     public void dump(String prefix, PrintWriter writer) {
139         writer.println(prefix + "LauncherState");
140         writer.println(prefix + "\tmLastStableState:" + mLastStableState);
141         writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
142         writer.println(prefix + "\tmState:" + mState);
143         writer.println(prefix + "\tmRestState:" + mRestState);
144         writer.println(prefix + "\tisInTransition:" + (mConfig.mCurrentAnimation != null));
145     }
146 
getStateHandlers()147     public StateHandler[] getStateHandlers() {
148         if (mStateHandlers == null) {
149             mStateHandlers = UiFactory.getStateHandler(mLauncher);
150         }
151         return mStateHandlers;
152     }
153 
addStateListener(StateListener listener)154     public void addStateListener(StateListener listener) {
155         mListeners.add(listener);
156     }
157 
removeStateListener(StateListener listener)158     public void removeStateListener(StateListener listener) {
159         mListeners.remove(listener);
160     }
161 
162     /**
163      * Returns true if the state changes should be animated.
164      */
shouldAnimateStateChange()165     public boolean shouldAnimateStateChange() {
166         return !mLauncher.isForceInvisible() && mLauncher.isStarted();
167     }
168 
169     /**
170      * @see #goToState(LauncherState, boolean, Runnable)
171      */
goToState(LauncherState state)172     public void goToState(LauncherState state) {
173         goToState(state, shouldAnimateStateChange());
174     }
175 
176     /**
177      * @see #goToState(LauncherState, boolean, Runnable)
178      */
goToState(LauncherState state, boolean animated)179     public void goToState(LauncherState state, boolean animated) {
180         goToState(state, animated, 0, null);
181     }
182 
183     /**
184      * Changes the Launcher state to the provided state.
185      *
186      * @param animated false if the state should change immediately without any animation,
187      *                true otherwise
188      * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
189      */
goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable)190     public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) {
191         goToState(state, animated, 0, onCompleteRunnable);
192     }
193 
194     /**
195      * Changes the Launcher state to the provided state after the given delay.
196      */
goToState(LauncherState state, long delay, Runnable onCompleteRunnable)197     public void goToState(LauncherState state, long delay, Runnable onCompleteRunnable) {
198         goToState(state, true, delay, onCompleteRunnable);
199     }
200 
201     /**
202      * Changes the Launcher state to the provided state after the given delay.
203      */
goToState(LauncherState state, long delay)204     public void goToState(LauncherState state, long delay) {
205         goToState(state, true, delay, null);
206     }
207 
reapplyState()208     public void reapplyState() {
209         reapplyState(false);
210     }
211 
reapplyState(boolean cancelCurrentAnimation)212     public void reapplyState(boolean cancelCurrentAnimation) {
213         boolean wasInAnimation = mConfig.mCurrentAnimation != null;
214         if (cancelCurrentAnimation) {
215             cancelAllStateElementAnimation();
216             cancelAnimation();
217         }
218         if (mConfig.mCurrentAnimation == null) {
219             for (StateHandler handler : getStateHandlers()) {
220                 handler.setState(mState);
221             }
222             if (wasInAnimation) {
223                 onStateTransitionEnd(mState);
224             }
225         }
226     }
227 
goToState(LauncherState state, boolean animated, long delay, final Runnable onCompleteRunnable)228     private void goToState(LauncherState state, boolean animated, long delay,
229             final Runnable onCompleteRunnable) {
230         animated &= Utilities.areAnimationsEnabled(mLauncher);
231         if (mLauncher.isInState(state)) {
232             if (mConfig.mCurrentAnimation == null) {
233                 // Run any queued runnable
234                 if (onCompleteRunnable != null) {
235                     onCompleteRunnable.run();
236                 }
237                 return;
238             } else if (!mConfig.userControlled && animated && mConfig.mTargetState == state) {
239                 // We are running the same animation as requested
240                 if (onCompleteRunnable != null) {
241                     mConfig.mCurrentAnimation.addListener(new AnimationSuccessListener() {
242                         @Override
243                         public void onAnimationSuccess(Animator animator) {
244                             onCompleteRunnable.run();
245                         }
246                     });
247                 }
248                 return;
249             }
250         }
251 
252         // Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
253         LauncherState fromState = mState;
254         mConfig.reset();
255 
256         if (!animated) {
257             cancelAllStateElementAnimation();
258             onStateTransitionStart(state);
259             for (StateHandler handler : getStateHandlers()) {
260                 handler.setState(state);
261             }
262 
263             onStateTransitionEnd(state);
264 
265             // Run any queued runnable
266             if (onCompleteRunnable != null) {
267                 onCompleteRunnable.run();
268             }
269             return;
270         }
271 
272         if (delay > 0) {
273             // Create the animation after the delay as some properties can change between preparing
274             // the animation and running the animation.
275             int startChangeId = mConfig.mChangeId;
276             mUiHandler.postDelayed(() -> {
277                 if (mConfig.mChangeId == startChangeId) {
278                     goToStateAnimated(state, fromState, onCompleteRunnable);
279                 }
280             }, delay);
281         } else {
282             goToStateAnimated(state, fromState, onCompleteRunnable);
283         }
284     }
285 
goToStateAnimated(LauncherState state, LauncherState fromState, Runnable onCompleteRunnable)286     private void goToStateAnimated(LauncherState state, LauncherState fromState,
287             Runnable onCompleteRunnable) {
288         // Since state NORMAL can be reached from multiple states, just assume that the
289         // transition plays in reverse and use the same duration as previous state.
290         mConfig.duration = state == NORMAL ? fromState.transitionDuration : state.transitionDuration;
291 
292         AnimatorSetBuilder builder = new AnimatorSetBuilder();
293         prepareForAtomicAnimation(fromState, state, builder);
294         AnimatorSet animation = createAnimationToNewWorkspaceInternal(
295                 state, builder, onCompleteRunnable);
296         mUiHandler.post(new StartAnimRunnable(animation));
297     }
298 
299     /**
300      * Prepares for a non-user controlled animation from fromState to toState. Preparations include:
301      * - Setting interpolators for various animations included in the state transition.
302      * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
303      */
prepareForAtomicAnimation(LauncherState fromState, LauncherState toState, AnimatorSetBuilder builder)304     public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
305             AnimatorSetBuilder builder) {
306         toState.prepareForAtomicAnimation(mLauncher, fromState, builder);
307     }
308 
createAtomicAnimation(LauncherState fromState, LauncherState toState, AnimatorSetBuilder builder, @AnimationComponents int atomicComponent, long duration)309     public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
310             AnimatorSetBuilder builder, @AnimationComponents int atomicComponent, long duration) {
311         prepareForAtomicAnimation(fromState, toState, builder);
312         AnimationConfig config = new AnimationConfig();
313         config.animComponents = atomicComponent;
314         config.duration = duration;
315         for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
316             handler.setStateWithAnimation(toState, builder, config);
317         }
318         return builder.build();
319     }
320 
321     /**
322      * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
323      * state transition. The UI is force-set to fromState before creating the controller.
324      * @param fromState the initial state for the transition.
325      * @param state the final state for the transition.
326      * @param duration intended duration for normal playback. Use higher duration for better
327      *                accuracy.
328      */
createAnimationToNewWorkspace( LauncherState fromState, LauncherState state, long duration)329     public AnimatorPlaybackController createAnimationToNewWorkspace(
330             LauncherState fromState, LauncherState state, long duration) {
331         // Since we are creating a state animation to a different state, temporarily prevent state
332         // change as part of config reset.
333         LauncherState originalRestState = mRestState;
334         mRestState = state;
335         mConfig.reset();
336         mRestState = originalRestState;
337 
338         for (StateHandler handler : getStateHandlers()) {
339             handler.setState(fromState);
340         }
341 
342         return createAnimationToNewWorkspace(state, duration);
343     }
344 
345     /**
346      * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
347      * state transition.
348      * @param state the final state for the transition.
349      * @param duration intended duration for normal playback. Use higher duration for better
350      *                accuracy.
351      */
createAnimationToNewWorkspace( LauncherState state, long duration)352     public AnimatorPlaybackController createAnimationToNewWorkspace(
353             LauncherState state, long duration) {
354         return createAnimationToNewWorkspace(state, duration, LauncherStateManager.ANIM_ALL);
355     }
356 
createAnimationToNewWorkspace( LauncherState state, long duration, @AnimationComponents int animComponents)357     public AnimatorPlaybackController createAnimationToNewWorkspace(
358             LauncherState state, long duration, @AnimationComponents int animComponents) {
359         return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null,
360                 animComponents);
361     }
362 
createAnimationToNewWorkspace(LauncherState state, AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable, @AnimationComponents int animComponents)363     public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
364             AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable,
365             @AnimationComponents int animComponents) {
366         mConfig.reset();
367         mConfig.userControlled = true;
368         mConfig.animComponents = animComponents;
369         mConfig.duration = duration;
370         mConfig.playbackController = AnimatorPlaybackController.wrap(
371                 createAnimationToNewWorkspaceInternal(state, builder, null), duration,
372                 onCancelRunnable);
373         return mConfig.playbackController;
374     }
375 
createAnimationToNewWorkspaceInternal(final LauncherState state, AnimatorSetBuilder builder, final Runnable onCompleteRunnable)376     protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
377             AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
378 
379         for (StateHandler handler : getStateHandlers()) {
380             handler.setStateWithAnimation(state, builder, mConfig);
381         }
382 
383         final AnimatorSet animation = builder.build();
384         animation.addListener(new AnimationSuccessListener() {
385 
386             @Override
387             public void onAnimationStart(Animator animation) {
388                 // Change the internal state only when the transition actually starts
389                 onStateTransitionStart(state);
390             }
391 
392             @Override
393             public void onAnimationSuccess(Animator animator) {
394                 // Run any queued runnables
395                 if (onCompleteRunnable != null) {
396                     onCompleteRunnable.run();
397                 }
398                 onStateTransitionEnd(state);
399             }
400         });
401         mConfig.setAnimation(animation, state);
402         return mConfig.mCurrentAnimation;
403     }
404 
onStateTransitionStart(LauncherState state)405     private void onStateTransitionStart(LauncherState state) {
406         if (TestProtocol.sDebugTracing) {
407             android.util.Log.d(TestProtocol.NO_DRAG_TAG,
408                     "onStateTransitionStart");
409         }
410         if (mState != state) {
411             mState.onStateDisabled(mLauncher);
412         }
413         mState = state;
414         mState.onStateEnabled(mLauncher);
415         mLauncher.onStateSet(mState);
416 
417         if (state.disablePageClipping) {
418             // Only disable clipping if needed, otherwise leave it as previous value.
419             mLauncher.getWorkspace().setClipChildren(false);
420         }
421         UiFactory.onLauncherStateOrResumeChanged(mLauncher);
422 
423         for (int i = mListeners.size() - 1; i >= 0; i--) {
424             mListeners.get(i).onStateTransitionStart(state);
425         }
426     }
427 
onStateTransitionEnd(LauncherState state)428     private void onStateTransitionEnd(LauncherState state) {
429         // Only change the stable states after the transitions have finished
430         if (state != mCurrentStableState) {
431             mLastStableState = state.getHistoryForState(mCurrentStableState);
432             if (TestProtocol.sDebugTracing) {
433                 Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
434                         "mCurrentStableState = " + state.getClass().getSimpleName() + " @ " +
435                                 android.util.Log.getStackTraceString(new Throwable()));
436             }
437             mCurrentStableState = state;
438         }
439 
440         state.onStateTransitionEnd(mLauncher);
441         mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
442         mLauncher.finishAutoCancelActionMode();
443 
444         if (state == NORMAL) {
445             setRestState(null);
446         }
447 
448         UiFactory.onLauncherStateOrResumeChanged(mLauncher);
449 
450         for (int i = mListeners.size() - 1; i >= 0; i--) {
451             mListeners.get(i).onStateTransitionComplete(state);
452         }
453 
454         AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
455     }
456 
onWindowFocusChanged()457     public void onWindowFocusChanged() {
458         UiFactory.onLauncherStateOrFocusChanged(mLauncher);
459     }
460 
getLastState()461     public LauncherState getLastState() {
462         return mLastStableState;
463     }
464 
moveToRestState()465     public void moveToRestState() {
466         if (mConfig.mCurrentAnimation != null && mConfig.userControlled) {
467             // The user is doing something. Lets not mess it up
468             return;
469         }
470         if (mState.disableRestore) {
471             goToState(getRestState());
472             // Reset history
473             mLastStableState = NORMAL;
474         }
475     }
476 
getRestState()477     public LauncherState getRestState() {
478         return mRestState == null ? NORMAL : mRestState;
479     }
480 
setRestState(LauncherState restState)481     public void setRestState(LauncherState restState) {
482         mRestState = restState;
483     }
484 
485     /**
486      * Cancels the current animation.
487      */
cancelAnimation()488     public void cancelAnimation() {
489         mConfig.reset();
490     }
491 
setCurrentUserControlledAnimation(AnimatorPlaybackController controller)492     public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
493         clearCurrentAnimation();
494         setCurrentAnimation(controller.getTarget());
495         mConfig.userControlled = true;
496         mConfig.playbackController = controller;
497     }
498 
499     /**
500      * Sets the animation as the current state animation, i.e., canceled when
501      * starting another animation and may block some launcher interactions while running.
502      *
503      * @param childAnimations Set of animations with the new target is controlling.
504      */
setCurrentAnimation(AnimatorSet anim, Animator... childAnimations)505     public void setCurrentAnimation(AnimatorSet anim, Animator... childAnimations) {
506         for (Animator childAnim : childAnimations) {
507             if (childAnim == null) {
508                 continue;
509             }
510             if (mConfig.playbackController != null
511                     && mConfig.playbackController.getTarget() == childAnim) {
512                 clearCurrentAnimation();
513                 break;
514             } else if (mConfig.mCurrentAnimation == childAnim) {
515                 clearCurrentAnimation();
516                 break;
517             }
518         }
519         boolean reapplyNeeded = mConfig.mCurrentAnimation != null;
520         cancelAnimation();
521         if (reapplyNeeded) {
522             reapplyState();
523             // Dispatch on transition end, so that any transient property is cleared.
524             onStateTransitionEnd(mState);
525         }
526         mConfig.setAnimation(anim, null);
527     }
528 
cancelAllStateElementAnimation()529     private void cancelAllStateElementAnimation() {
530         if (mStateElementAnimators == null) {
531             return;
532         }
533 
534         for (Animator animator : mStateElementAnimators) {
535             if (animator != null) {
536                 animator.cancel();
537             }
538         }
539     }
540 
541     /**
542      * Cancels a currently running gesture animation
543      */
cancelStateElementAnimation(int index)544     public void cancelStateElementAnimation(int index) {
545         if (mStateElementAnimators == null) {
546             return;
547         }
548         if (mStateElementAnimators[index] != null) {
549             mStateElementAnimators[index].cancel();
550         }
551     }
552 
createStateElementAnimation(int index, float... values)553     public Animator createStateElementAnimation(int index, float... values) {
554         cancelStateElementAnimation(index);
555         LauncherAppTransitionManager latm = mLauncher.getAppTransitionManager();
556         if (mStateElementAnimators == null) {
557             mStateElementAnimators = new Animator[latm.getStateElementAnimationsCount()];
558         }
559         Animator anim = latm.createStateElementAnimation(index, values);
560         mStateElementAnimators[index] = anim;
561         anim.addListener(new AnimatorListenerAdapter() {
562             @Override
563             public void onAnimationEnd(Animator animation) {
564                 mStateElementAnimators[index] = null;
565             }
566         });
567         return anim;
568     }
569 
clearCurrentAnimation()570     private void clearCurrentAnimation() {
571         if (mConfig.mCurrentAnimation != null) {
572             mConfig.mCurrentAnimation.removeListener(mConfig);
573             mConfig.mCurrentAnimation = null;
574         }
575         mConfig.playbackController = null;
576     }
577 
578     private class StartAnimRunnable implements Runnable {
579 
580         private final AnimatorSet mAnim;
581 
StartAnimRunnable(AnimatorSet anim)582         public StartAnimRunnable(AnimatorSet anim) {
583             if (TestProtocol.sDebugTracing) {
584                 android.util.Log.d(TestProtocol.NO_DRAG_TAG,
585                         "StartAnimRunnable");
586             }
587             mAnim = anim;
588         }
589 
590         @Override
run()591         public void run() {
592             if (mConfig.mCurrentAnimation != mAnim) {
593                 return;
594             }
595             mAnim.start();
596         }
597     }
598 
599     public static class AnimationConfig extends AnimatorListenerAdapter {
600         public long duration;
601         public boolean userControlled;
602         public AnimatorPlaybackController playbackController;
603         public @AnimationComponents int animComponents = ANIM_ALL;
604         private PropertySetter mPropertySetter;
605 
606         private AnimatorSet mCurrentAnimation;
607         private LauncherState mTargetState;
608         // Id to keep track of config changes, to tie an animation with the corresponding request
609         private int mChangeId = 0;
610 
611         /**
612          * Cancels the current animation and resets config variables.
613          */
reset()614         public void reset() {
615             duration = 0;
616             userControlled = false;
617             animComponents = ANIM_ALL;
618             mPropertySetter = null;
619             mTargetState = null;
620 
621             if (playbackController != null) {
622                 playbackController.getAnimationPlayer().cancel();
623                 playbackController.dispatchOnCancel();
624             } else if (mCurrentAnimation != null) {
625                 mCurrentAnimation.setDuration(0);
626                 mCurrentAnimation.cancel();
627             }
628 
629             mCurrentAnimation = null;
630             playbackController = null;
631             mChangeId ++;
632         }
633 
getPropertySetter(AnimatorSetBuilder builder)634         public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
635             if (mPropertySetter == null) {
636                 mPropertySetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
637                         : new AnimatedPropertySetter(duration, builder);
638             }
639             return mPropertySetter;
640         }
641 
642         @Override
onAnimationEnd(Animator animation)643         public void onAnimationEnd(Animator animation) {
644             if (playbackController != null && playbackController.getTarget() == animation) {
645                 playbackController = null;
646             }
647             if (mCurrentAnimation == animation) {
648                 mCurrentAnimation = null;
649             }
650         }
651 
setAnimation(AnimatorSet animation, LauncherState targetState)652         public void setAnimation(AnimatorSet animation, LauncherState targetState) {
653             mCurrentAnimation = animation;
654             mTargetState = targetState;
655             mCurrentAnimation.addListener(this);
656         }
657 
playAtomicOverviewScaleComponent()658         public boolean playAtomicOverviewScaleComponent() {
659             return (animComponents & ATOMIC_OVERVIEW_SCALE_COMPONENT) != 0;
660         }
661 
playAtomicOverviewPeekComponent()662         public boolean playAtomicOverviewPeekComponent() {
663             return (animComponents & ATOMIC_OVERVIEW_PEEK_COMPONENT) != 0;
664         }
665 
playNonAtomicComponent()666         public boolean playNonAtomicComponent() {
667             return (animComponents & NON_ATOMIC_COMPONENT) != 0;
668         }
669     }
670 
671     public interface StateHandler {
672 
673         /**
674          * Updates the UI to {@param state} without any animations
675          */
setState(LauncherState state)676         void setState(LauncherState state);
677 
678         /**
679          * Sets the UI to {@param state} by animating any changes.
680          */
setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config)681         void setStateWithAnimation(LauncherState toState,
682                 AnimatorSetBuilder builder, AnimationConfig config);
683     }
684 
685     public interface StateListener {
686 
onStateTransitionStart(LauncherState toState)687         void onStateTransitionStart(LauncherState toState);
onStateTransitionComplete(LauncherState finalState)688         void onStateTransitionComplete(LauncherState finalState);
689     }
690 }
691