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