• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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