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