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