1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.taskbar; 17 18 import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS; 19 20 import static com.android.app.animation.Interpolators.EMPHASIZED; 21 import static com.android.app.animation.Interpolators.FINAL_FRAME; 22 import static com.android.app.animation.Interpolators.INSTANT; 23 import static com.android.app.animation.Interpolators.LINEAR; 24 import static com.android.internal.jank.InteractionJankMonitor.Configuration; 25 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; 26 import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash; 27 import static com.android.launcher3.QuickstepTransitionManager.PINNED_TASKBAR_TRANSITION_DURATION; 28 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; 29 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE; 30 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW; 31 import static com.android.launcher3.taskbar.TaskbarActivityContext.ENABLE_TASKBAR_BEHIND_SHADE; 32 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 33 import static com.android.launcher3.util.FlagDebugUtils.appendFlag; 34 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; 35 import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR; 36 import static com.android.quickstep.util.SystemUiFlagUtils.isTaskbarHidden; 37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED; 38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING; 39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE; 40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; 41 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; 42 43 import android.animation.Animator; 44 import android.animation.AnimatorListenerAdapter; 45 import android.animation.AnimatorSet; 46 import android.animation.ValueAnimator; 47 import android.app.RemoteAction; 48 import android.graphics.drawable.Icon; 49 import android.os.SystemClock; 50 import android.util.Log; 51 import android.view.InsetsController; 52 import android.view.View; 53 import android.view.ViewConfiguration; 54 import android.view.accessibility.AccessibilityManager; 55 import android.view.animation.Interpolator; 56 57 import androidx.annotation.IntDef; 58 import androidx.annotation.NonNull; 59 import androidx.annotation.Nullable; 60 import androidx.annotation.VisibleForTesting; 61 62 import com.android.internal.jank.InteractionJankMonitor; 63 import com.android.launcher3.Alarm; 64 import com.android.launcher3.DeviceProfile; 65 import com.android.launcher3.R; 66 import com.android.launcher3.anim.AnimatedFloat; 67 import com.android.launcher3.anim.AnimationSuccessListener; 68 import com.android.launcher3.anim.AnimatorListeners; 69 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; 70 import com.android.quickstep.SystemUiProxy; 71 import com.android.quickstep.util.SystemUiFlagUtils; 72 73 import java.io.PrintWriter; 74 import java.lang.annotation.Retention; 75 import java.lang.annotation.RetentionPolicy; 76 import java.util.StringJoiner; 77 import java.util.function.LongPredicate; 78 79 /** 80 * Coordinates between controllers such as TaskbarViewController and StashedHandleViewController to 81 * create a cohesive animation between stashed/unstashed states. 82 */ 83 public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController { 84 private static final String TAG = "TaskbarStashController"; 85 private static final boolean DEBUG = false; 86 87 /** 88 * Def. value for @param shouldBubblesFollow in 89 * {@link #updateAndAnimateTransientTaskbar(boolean)} */ 90 public static boolean SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE = true; 91 92 public static final int FLAG_IN_APP = 1 << 0; 93 public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 1; // shade open, ... 94 public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 2; // setup wizard and AllSetActivity 95 public static final int FLAG_STASHED_IME = 1 << 3; // IME is visible 96 public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 4; 97 public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 5; // All apps is visible. 98 public static final int FLAG_IN_SETUP = 1 << 6; // In the Setup Wizard 99 public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 7; // phone screen gesture nav, stashed 100 public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 8; // Autohide (transient taskbar). 101 public static final int FLAG_STASHED_SYSUI = 1 << 9; // app pinning,... 102 public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 10; // device is locked: keyguard, ... 103 public static final int FLAG_IN_OVERVIEW = 1 << 11; // launcher is in overview 104 // An internal no-op flag to determine whether we should delay the taskbar background animation 105 private static final int FLAG_DELAY_TASKBAR_BG_TAG = 1 << 12; 106 public static final int FLAG_STASHED_FOR_BUBBLES = 1 << 13; // show handle for stashed hotseat 107 public static final int FLAG_TASKBAR_HIDDEN = 1 << 14; // taskbar hidden during dream, etc... 108 // taskbar should always be stashed for bubble bar on phone 109 public static final int FLAG_STASHED_BUBBLE_BAR_ON_PHONE = 1 << 15; 110 111 public static final int FLAG_IGNORE_IN_APP = 1 << 16; // used to sync with app launch animation 112 113 // If any of these flags are enabled, isInApp should return true. 114 private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP; 115 116 // If we're in an app and any of these flags are enabled, taskbar should be stashed. 117 private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_SYSUI 118 | FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_TASKBAR_ALL_APPS 119 | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO | FLAG_STASHED_IME; 120 121 // If we're in overview and any of these flags are enabled, taskbar should be stashed. 122 private static final int FLAGS_STASHED_IN_OVERVIEW = FLAG_STASHED_IME; 123 124 // If any of these flags are enabled, inset apps by our stashed height instead of our unstashed 125 // height. This way the reported insets are consistent even during transitions out of the app. 126 // Currently any flag that causes us to stash in an app is included, except for IME or All Apps 127 // since those cover the underlying app anyway and thus the app shouldn't change insets. 128 private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP 129 & ~FLAG_STASHED_IME & ~FLAG_STASHED_IN_TASKBAR_ALL_APPS & ~FLAG_STASHED_IN_APP_SYSUI; 130 131 // If any of these flags are enabled, the taskbar must be stashed. 132 private static final int FLAGS_FORCE_STASHED = FLAG_STASHED_SYSUI | FLAG_STASHED_DEVICE_LOCKED 133 | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN 134 | FLAG_STASHED_FOR_BUBBLES | FLAG_STASHED_BUBBLE_BAR_ON_PHONE; 135 136 /** 137 * How long to stash/unstash when manually invoked via long press. 138 * 139 * Use {@link #getStashDuration()} to query duration 140 */ 141 @VisibleForTesting 142 static final long TASKBAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE; 143 144 /** 145 * How long to stash/unstash transient taskbar. 146 * 147 * Use {@link #getStashDuration()} to query duration. 148 */ 149 @VisibleForTesting 150 static final long TRANSIENT_TASKBAR_STASH_DURATION = 417; 151 152 /** 153 * How long to stash/unstash when keyboard is appearing/disappearing. 154 */ 155 @VisibleForTesting 156 static final long TASKBAR_STASH_DURATION_FOR_IME = 80; 157 158 /** 159 * The scale TaskbarView animates to when being stashed. 160 */ 161 protected static final float STASHED_TASKBAR_SCALE = 0.5f; 162 163 /** 164 * How long the hint animation plays, starting on motion down. 165 */ 166 private static final long TASKBAR_HINT_STASH_DURATION = 167 ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT; 168 169 /** 170 * How long to delay the icon/stash handle alpha. 171 */ 172 public static final long TASKBAR_STASH_ALPHA_START_DELAY = 33; 173 174 /** 175 * How long the icon/stash handle alpha animation plays. 176 */ 177 public static final long TRANSIENT_TASKBAR_STASH_ALPHA_DURATION = 50; 178 179 /** 180 * How long to delay the icon/stash handle alpha for the home to app taskbar animation. 181 */ 182 private static final long TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY = 66; 183 184 /** 185 * The scale that the stashed handle animates to when hinting towards the unstashed state. 186 */ 187 private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f; 188 189 /** 190 * Whether taskbar should be stashed out of the box. 191 */ 192 private static final boolean DEFAULT_STASHED_PREF = false; 193 194 // Auto stashes when user has not interacted with the Taskbar after X ms. 195 private static final long NO_TOUCH_TIMEOUT_TO_STASH_MS = 5000; 196 197 // Duration for which an unlock event is considered "current", as other events are received 198 // asynchronously. 199 public static final long UNLOCK_TRANSITION_MEMOIZATION_MS = 200; 200 201 /** 202 * The default stash animation, morphing the taskbar into the navbar. 203 */ 204 private static final int TRANSITION_DEFAULT = 0; 205 /** 206 * Transitioning from launcher to app. Same as TRANSITION_DEFAULT, differs in internal 207 * animation timings. 208 */ 209 private static final int TRANSITION_HOME_TO_APP = 1; 210 /** 211 * Fading the navbar in and out, where the taskbar jumpcuts in and out at the very begin/end of 212 * the transition. Used to transition between the hotseat and navbar` without the stash/unstash 213 * transition. 214 */ 215 private static final int TRANSITION_HANDLE_FADE = 2; 216 /** 217 * Same as TRANSITION_DEFAULT, but exclusively used during an "navbar unstash to hotseat 218 * animation" bound to the progress of a swipe gesture. It differs from TRANSITION_DEFAULT 219 * by not scaling the height of the taskbar background. 220 */ 221 private static final int TRANSITION_UNSTASH_SUW_MANUAL = 3; 222 223 /** 224 * total duration of entering dream state animation, which we use as start delay to 225 * applyState() when SYSUI_STATE_DEVICE_DREAMING flag is present. Keep this in sync with 226 * DreamAnimationController.TOTAL_ANIM_DURATION. 227 */ 228 private static final int SKIP_TOTAL_DREAM_ANIM_DURATION = 450; 229 230 @Retention(RetentionPolicy.SOURCE) 231 @IntDef(value = { 232 TRANSITION_DEFAULT, 233 TRANSITION_HOME_TO_APP, 234 TRANSITION_HANDLE_FADE, 235 TRANSITION_UNSTASH_SUW_MANUAL, 236 }) 237 private @interface StashAnimation { 238 } 239 240 private final TaskbarActivityContext mActivity; 241 private final int mStashedHeight; 242 private final int mUnstashedHeight; 243 private final SystemUiProxy mSystemUiProxy; 244 245 // Initialized in init. 246 private TaskbarControllers mControllers; 247 // Taskbar background properties. 248 private AnimatedFloat mTaskbarBackgroundOffset; 249 private AnimatedFloat mTaskbarImeBgAlpha; 250 private MultiProperty mTaskbarBackgroundAlphaForStash; 251 // TaskbarView icon properties. 252 private MultiProperty mIconAlphaForStash; 253 private AnimatedFloat mIconScaleForStash; 254 private AnimatedFloat mIconTranslationYForStash; 255 // Stashed handle properties. 256 private MultiProperty mTaskbarStashedHandleAlpha; 257 private AnimatedFloat mTaskbarStashedHandleHintScale; 258 private final AccessibilityManager mAccessibilityManager; 259 260 /** Whether we are currently visually stashed (might change based on launcher state). */ 261 private boolean mIsStashed = false; 262 private long mState; 263 264 private @Nullable AnimatorSet mAnimator; 265 private boolean mIsSystemGestureInProgress; 266 /** Whether the IME is visible. */ 267 private boolean mIsImeVisible; 268 269 private final Alarm mTimeoutAlarm = new Alarm(); 270 private boolean mEnableBlockingTimeoutDuringTests = false; 271 272 private Animator mTaskbarBackgroundAlphaAnimator; 273 private final long mTaskbarBackgroundDuration; 274 private boolean mUserIsNotGoingHome = false; 275 276 private final boolean mInAppStateAffectsDesktopTasksVisibilityInTaskbar; 277 278 // Evaluate whether the handle should be stashed 279 private final LongPredicate mIsStashedPredicate = flags -> { 280 boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP); 281 boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP); 282 boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE); 283 boolean inOverview = hasAnyFlag(flags, FLAG_IN_OVERVIEW); 284 boolean stashedInOverview = hasAnyFlag(flags, FLAGS_STASHED_IN_OVERVIEW); 285 boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED); 286 return (inApp && stashedInApp) 287 || (!inApp && stashedLauncherState) 288 || (inOverview && stashedInOverview) 289 || forceStashed; 290 }; 291 private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder( 292 mIsStashedPredicate); 293 294 private boolean mIsTaskbarSystemActionRegistered = false; 295 private TaskbarSharedState mTaskbarSharedState; 296 TaskbarStashController(TaskbarActivityContext activity)297 public TaskbarStashController(TaskbarActivityContext activity) { 298 mActivity = activity; 299 mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity); 300 mAccessibilityManager = mActivity.getSystemService(AccessibilityManager.class); 301 302 // Taskbar, via `TaskbarDesktopModeController`, depends on `TaskbarStashController` state to 303 // determine whether desktop tasks should be shown because taskbar is pinned on the home 304 // screen for freeform windowing displays. In this case, list of items shown in the taskbar 305 // needs to be updated when in-app state changes. 306 // TODO(b/390665752): Feature to "lock" pinned taskbar to home screen will be superseded by 307 // pinning, in other launcher states, at which point this variable can be removed. 308 mInAppStateAffectsDesktopTasksVisibilityInTaskbar = 309 !mActivity.showDesktopTaskbarForFreeformDisplay() 310 && mActivity.showLockedTaskbarOnHome(); 311 312 mTaskbarBackgroundDuration = activity.getResources().getInteger( 313 R.integer.taskbar_background_duration); 314 if (mActivity.isPhoneMode()) { 315 mUnstashedHeight = mActivity.getResources().getDimensionPixelSize( 316 R.dimen.taskbar_phone_size); 317 mStashedHeight = mActivity.getResources().getDimensionPixelSize( 318 R.dimen.taskbar_stashed_size); 319 } else { 320 mUnstashedHeight = mActivity.getDeviceProfile().taskbarHeight; 321 mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarHeight; 322 } 323 } 324 325 /** 326 * Initializes the controller 327 */ init( TaskbarControllers controllers, boolean setupUIVisible, TaskbarSharedState sharedState)328 public void init( 329 TaskbarControllers controllers, 330 boolean setupUIVisible, 331 TaskbarSharedState sharedState) { 332 mControllers = controllers; 333 mTaskbarSharedState = sharedState; 334 335 TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController; 336 mTaskbarBackgroundOffset = dragLayerController.getTaskbarBackgroundOffset(); 337 mTaskbarImeBgAlpha = dragLayerController.getImeBgTaskbar(); 338 mTaskbarBackgroundAlphaForStash = dragLayerController.getBackgroundRendererAlphaForStash(); 339 340 TaskbarViewController taskbarViewController = controllers.taskbarViewController; 341 mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().get( 342 TaskbarViewController.ALPHA_INDEX_STASH); 343 mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash(); 344 mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash(); 345 346 StashedHandleViewController stashedHandleController = 347 controllers.stashedHandleViewController; 348 mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().get( 349 StashedHandleViewController.ALPHA_INDEX_STASHED); 350 mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale(); 351 352 boolean isTransientTaskbar = mActivity.isTransientTaskbar(); 353 boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible; 354 boolean isStashedInAppAuto = 355 isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned(); 356 357 if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) { 358 isStashedInAppAuto = isStashedInAppAuto && mTaskbarSharedState.taskbarWasStashedAuto; 359 } 360 updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isStashedInAppAuto); 361 updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup); 362 updateStateForFlag(FLAG_IN_SETUP, isInSetup); 363 updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, mActivity.isPhoneGestureNavMode()); 364 // For now, assume we're in an app, since LauncherTaskbarUIController won't be able to tell 365 // us that we're paused until a bit later. This avoids flickering upon recreating taskbar. 366 updateStateForFlag(FLAG_IN_APP, true); 367 updateStateForFlag(FLAG_STASHED_BUBBLE_BAR_ON_PHONE, mActivity.isBubbleBarOnPhone()); 368 369 applyState(/* duration = */ 0); 370 371 // Hide the background while stashed so it doesn't show on fast swipes home 372 boolean shouldHideTaskbarBackground = mActivity.isPhoneMode() || 373 (enableScalingRevealHomeAnimation() && isTransientTaskbar && isStashed()); 374 375 mTaskbarBackgroundAlphaForStash.setValue(shouldHideTaskbarBackground ? 0 : 1); 376 377 if (mTaskbarSharedState.getTaskbarWasPinned() 378 || !mTaskbarSharedState.taskbarWasStashedAuto) { 379 tryStartTaskbarTimeout(); 380 } 381 notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp()); 382 } 383 384 /** 385 * Returns whether the taskbar can visually stash into a handle based on the current device 386 * state. 387 */ supportsVisualStashing()388 public boolean supportsVisualStashing() { 389 return !mActivity.isThreeButtonNav() && mControllers.uiController.supportsVisualStashing(); 390 } 391 392 /** 393 * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar 394 * testing. 395 */ 396 @VisibleForTesting enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout)397 public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) { 398 mEnableBlockingTimeoutDuringTests = enableBlockingTimeout; 399 } 400 401 /** 402 * Sets the flag indicating setup UI is visible 403 */ setSetupUIVisible(boolean isVisible)404 protected void setSetupUIVisible(boolean isVisible) { 405 boolean hideTaskbar = isVisible || !mActivity.isUserSetupComplete(); 406 updateStateForFlag(FLAG_IN_SETUP, hideTaskbar); 407 updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, hideTaskbar); 408 applyState(hideTaskbar ? 0 : getStashDuration()); 409 } 410 411 /** 412 * Returns how long the stash/unstash animation should play. 413 */ getStashDuration()414 public long getStashDuration() { 415 if (mActivity.isPinnedTaskbar()) { 416 return PINNED_TASKBAR_TRANSITION_DURATION; 417 } 418 return mActivity.isTransientTaskbar() ? TRANSIENT_TASKBAR_STASH_DURATION 419 : TASKBAR_STASH_DURATION; 420 } 421 422 /** 423 * Returns whether the taskbar is currently visually stashed. 424 */ isStashed()425 public boolean isStashed() { 426 return mIsStashed; 427 } 428 isDeviceLocked()429 public boolean isDeviceLocked() { 430 return hasAnyFlag(FLAG_STASHED_DEVICE_LOCKED); 431 } 432 433 /** 434 * Sets the hotseat stashed. 435 * b/373429249 - we might change this behavior if we remove the scrim, that's why we're keeping 436 * this method 437 */ stashHotseat(boolean stash)438 public void stashHotseat(boolean stash) { 439 mControllers.uiController.stashHotseat(stash); 440 } 441 442 /** 443 * Instantly un-stashes the hotseat. 444 * * b/373429249 - we might change this behavior if we remove the scrim, that's why we're 445 * keeping this method 446 */ unStashHotseatInstantly()447 public void unStashHotseatInstantly() { 448 mControllers.uiController.unStashHotseatInstantly(); 449 } 450 451 /** 452 * Returns whether the taskbar should be stashed in apps (e.g. user long pressed to stash). 453 */ isStashedInApp()454 public boolean isStashedInApp() { 455 return hasAnyFlag(FLAGS_STASHED_IN_APP); 456 } 457 458 /** 459 * Returns whether the taskbar should be stashed in the current LauncherState. 460 */ isInStashedLauncherState()461 public boolean isInStashedLauncherState() { 462 return (hasAnyFlag(FLAG_IN_STASHED_LAUNCHER_STATE) && supportsVisualStashing()); 463 } 464 hasAnyFlag(long flagMask)465 private boolean hasAnyFlag(long flagMask) { 466 return hasAnyFlag(mState, flagMask); 467 } 468 hasAnyFlag(long flags, long flagMask)469 private boolean hasAnyFlag(long flags, long flagMask) { 470 return (flags & flagMask) != 0; 471 } 472 473 474 /** 475 * Returns whether the taskbar is currently visible and not in the process of being stashed. 476 */ isTaskbarVisibleAndNotStashing()477 public boolean isTaskbarVisibleAndNotStashing() { 478 return !mIsStashed && mControllers.taskbarViewController.areIconsVisible(); 479 } 480 isInApp()481 public boolean isInApp() { 482 return hasAnyFlag(FLAGS_IN_APP); 483 } 484 485 /** Returns whether the taskbar is currently in overview screen. */ isInOverview()486 public boolean isInOverview() { 487 return hasAnyFlag(FLAG_IN_OVERVIEW); 488 } 489 490 /** Returns whether the taskbar is currently on launcher home screen. */ isOnHome()491 public boolean isOnHome() { 492 return !isInOverview() && !isInApp(); 493 } 494 495 /** Returns whether taskbar is hidden for bubbles. */ isHiddenForBubbles()496 public boolean isHiddenForBubbles() { 497 return hasAnyFlag(FLAG_STASHED_FOR_BUBBLES); 498 } 499 500 /** 501 * Returns the height that taskbar will be touchable. 502 */ getTouchableHeight()503 public int getTouchableHeight() { 504 return mIsStashed 505 ? mStashedHeight 506 : (mUnstashedHeight + mActivity.getDeviceProfile().taskbarBottomMargin); 507 } 508 509 /** 510 * Returns the height that taskbar will inset when inside apps. 511 * 512 * @see android.view.WindowInsets.Type#navigationBars() 513 * @see android.view.WindowInsets.Type#systemBars() 514 */ getContentHeightToReportToApps()515 public int getContentHeightToReportToApps() { 516 boolean isTransient = mActivity.isTransientTaskbar(); 517 if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode() || isTransient)) { 518 return getStashedHeight(); 519 } 520 521 if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) { 522 DeviceProfile dp = mActivity.getDeviceProfile(); 523 if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && (dp.isTaskbarPresent 524 || mActivity.isPhoneGestureNavMode())) { 525 // We always show the back button in SUW but in portrait the SUW layout may not 526 // be wide enough to support overlapping the nav bar with its content. 527 // We're sending different res values in portrait vs landscape 528 return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets); 529 } 530 boolean isAnimating = mAnimator != null && mAnimator.isStarted(); 531 if (!mControllers.stashedHandleViewController.isStashedHandleVisible() 532 && isInApp() 533 && !isAnimating) { 534 // We are in a settled state where we're not showing the handle even though taskbar 535 // is stashed. This can happen for example when home button is disabled (see 536 // StashedHandleViewController#setIsHomeButtonDisabled()). 537 return 0; 538 } 539 return mStashedHeight; 540 } 541 542 return mUnstashedHeight; 543 } 544 545 /** 546 * Returns the height that taskbar will inset when inside apps. 547 * 548 * @see android.view.WindowInsets.Type#tappableElement() 549 */ getTappableHeightToReportToApps()550 public int getTappableHeightToReportToApps() { 551 int contentHeight = getContentHeightToReportToApps(); 552 return contentHeight <= mStashedHeight ? 0 : contentHeight; 553 } 554 getStashedHeight()555 public int getStashedHeight() { 556 return mStashedHeight; 557 } 558 559 /** 560 * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION. 561 * If bubble bar exists, it will match taskbars stashing behavior. 562 * Will not delay taskbar background by default. 563 */ updateAndAnimateTransientTaskbar(boolean stash)564 public void updateAndAnimateTransientTaskbar(boolean stash) { 565 updateAndAnimateTransientTaskbar(stash, SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE, false); 566 } 567 568 /** 569 * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION. 570 */ updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow)571 public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow) { 572 updateAndAnimateTransientTaskbar(stash, shouldBubblesFollow, false); 573 } 574 575 /** 576 * Stash or unstashes the transient taskbar. 577 * 578 * @param stash whether transient taskbar should be stashed. 579 * @param shouldBubblesFollow whether bubbles should match taskbars behavior. 580 * @param delayTaskbarBackground whether we will delay the taskbar background animation 581 */ updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow, boolean delayTaskbarBackground)582 public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow, 583 boolean delayTaskbarBackground) { 584 if (!mActivity.isTransientTaskbar() || mActivity.isBubbleBarOnPhone()) { 585 return; 586 } 587 588 if (stash 589 && !mControllers.taskbarAutohideSuspendController 590 .isSuspendedForTransientTaskbarInLauncher() 591 && mControllers.taskbarAutohideSuspendController 592 .isTransientTaskbarStashingSuspended()) { 593 // Avoid stashing if autohide is currently suspended. 594 return; 595 } 596 597 boolean shouldApplyState = false; 598 599 if (delayTaskbarBackground) { 600 mControllers.taskbarStashController.updateStateForFlag(FLAG_DELAY_TASKBAR_BG_TAG, true); 601 shouldApplyState = true; 602 } 603 604 if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) { 605 mTaskbarSharedState.taskbarWasStashedAuto = stash; 606 updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash); 607 shouldApplyState = true; 608 } 609 610 if (shouldApplyState) { 611 applyState(); 612 } 613 614 // Effectively a no-opp to remove the tag. 615 if (delayTaskbarBackground) { 616 mControllers.taskbarStashController.updateStateForFlag(FLAG_DELAY_TASKBAR_BG_TAG, 617 false); 618 mControllers.taskbarStashController.applyState(0); 619 } 620 621 mControllers.bubbleControllers.ifPresent(controllers -> { 622 if (shouldBubblesFollow) { 623 final boolean willStash = mIsStashedPredicate.test(mState); 624 if (willStash != controllers.bubbleStashController.isStashed()) { 625 // Typically bubbles gets stashed / unstashed along with Taskbar, however, if 626 // taskbar is becoming stashed because bubbles is being expanded, we don't want 627 // to stash bubbles. 628 if (willStash) { 629 controllers.bubbleStashController.stashBubbleBar(); 630 } else { 631 controllers.bubbleStashController.showBubbleBar(false /* expandBubbles */); 632 } 633 } 634 } 635 }); 636 } 637 638 /** 639 * Stashes transient taskbar after it has timed out. 640 */ updateAndAnimateTransientTaskbarForTimeout()641 private void updateAndAnimateTransientTaskbarForTimeout() { 642 // If bubbles are expanded we shouldn't stash them when taskbar is hidden 643 // for the timeout. 644 boolean bubbleBarExpanded = mControllers.bubbleControllers.isPresent() 645 && mControllers.bubbleControllers.get().bubbleBarViewController.isExpanded(); 646 updateAndAnimateTransientTaskbar(/* stash= */ true, 647 /* shouldBubblesFollow= */ !bubbleBarExpanded); 648 } 649 650 /** Toggles the Taskbar's stash state. */ toggleTaskbarStash()651 public void toggleTaskbarStash() { 652 if (!mActivity.isTransientTaskbar() || !hasAnyFlag(FLAGS_IN_APP)) return; 653 updateAndAnimateTransientTaskbar(!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)); 654 } 655 656 /** 657 * Adds the Taskbar unstash to Hotseat animator to the animator set. 658 * 659 * This should be used to run a Taskbar unstash to Hotseat animation whose progress matches a 660 * swipe progress. 661 * 662 * @param placeholderDuration a placeholder duration to be used to ensure all full-length 663 * sub-animations are properly coordinated. This duration should not 664 * actually be used since this animation tracks a swipe progress. 665 */ addUnstashToHotseatAnimationFromSuw(AnimatorSet animation, int placeholderDuration)666 protected void addUnstashToHotseatAnimationFromSuw(AnimatorSet animation, 667 int placeholderDuration) { 668 // Defer any UI updates now to avoid the UI becoming stale when the animation plays. 669 mControllers.taskbarViewController.setDeferUpdatesForSUW(true); 670 createAnimToIsStashed( 671 /* isStashed= */ mActivity.isPhoneMode(), 672 placeholderDuration, 673 TRANSITION_UNSTASH_SUW_MANUAL, 674 /* skipTaskbarBackgroundDelay */ false, 675 /* jankTag= */ "SUW_MANUAL"); 676 animation.addListener(AnimatorListeners.forEndCallback( 677 () -> mControllers.taskbarViewController.setDeferUpdatesForSUW(false))); 678 animation.play(mAnimator); 679 } 680 681 /** 682 * Create a stash animation and save to {@link #mAnimator}. 683 * 684 * @param isStashed whether it's a stash animation or an unstash animation 685 * @param duration duration of the animation 686 * @param animationType what transition type to play. 687 * @param shouldDelayBackground whether we should delay the taskbar bg animation 688 * @param jankTag tag to be used in jank monitor trace. 689 */ createAnimToIsStashed(boolean isStashed, long duration, @StashAnimation int animationType, boolean shouldDelayBackground, String jankTag)690 private void createAnimToIsStashed(boolean isStashed, long duration, 691 @StashAnimation int animationType, boolean shouldDelayBackground, String jankTag) { 692 if (animationType == TRANSITION_UNSTASH_SUW_MANUAL && isStashed) { 693 // The STASH_ANIMATION_SUW_MANUAL must only be used during an unstash animation. 694 Log.e(TAG, "Illegal arguments:Using TRANSITION_UNSTASH_SUW_MANUAL to stash taskbar"); 695 } 696 697 if (mAnimator != null) { 698 mAnimator.cancel(); 699 } 700 mAnimator = new AnimatorSet(); 701 addJankMonitorListener( 702 mAnimator, /* expanding= */ !isStashed, /* tag= */ jankTag); 703 final float stashTranslation = mActivity.isPhoneMode() || mActivity.isTransientTaskbar() 704 ? 0 705 : (mUnstashedHeight - mStashedHeight); 706 707 if (!supportsVisualStashing()) { 708 // Just hide/show the icons and background instead of stashing into a handle. 709 mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1) 710 .setDuration(duration)); 711 mAnimator.playTogether(mTaskbarBackgroundOffset.animateToValue(isStashed ? 1 : 0) 712 .setDuration(duration)); 713 mAnimator.playTogether(mIconTranslationYForStash.animateToValue(isStashed 714 ? stashTranslation : 0) 715 .setDuration(duration)); 716 mAnimator.play(mTaskbarImeBgAlpha.animateToValue( 717 (hasAnyFlag(FLAG_STASHED_IME) && isStashed) ? 0 : 1).setDuration( 718 duration)); 719 mAnimator.addListener(AnimatorListeners.forEndCallback(() -> { 720 mAnimator = null; 721 mIsStashed = isStashed; 722 onIsStashedChanged(mIsStashed); 723 })); 724 return; 725 } 726 727 if (mActivity.isTransientTaskbar()) { 728 createTransientAnimToIsStashed(mAnimator, isStashed, duration, 729 shouldDelayBackground, animationType); 730 } else { 731 createAnimToIsStashed(mAnimator, isStashed, duration, stashTranslation, animationType); 732 } 733 734 mAnimator.addListener(new AnimatorListenerAdapter() { 735 @Override 736 public void onAnimationStart(Animator animation) { 737 mIsStashed = isStashed; 738 onIsStashedChanged(mIsStashed); 739 740 cancelTimeoutIfExists(); 741 } 742 743 @Override 744 public void onAnimationEnd(Animator animation) { 745 mAnimator = null; 746 747 if (!mIsStashed) { 748 tryStartTaskbarTimeout(); 749 } 750 751 // only announce if we are actually animating 752 if (duration > 0 && isInApp()) { 753 mControllers.taskbarViewController.announceForAccessibility(); 754 } 755 } 756 }); 757 } 758 createAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration, float stashTranslation, @StashAnimation int animationType)759 private void createAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration, 760 float stashTranslation, @StashAnimation int animationType) { 761 AnimatorSet fullLengthAnimatorSet = new AnimatorSet(); 762 // Not exactly half and may overlap. See [first|second]HalfDurationScale below. 763 AnimatorSet firstHalfAnimatorSet = new AnimatorSet(); 764 AnimatorSet secondHalfAnimatorSet = new AnimatorSet(); 765 766 final float firstHalfDurationScale; 767 final float secondHalfDurationScale; 768 769 if (isStashed) { 770 firstHalfDurationScale = 0.75f; 771 secondHalfDurationScale = 0.5f; 772 773 fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation)); 774 fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(1)); 775 776 firstHalfAnimatorSet.playTogether( 777 mIconAlphaForStash.animateToValue(0), 778 mIconScaleForStash.animateToValue(mActivity.isPhoneMode() ? 779 0 : STASHED_TASKBAR_SCALE) 780 ); 781 secondHalfAnimatorSet.playTogether( 782 mTaskbarStashedHandleAlpha.animateToValue(1) 783 ); 784 785 if (animationType == TRANSITION_HANDLE_FADE) { 786 fullLengthAnimatorSet.setInterpolator(INSTANT); 787 firstHalfAnimatorSet.setInterpolator(INSTANT); 788 } 789 } else { 790 firstHalfDurationScale = 0.5f; 791 secondHalfDurationScale = 0.75f; 792 793 fullLengthAnimatorSet.playTogether( 794 mIconScaleForStash.animateToValue(1), 795 mIconTranslationYForStash.animateToValue(0)); 796 797 final boolean animateBg = animationType != TRANSITION_UNSTASH_SUW_MANUAL; 798 if (animateBg) { 799 fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(0)); 800 } else { 801 fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback( 802 () -> mTaskbarBackgroundOffset.updateValue(0))); 803 } 804 805 firstHalfAnimatorSet.playTogether( 806 mTaskbarStashedHandleAlpha.animateToValue(0) 807 ); 808 secondHalfAnimatorSet.playTogether( 809 mIconAlphaForStash.animateToValue(1) 810 ); 811 812 if (animationType == TRANSITION_HANDLE_FADE) { 813 fullLengthAnimatorSet.setInterpolator(FINAL_FRAME); 814 secondHalfAnimatorSet.setInterpolator(FINAL_FRAME); 815 } 816 } 817 818 fullLengthAnimatorSet.play(mControllers.stashedHandleViewController 819 .createRevealAnimToIsStashed(isStashed)); 820 // Return the stashed handle to its default scale in case it was changed as part of the 821 // feedforward hint. Note that the reveal animation above also visually scales it. 822 fullLengthAnimatorSet.play(mTaskbarStashedHandleHintScale.animateToValue(1f)); 823 824 fullLengthAnimatorSet.setDuration(duration); 825 firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale)); 826 secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale)); 827 secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale))); 828 829 as.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, 830 secondHalfAnimatorSet); 831 832 } 833 createTransientAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration, boolean shouldDelayBackground, @StashAnimation int animationType)834 private void createTransientAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration, 835 boolean shouldDelayBackground, @StashAnimation int animationType) { 836 // Target values of the properties this is going to set 837 final float backgroundOffsetTarget = isStashed ? 1 : 0; 838 final float iconAlphaTarget = isStashed ? 0 : 1; 839 final float stashedHandleAlphaTarget = isStashed ? 1 : 0; 840 final float backgroundAlphaTarget = isStashed ? 0 : 1; 841 842 // Timing for the alpha values depend on the animation played 843 long iconAlphaStartDelay = 0, iconAlphaDuration = 0, backgroundAndHandleAlphaStartDelay = 0, 844 backgroundAndHandleAlphaDuration = 0; 845 if (duration > 0) { 846 if (animationType == TRANSITION_HANDLE_FADE) { 847 // When fading, the handle fades in/out at the beginning of the transition with 848 // TASKBAR_STASH_ALPHA_DURATION. 849 backgroundAndHandleAlphaDuration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION; 850 // The iconAlphaDuration must be set to duration for the skippable interpolators 851 // below to work. 852 iconAlphaDuration = duration; 853 } else { 854 iconAlphaStartDelay = TASKBAR_STASH_ALPHA_START_DELAY; 855 iconAlphaDuration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION; 856 backgroundAndHandleAlphaDuration = TRANSIENT_TASKBAR_STASH_ALPHA_DURATION; 857 858 if (isStashed) { 859 if (animationType == TRANSITION_HOME_TO_APP) { 860 iconAlphaStartDelay = TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY; 861 } 862 backgroundAndHandleAlphaStartDelay = iconAlphaStartDelay; 863 backgroundAndHandleAlphaDuration = Math.max(0, duration - iconAlphaStartDelay); 864 } 865 866 } 867 } 868 869 play(as, mTaskbarStashedHandleAlpha.animateToValue(stashedHandleAlphaTarget), 870 backgroundAndHandleAlphaStartDelay, 871 backgroundAndHandleAlphaDuration, LINEAR); 872 873 874 if (enableScalingRevealHomeAnimation() 875 && !isStashed 876 && shouldDelayBackground) { 877 play(as, getTaskbarBackgroundAnimatorWhenNotGoingHome(duration), 878 0, 0, LINEAR); 879 as.addListener(AnimatorListeners.forEndCallback( 880 () -> mTaskbarBackgroundAlphaForStash.setValue(backgroundAlphaTarget))); 881 } else { 882 play(as, mTaskbarBackgroundAlphaForStash.animateToValue(backgroundAlphaTarget), 883 backgroundAndHandleAlphaStartDelay, 884 backgroundAndHandleAlphaDuration, LINEAR); 885 } 886 887 // The rest of the animations might be "skipped" in TRANSITION_HANDLE_FADE transitions. 888 AnimatorSet skippable = as; 889 if (animationType == TRANSITION_HANDLE_FADE) { 890 skippable = new AnimatorSet(); 891 as.play(skippable); 892 skippable.setInterpolator(isStashed ? INSTANT : FINAL_FRAME); 893 } 894 895 final boolean animateBg = animationType != TRANSITION_UNSTASH_SUW_MANUAL; 896 if (animateBg) { 897 play(skippable, mTaskbarBackgroundOffset.animateToValue(backgroundOffsetTarget), 0, 898 duration, EMPHASIZED); 899 } else { 900 skippable.addListener(AnimatorListeners.forEndCallback( 901 () -> mTaskbarBackgroundOffset.updateValue(backgroundOffsetTarget))); 902 } 903 904 play(skippable, mIconAlphaForStash.animateToValue(iconAlphaTarget), iconAlphaStartDelay, 905 iconAlphaDuration, 906 LINEAR); 907 908 if (isStashed) { 909 play(skippable, mControllers.taskbarSpringOnStashController.createSpringToStash(), 910 0, duration, LINEAR); 911 } else { 912 play(skippable, mControllers.taskbarSpringOnStashController.createResetAnimForUnstash(), 913 0, duration, LINEAR); 914 } 915 916 mControllers.taskbarViewController.addRevealAnimToIsStashed(skippable, isStashed, duration, 917 EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL); 918 919 play(skippable, mControllers.stashedHandleViewController 920 .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED); 921 922 // Return the stashed handle to its default scale in case it was changed as part of the 923 // feedforward hint. Note that the reveal animation above also visually scales it. 924 skippable.play(mTaskbarStashedHandleHintScale.animateToValue(1f) 925 .setDuration(isStashed ? duration / 2 : duration)); 926 } 927 getTaskbarBackgroundAnimatorWhenNotGoingHome(long duration)928 private Animator getTaskbarBackgroundAnimatorWhenNotGoingHome(long duration) { 929 ValueAnimator a = ValueAnimator.ofFloat(0, 1); 930 a.setDuration(duration); 931 a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 932 // This value is arbitrary. 933 private static final float ANIMATED_FRACTION_THRESHOLD = 0.25f; 934 private boolean mTaskbarBgAlphaAnimationStarted = false; 935 @Override 936 public void onAnimationUpdate(ValueAnimator valueAnimator) { 937 if (mTaskbarBgAlphaAnimationStarted) { 938 return; 939 } 940 941 if (valueAnimator.getAnimatedFraction() >= ANIMATED_FRACTION_THRESHOLD) { 942 if (mUserIsNotGoingHome) { 943 playTaskbarBackgroundAlphaAnimation(); 944 mTaskbarBgAlphaAnimationStarted = true; 945 } 946 } 947 } 948 }); 949 return a; 950 } 951 952 /** 953 * Sets whether the user is going home based on the current gesture. 954 */ setUserIsNotGoingHome(boolean userIsNotGoingHome)955 public void setUserIsNotGoingHome(boolean userIsNotGoingHome) { 956 mUserIsNotGoingHome = userIsNotGoingHome; 957 } 958 959 /** 960 * Plays the taskbar background alpha animation if one is not currently playing. 961 */ playTaskbarBackgroundAlphaAnimation()962 public void playTaskbarBackgroundAlphaAnimation() { 963 if (mTaskbarBackgroundAlphaAnimator != null 964 && mTaskbarBackgroundAlphaAnimator.isRunning()) { 965 return; 966 } 967 mTaskbarBackgroundAlphaAnimator = mTaskbarBackgroundAlphaForStash 968 .animateToValue(1f) 969 .setDuration(mTaskbarBackgroundDuration); 970 mTaskbarBackgroundAlphaAnimator.setInterpolator(LINEAR); 971 mTaskbarBackgroundAlphaAnimator.addListener(new AnimatorListenerAdapter() { 972 @Override 973 public void onAnimationEnd(Animator animation) { 974 mTaskbarBackgroundAlphaAnimator = null; 975 } 976 }); 977 mTaskbarBackgroundAlphaAnimator.start(); 978 } 979 play(AnimatorSet as, @Nullable Animator a, long startDelay, long duration, Interpolator interpolator)980 private static void play(AnimatorSet as, @Nullable Animator a, long startDelay, long duration, 981 Interpolator interpolator) { 982 if (a == null) { 983 return; 984 } 985 a.setDuration(duration); 986 a.setStartDelay(startDelay); 987 a.setInterpolator(interpolator); 988 as.play(a); 989 } 990 addJankMonitorListener( AnimatorSet animator, boolean expanding, String tag)991 private void addJankMonitorListener( 992 AnimatorSet animator, boolean expanding, String tag) { 993 View v = mControllers.taskbarActivityContext.getDragLayer(); 994 if (!v.isAttachedToWindow()) { 995 // If the task bar drag layer is not attached to window, we don't need to monitor jank 996 // (actually we can't pass in an unattached view either). 997 return; 998 } 999 int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND : 1000 InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE; 1001 animator.addListener(new AnimationSuccessListener() { 1002 @Override 1003 public void onAnimationStart(@NonNull Animator animation) { 1004 final Configuration.Builder builder = 1005 Configuration.Builder.withView(action, v); 1006 if (tag != null) { 1007 builder.setTag(tag); 1008 } 1009 InteractionJankMonitor.getInstance().begin(builder); 1010 } 1011 1012 @Override 1013 public void onAnimationSuccess(@NonNull Animator animator) { 1014 InteractionJankMonitor.getInstance().end(action); 1015 } 1016 1017 @Override 1018 public void onAnimationCancel(@NonNull Animator animation) { 1019 super.onAnimationCancel(animation); 1020 1021 InteractionJankMonitor.getInstance().cancel(action); 1022 } 1023 }); 1024 } 1025 1026 /** 1027 * Creates and starts a partial unstash animation, hinting at the new state that will trigger 1028 * when long press is detected. 1029 * 1030 * @param animateForward Whether we are going towards the new unstashed state or returning to 1031 * the stashed state. 1032 */ startUnstashHint(boolean animateForward)1033 protected void startUnstashHint(boolean animateForward) { 1034 if (!isStashed()) { 1035 // Already unstashed, no need to hint in that direction. 1036 return; 1037 } 1038 mTaskbarStashedHandleHintScale.animateToValue( 1039 animateForward ? UNSTASHED_TASKBAR_HANDLE_HINT_SCALE : 1) 1040 .setDuration(TASKBAR_HINT_STASH_DURATION).start(); 1041 } 1042 onIsStashedChanged(boolean isStashed)1043 private void onIsStashedChanged(boolean isStashed) { 1044 mControllers.runAfterInit(() -> { 1045 mControllers.stashedHandleViewController.onIsStashedChanged( 1046 isStashed && supportsVisualStashing()); 1047 mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged(); 1048 }); 1049 } 1050 applyState()1051 public void applyState() { 1052 applyState(/* postApplyAction = */ null); 1053 } 1054 1055 /** Applies state and performs action after state is applied. */ applyState(@ullable Runnable postApplyAction)1056 public void applyState(@Nullable Runnable postApplyAction) { 1057 applyState(hasAnyFlag(FLAG_IN_SETUP) ? 0 : TASKBAR_STASH_DURATION, postApplyAction); 1058 } 1059 applyState(long duration)1060 public void applyState(long duration) { 1061 applyState(duration, /* postApplyAction = */ null); 1062 } 1063 applyState(long duration, @Nullable Runnable postApplyAction)1064 private void applyState(long duration, @Nullable Runnable postApplyAction) { 1065 Animator animator = createApplyStateAnimator(duration); 1066 if (animator != null) { 1067 if (postApplyAction != null) { 1068 // performs action on animation end 1069 animator.addListener(AnimatorListeners.forEndCallback(postApplyAction)); 1070 } 1071 animator.start(); 1072 } else if (postApplyAction != null) { 1073 // animator was not created, just execute the action 1074 postApplyAction.run(); 1075 } 1076 } 1077 applyState(long duration, long startDelay)1078 public void applyState(long duration, long startDelay) { 1079 Animator animator = createApplyStateAnimator(duration); 1080 if (animator != null) { 1081 animator.setStartDelay(startDelay); 1082 animator.start(); 1083 } 1084 } 1085 1086 /** 1087 * Returns an animator which applies the latest state if mIsStashed is changed, or {@code null} 1088 * otherwise. 1089 */ 1090 @Nullable createApplyStateAnimator(long duration)1091 public Animator createApplyStateAnimator(long duration) { 1092 if (mActivity.isPhoneMode()) { 1093 return null; 1094 } 1095 return mStatePropertyHolder.createSetStateAnimator(mState, duration); 1096 } 1097 1098 /** 1099 * Should be called when a system gesture starts and settles, so we can remove 1100 * FLAG_STASHED_IN_APP_IME while the gesture is in progress. 1101 */ setSystemGestureInProgress(boolean inProgress)1102 public void setSystemGestureInProgress(boolean inProgress) { 1103 mIsSystemGestureInProgress = inProgress; 1104 setStashedImeState(); 1105 } 1106 setStashedImeState()1107 private void setStashedImeState() { 1108 boolean shouldStashForIme = shouldStashForIme(); 1109 if (hasAnyFlag(FLAG_STASHED_IME) != shouldStashForIme) { 1110 updateStateForFlag(FLAG_STASHED_IME, shouldStashForIme); 1111 applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme()); 1112 } else { 1113 applyState(mControllers.taskbarOverlayController.getCloseDuration()); 1114 } 1115 } 1116 1117 /** 1118 * Should be called when Ime inset is changed to determine if taskbar should be stashed 1119 */ onImeInsetChanged()1120 public void onImeInsetChanged() { 1121 setStashedImeState(); 1122 } 1123 1124 /** 1125 * When hiding the IME, delay the unstash animation to align with the end of the transition. 1126 */ 1127 @VisibleForTesting getTaskbarStashStartDelayForIme()1128 long getTaskbarStashStartDelayForIme() { 1129 if (mIsImeVisible) { 1130 // Only delay when IME is exiting, not entering. 1131 return 0; 1132 } 1133 // This duration is based on input_method_extract_exit.xml. 1134 long imeExitDuration = mControllers.taskbarActivityContext.getResources() 1135 .getInteger(android.R.integer.config_shortAnimTime); 1136 return imeExitDuration - TASKBAR_STASH_DURATION_FOR_IME; 1137 } 1138 1139 /** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */ updateStateForSysuiFlags(long systemUiStateFlags, boolean skipAnim)1140 public void updateStateForSysuiFlags(long systemUiStateFlags, boolean skipAnim) { 1141 long animDuration = TASKBAR_STASH_DURATION; 1142 long startDelay = 0; 1143 1144 updateStateForFlag(FLAG_STASHED_IN_APP_SYSUI, hasAnyFlag(systemUiStateFlags, 1145 SYSUI_STATE_DIALOG_SHOWING | (ENABLE_TASKBAR_BEHIND_SHADE.isTrue() 1146 ? 0 1147 : SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) 1148 )); 1149 1150 boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW) 1151 && hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED) 1152 && mActivity.isTransientTaskbar(); 1153 updateStateForFlag(FLAG_STASHED_SYSUI, 1154 hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles); 1155 updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, 1156 SystemUiFlagUtils.isLocked(systemUiStateFlags)); 1157 1158 mIsImeVisible = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_VISIBLE); 1159 if (updateStateForFlag(FLAG_STASHED_IME, shouldStashForIme())) { 1160 animDuration = TASKBAR_STASH_DURATION_FOR_IME; 1161 startDelay = getTaskbarStashStartDelayForIme(); 1162 } 1163 1164 if (isTaskbarHidden(systemUiStateFlags) && !hasAnyFlag(FLAG_TASKBAR_HIDDEN)) { 1165 updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden(systemUiStateFlags)); 1166 applyState(0, SKIP_TOTAL_DREAM_ANIM_DURATION); 1167 } else { 1168 updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden(systemUiStateFlags)); 1169 applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay); 1170 } 1171 } 1172 1173 /** 1174 * We stash when the IME is visible. 1175 * 1176 * <p>Do not stash if in small screen, with 3 button nav, and in landscape (or seascape). 1177 * <p>Do not stash if taskbar is transient. 1178 * <p>Do not stash if hardware keyboard is attached and taskbar is pinned and IME is docked. 1179 * <p>Do not stash if a system gesture is started. 1180 */ shouldStashForIme()1181 private boolean shouldStashForIme() { 1182 if (mActivity.isTransientTaskbar()) { 1183 return false; 1184 } 1185 // Do not stash if in small screen, with 3 button nav, and in landscape. 1186 if (mActivity.isPhoneMode() && mActivity.isThreeButtonNav() 1187 && mActivity.getDeviceProfile().isLandscape) { 1188 return false; 1189 } 1190 1191 // Do not stash if pinned taskbar, hardware keyboard is attached and no IME is docked 1192 if (mActivity.isHardwareKeyboard() && mActivity.isPinnedTaskbar() 1193 && !mActivity.isImeDocked()) { 1194 return false; 1195 } 1196 1197 // Do not stash if hardware keyboard is attached, in 3 button nav and desktop windowing mode 1198 if (mActivity.isHardwareKeyboard() 1199 && mActivity.isThreeButtonNav() 1200 && mControllers.taskbarDesktopModeController 1201 .isInDesktopModeAndNotInOverview(mActivity.getDisplayId())) { 1202 return false; 1203 } 1204 1205 // Do not stash if a gesture started. 1206 if (mIsSystemGestureInProgress) { 1207 return false; 1208 } 1209 1210 return mIsImeVisible; 1211 } 1212 1213 /** 1214 * Updates the proper flag to indicate whether the task bar should be stashed. 1215 * 1216 * Note that this only updates the flag. {@link #applyState()} needs to be called separately. 1217 * 1218 * @param flag The flag to update. 1219 * @param enabled Whether to enable the flag: True will cause the task bar to be stashed / 1220 * unstashed. 1221 * @return Whether the flag state changed. 1222 */ updateStateForFlag(long flag, boolean enabled)1223 public boolean updateStateForFlag(long flag, boolean enabled) { 1224 long oldState = mState; 1225 if (enabled) { 1226 mState |= flag; 1227 } else { 1228 mState &= ~flag; 1229 } 1230 return mState != oldState; 1231 } 1232 1233 /** 1234 * Called after updateStateForFlag() and applyState() have been called. 1235 * 1236 * @param changedFlags The flags that have changed. 1237 */ onStateChangeApplied(long changedFlags)1238 private void onStateChangeApplied(long changedFlags) { 1239 if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP)) { 1240 mControllers.uiController.onStashedInAppChanged(); 1241 } 1242 if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAGS_IN_APP)) { 1243 notifyStashChange(/* visible */ hasAnyFlag(FLAGS_IN_APP), 1244 /* stashed */ isStashedInApp()); 1245 mControllers.taskbarAutohideSuspendController.updateFlag( 1246 TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, !isInApp()); 1247 } 1248 if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_AUTO)) { 1249 mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) 1250 ? LAUNCHER_TRANSIENT_TASKBAR_HIDE 1251 : LAUNCHER_TRANSIENT_TASKBAR_SHOW); 1252 mControllers.taskbarAutohideSuspendController.updateFlag( 1253 TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, 1254 !hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)); 1255 } 1256 if (hasAnyFlag(changedFlags, FLAG_IN_OVERVIEW | FLAG_IN_APP)) { 1257 mControllers.runAfterInit(() -> mControllers.taskbarInsetsController 1258 .onTaskbarOrBubblebarWindowHeightOrInsetsChanged()); 1259 if (mInAppStateAffectsDesktopTasksVisibilityInTaskbar) { 1260 mControllers.runAfterInit( 1261 () -> mControllers.taskbarViewController.commitRunningAppsToUI()); 1262 } 1263 } 1264 mActivity.applyForciblyShownFlagWhileTransientTaskbarUnstashed(!isStashedInApp()); 1265 } 1266 notifyStashChange(boolean visible, boolean stashed)1267 private void notifyStashChange(boolean visible, boolean stashed) { 1268 mSystemUiProxy.notifyTaskbarStatus(visible, stashed); 1269 setUpTaskbarSystemAction(visible); 1270 mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed); 1271 } 1272 1273 /** 1274 * Setup system action for showing Taskbar depending on its visibility. 1275 */ setUpTaskbarSystemAction(boolean visible)1276 public void setUpTaskbarSystemAction(boolean visible) { 1277 UI_HELPER_EXECUTOR.execute(() -> { 1278 if (!visible || !mActivity.isTransientTaskbar() 1279 || mActivity.isPhoneMode()) { 1280 mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR); 1281 mIsTaskbarSystemActionRegistered = false; 1282 return; 1283 } 1284 1285 if (!mIsTaskbarSystemActionRegistered) { 1286 RemoteAction taskbarRemoteAction = new RemoteAction( 1287 Icon.createWithResource(mActivity, R.drawable.ic_info_no_shadow), 1288 mActivity.getString(R.string.taskbar_a11y_title), 1289 mActivity.getString(R.string.taskbar_a11y_title), 1290 mTaskbarSharedState.taskbarSystemActionPendingIntent); 1291 1292 mAccessibilityManager.registerSystemAction(taskbarRemoteAction, 1293 SYSTEM_ACTION_ID_TASKBAR); 1294 mIsTaskbarSystemActionRegistered = true; 1295 } 1296 }); 1297 } 1298 1299 /** 1300 * Clean up on destroy from TaskbarControllers 1301 */ onDestroy()1302 public void onDestroy() { 1303 // If the controller is destroyed before the animation finishes, we cancel the animation 1304 // so that we don't finish the CUJ. 1305 if (mAnimator != null) { 1306 mAnimator.cancel(); 1307 mAnimator = null; 1308 } 1309 UI_HELPER_EXECUTOR.execute( 1310 () -> mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR)); 1311 } 1312 1313 /** 1314 * Cancels a timeout if any exists. 1315 */ cancelTimeoutIfExists()1316 public void cancelTimeoutIfExists() { 1317 if (mTimeoutAlarm.alarmPending()) { 1318 mTimeoutAlarm.cancelAlarm(); 1319 } 1320 } 1321 1322 /** 1323 * Updates the status of the taskbar timeout. 1324 * 1325 * @param isAutohideSuspended If true, cancels any existing timeout 1326 * If false, attempts to re/start the timeout 1327 */ updateTaskbarTimeout(boolean isAutohideSuspended)1328 public void updateTaskbarTimeout(boolean isAutohideSuspended) { 1329 if (!mActivity.isTransientTaskbar()) { 1330 return; 1331 } 1332 if (isAutohideSuspended) { 1333 cancelTimeoutIfExists(); 1334 } else { 1335 tryStartTaskbarTimeout(); 1336 } 1337 } 1338 1339 /** 1340 * Attempts to start timer to auto hide the taskbar based on time. 1341 */ tryStartTaskbarTimeout()1342 private void tryStartTaskbarTimeout() { 1343 if (!mActivity.isTransientTaskbar() || mIsStashed || mEnableBlockingTimeoutDuringTests) { 1344 return; 1345 } 1346 1347 cancelTimeoutIfExists(); 1348 1349 mTimeoutAlarm.setOnAlarmListener(this::onTaskbarTimeout); 1350 mTimeoutAlarm.setAlarm(getTaskbarAutoHideTimeout()); 1351 } 1352 1353 /** 1354 * returns appropriate timeout for taskbar to stash depending on accessibility being on/off. 1355 */ getTaskbarAutoHideTimeout()1356 private long getTaskbarAutoHideTimeout() { 1357 return mAccessibilityManager.getRecommendedTimeoutMillis((int) NO_TOUCH_TIMEOUT_TO_STASH_MS, 1358 FLAG_CONTENT_CONTROLS); 1359 } 1360 onTaskbarTimeout(Alarm alarm)1361 private void onTaskbarTimeout(Alarm alarm) { 1362 if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) { 1363 return; 1364 } 1365 updateAndAnimateTransientTaskbarForTimeout(); 1366 } 1367 1368 @VisibleForTesting getTimeoutAlarm()1369 Alarm getTimeoutAlarm() { 1370 return mTimeoutAlarm; 1371 } 1372 1373 @Override dumpLogs(String prefix, PrintWriter pw)1374 public void dumpLogs(String prefix, PrintWriter pw) { 1375 pw.println(prefix + "TaskbarStashController:"); 1376 1377 pw.println(prefix + "\tmStashedHeight=" + mStashedHeight); 1378 pw.println(prefix + "\tmUnstashedHeight=" + mUnstashedHeight); 1379 pw.println(prefix + "\tmIsStashed=" + mIsStashed); 1380 pw.println(prefix + "\tappliedState=" + getStateString(mStatePropertyHolder.mPrevFlags)); 1381 pw.println(prefix + "\tmState=" + getStateString(mState)); 1382 pw.println(prefix + "\tmIsSystemGestureInProgress=" + mIsSystemGestureInProgress); 1383 pw.println(prefix + "\tmIsImeVisible=" + mIsImeVisible); 1384 } 1385 getStateString(long flags)1386 private static String getStateString(long flags) { 1387 StringJoiner sj = new StringJoiner("|"); 1388 appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP"); 1389 appendFlag(sj, flags, FLAG_STASHED_IN_APP_SYSUI, "FLAG_STASHED_IN_APP_SYSUI"); 1390 appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP"); 1391 appendFlag(sj, flags, FLAG_STASHED_IME, "FLAG_STASHED_IN_APP_IME"); 1392 appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE"); 1393 appendFlag(sj, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_TASKBAR_ALL_APPS"); 1394 appendFlag(sj, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP"); 1395 appendFlag(sj, flags, FLAG_STASHED_IN_APP_AUTO, "FLAG_STASHED_IN_APP_AUTO"); 1396 appendFlag(sj, flags, FLAG_STASHED_SYSUI, "FLAG_STASHED_SYSUI"); 1397 appendFlag(sj, flags, FLAG_STASHED_DEVICE_LOCKED, "FLAG_STASHED_DEVICE_LOCKED"); 1398 appendFlag(sj, flags, FLAG_IN_OVERVIEW, "FLAG_IN_OVERVIEW"); 1399 return sj.toString(); 1400 } 1401 1402 private class StatePropertyHolder { 1403 private final LongPredicate mStashCondition; 1404 1405 private boolean mIsStashed; 1406 private @StashAnimation int mLastStartedTransitionType = TRANSITION_DEFAULT; 1407 private long mPrevFlags; 1408 1409 private long mLastUnlockTransitionTimeout = 0; 1410 StatePropertyHolder(LongPredicate stashCondition)1411 StatePropertyHolder(LongPredicate stashCondition) { 1412 mStashCondition = stashCondition; 1413 } 1414 1415 /** 1416 * Creates an animator (stored in mAnimator) which applies the latest state, potentially 1417 * creating a new animation (stored in mAnimator). 1418 * 1419 * @param flags The latest flags to apply (see the top of this file). 1420 * @param duration The length of the animation. 1421 * @return mAnimator if mIsStashed changed, or {@code null} otherwise. 1422 */ 1423 @Nullable createSetStateAnimator(long flags, long duration)1424 public Animator createSetStateAnimator(long flags, long duration) { 1425 // We do this when we want to synchronize the app launch and taskbar stash animations. 1426 if (syncAppLaunchWithTaskbarStash() 1427 && hasAnyFlag(FLAG_IGNORE_IN_APP) 1428 && hasAnyFlag(flags, FLAG_IN_APP)) { 1429 flags = flags & ~FLAG_IN_APP; 1430 } 1431 1432 boolean isStashed = mStashCondition.test(flags); 1433 1434 if (DEBUG) { 1435 String stateString = formatFlagChange(flags, mPrevFlags, 1436 TaskbarStashController::getStateString); 1437 Log.d(TAG, "createSetStateAnimator: flags: " + stateString 1438 + ", duration: " + duration 1439 + ", isStashed: " + isStashed 1440 + ", mIsStashed: " + mIsStashed); 1441 } 1442 1443 long changedFlags = mPrevFlags ^ flags; 1444 if (mPrevFlags != flags) { 1445 onStateChangeApplied(changedFlags); 1446 mPrevFlags = flags; 1447 } 1448 1449 boolean isUnlockTransition = hasAnyFlag(changedFlags, FLAG_STASHED_DEVICE_LOCKED) 1450 && !hasAnyFlag(FLAG_STASHED_DEVICE_LOCKED); 1451 if (isUnlockTransition) { 1452 // the launcher might not be resumed at the time the device is considered 1453 // unlocked (when the keyguard goes away), but possibly shortly afterwards. 1454 // To play the unlock transition at the time the unstash animation actually happens, 1455 // this memoizes the state transition for UNLOCK_TRANSITION_MEMOIZATION_MS. 1456 mLastUnlockTransitionTimeout = 1457 SystemClock.elapsedRealtime() + UNLOCK_TRANSITION_MEMOIZATION_MS; 1458 } 1459 1460 @StashAnimation int animationType = computeTransitionType(changedFlags); 1461 1462 // Allow re-starting animation if upgrading from default animation type, otherwise 1463 // stick with the already started transition. 1464 boolean transitionTypeChanged = mAnimator != null && mAnimator.isStarted() 1465 && mLastStartedTransitionType == TRANSITION_DEFAULT 1466 && animationType != TRANSITION_DEFAULT; 1467 1468 // It is possible for stash=false to be requested by TRANSITION_HOME_TO_APP and 1469 // TRANSITION_DEFAULT in quick succession. In this case, we should ignore 1470 // transitionTypeChanged because the animations are exactly the same. 1471 if (transitionTypeChanged 1472 && (!mIsStashed && !isStashed) 1473 && animationType == TRANSITION_HOME_TO_APP) { 1474 transitionTypeChanged = false; 1475 } 1476 1477 if (mIsStashed != isStashed || transitionTypeChanged) { 1478 mIsStashed = isStashed; 1479 mLastStartedTransitionType = animationType; 1480 1481 boolean shouldDelayBackground = hasAnyFlag(FLAG_DELAY_TASKBAR_BG_TAG); 1482 // This sets mAnimator. 1483 createAnimToIsStashed(mIsStashed, duration, animationType, shouldDelayBackground, 1484 computeTaskbarJankMonitorTag(changedFlags)); 1485 return mAnimator; 1486 } 1487 return null; 1488 } 1489 1490 /** Calculates the tag for CUJ_TASKBAR_EXPAND and CUJ_TASKBAR_COLLAPSE jank traces. */ computeTaskbarJankMonitorTag(long changedFlags)1491 private String computeTaskbarJankMonitorTag(long changedFlags) { 1492 if (hasAnyFlag(changedFlags, FLAG_IN_APP)) { 1493 // moving in or out of the app 1494 if (hasAnyFlag(FLAG_IN_APP)) { 1495 return "Home to App"; 1496 } else { 1497 return "App to Home"; 1498 } 1499 } 1500 if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_AUTO)) { 1501 // stash and unstash with-in the app 1502 if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)) { 1503 return "Stashed in app"; 1504 } else { 1505 return "Manually unstashed"; 1506 } 1507 } 1508 return ""; 1509 } 1510 computeTransitionType(long changedFlags)1511 private @StashAnimation int computeTransitionType(long changedFlags) { 1512 1513 boolean hotseatHiddenDuringAppLaunch = 1514 !mControllers.uiController.isHotseatIconOnTopWhenAligned() 1515 && hasAnyFlag(changedFlags, FLAG_IN_APP); 1516 if (hotseatHiddenDuringAppLaunch) { 1517 // When launching an app from the all-apps drawer, the hotseat is hidden behind the 1518 // drawer. In this case, the navbar must just fade in, without a stash transition, 1519 // as the taskbar stash animation would otherwise be visible above the all-apps 1520 // drawer once the hotseat is detached. 1521 return TRANSITION_HANDLE_FADE; 1522 } 1523 1524 boolean isUnlockTransition = 1525 SystemClock.elapsedRealtime() < mLastUnlockTransitionTimeout; 1526 if (isUnlockTransition) { 1527 // When transitioning to unlocked device, the hotseat will already be visible on 1528 // the homescreen, thus do not play an un-stash animation. 1529 // Keep isUnlockTransition in sync with its counterpart in 1530 // TaskbarLauncherStateController#onStateChangeApplied. 1531 return TRANSITION_HANDLE_FADE; 1532 } 1533 1534 boolean homeToApp = hasAnyFlag(changedFlags, FLAG_IN_APP) && hasAnyFlag(FLAG_IN_APP); 1535 if (homeToApp) { 1536 return TRANSITION_HOME_TO_APP; 1537 } 1538 1539 return TRANSITION_DEFAULT; 1540 } 1541 } 1542 } 1543