• 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.os.Trace.TRACE_TAG_APP;
19 import static android.view.Display.DEFAULT_DISPLAY;
20 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
22 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
23 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
24 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
25 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
26 import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
27 
28 import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
29 
30 import static com.android.app.animation.Interpolators.LINEAR;
31 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
32 import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP;
33 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
34 import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
35 import static com.android.launcher3.Flags.enableCursorHoverStates;
36 import static com.android.launcher3.Flags.removeExcludeFromScreenMagnificationFlagUsage;
37 import static com.android.launcher3.Utilities.calculateTextHeight;
38 import static com.android.launcher3.Utilities.isRunningInTestHarness;
39 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
40 import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
41 import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
42 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
43 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
44 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
45 import static com.android.launcher3.taskbar.TaskbarStashController.SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE;
46 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
47 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
48 import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
49 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
50 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
51 import static com.android.wm.shell.Flags.enableBubbleBar;
52 import static com.android.wm.shell.Flags.enableBubbleBarOnPhones;
53 import static com.android.wm.shell.Flags.enableTinyTaskbar;
54 
55 import static java.lang.invoke.MethodHandles.Lookup.PROTECTED;
56 
57 import android.animation.AnimatorSet;
58 import android.animation.ValueAnimator;
59 import android.app.ActivityOptions;
60 import android.content.ActivityNotFoundException;
61 import android.content.Context;
62 import android.content.Intent;
63 import android.content.pm.ActivityInfo.Config;
64 import android.content.pm.LauncherApps;
65 import android.content.res.Resources;
66 import android.graphics.PixelFormat;
67 import android.graphics.Rect;
68 import android.graphics.drawable.Drawable;
69 import android.hardware.display.DisplayManager;
70 import android.os.IRemoteCallback;
71 import android.os.Process;
72 import android.os.Trace;
73 import android.provider.Settings;
74 import android.util.Log;
75 import android.view.Gravity;
76 import android.view.Surface;
77 import android.view.View;
78 import android.view.WindowInsets;
79 import android.view.WindowManager;
80 import android.widget.FrameLayout;
81 import android.widget.Toast;
82 import android.window.DesktopExperienceFlags;
83 import android.window.DesktopModeFlags;
84 import android.window.DesktopModeFlags.DesktopModeFlag;
85 import android.window.RemoteTransition;
86 
87 import androidx.annotation.NonNull;
88 import androidx.annotation.Nullable;
89 import androidx.annotation.VisibleForTesting;
90 import androidx.core.graphics.Insets;
91 import androidx.core.view.WindowInsetsCompat;
92 
93 import com.android.internal.jank.Cuj;
94 import com.android.launcher3.AbstractFloatingView;
95 import com.android.launcher3.BubbleTextView;
96 import com.android.launcher3.BubbleTextView.RunningAppState;
97 import com.android.launcher3.DeviceProfile;
98 import com.android.launcher3.Flags;
99 import com.android.launcher3.LauncherPrefs;
100 import com.android.launcher3.LauncherSettings.Favorites;
101 import com.android.launcher3.R;
102 import com.android.launcher3.Utilities;
103 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
104 import com.android.launcher3.anim.AnimatorListeners;
105 import com.android.launcher3.anim.AnimatorPlaybackController;
106 import com.android.launcher3.apppairs.AppPairIcon;
107 import com.android.launcher3.config.FeatureFlags;
108 import com.android.launcher3.desktop.DesktopAppLaunchTransition;
109 import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
110 import com.android.launcher3.folder.Folder;
111 import com.android.launcher3.folder.FolderIcon;
112 import com.android.launcher3.icons.BitmapRenderer;
113 import com.android.launcher3.icons.FastBitmapDrawable;
114 import com.android.launcher3.logger.LauncherAtom;
115 import com.android.launcher3.logging.StatsLogManager;
116 import com.android.launcher3.model.data.AppInfo;
117 import com.android.launcher3.model.data.AppPairInfo;
118 import com.android.launcher3.model.data.FolderInfo;
119 import com.android.launcher3.model.data.ItemInfo;
120 import com.android.launcher3.model.data.TaskItemInfo;
121 import com.android.launcher3.model.data.WorkspaceItemInfo;
122 import com.android.launcher3.popup.PopupContainerWithArrow;
123 import com.android.launcher3.popup.PopupDataProvider;
124 import com.android.launcher3.statehandlers.DesktopVisibilityController;
125 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
126 import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
127 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
128 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
129 import com.android.launcher3.taskbar.bubbles.BubbleBarPinController;
130 import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController;
131 import com.android.launcher3.taskbar.bubbles.BubbleBarView;
132 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
133 import com.android.launcher3.taskbar.bubbles.BubbleControllers;
134 import com.android.launcher3.taskbar.bubbles.BubbleCreator;
135 import com.android.launcher3.taskbar.bubbles.BubbleDismissController;
136 import com.android.launcher3.taskbar.bubbles.BubbleDragController;
137 import com.android.launcher3.taskbar.bubbles.BubblePinController;
138 import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController;
139 import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
140 import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider;
141 import com.android.launcher3.taskbar.bubbles.stashing.DeviceProfileDimensionsProviderAdapter;
142 import com.android.launcher3.taskbar.bubbles.stashing.PersistentBubbleStashController;
143 import com.android.launcher3.taskbar.bubbles.stashing.TransientBubbleStashController;
144 import com.android.launcher3.taskbar.customization.TaskbarFeatureEvaluator;
145 import com.android.launcher3.taskbar.customization.TaskbarSpecsEvaluator;
146 import com.android.launcher3.taskbar.growth.NudgeController;
147 import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
148 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
149 import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
150 import com.android.launcher3.testing.TestLogging;
151 import com.android.launcher3.testing.shared.TestProtocol;
152 import com.android.launcher3.touch.ItemClickHandler;
153 import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
154 import com.android.launcher3.util.ActivityOptionsWrapper;
155 import com.android.launcher3.util.ApiWrapper;
156 import com.android.launcher3.util.ApplicationInfoWrapper;
157 import com.android.launcher3.util.ComponentKey;
158 import com.android.launcher3.util.DisplayController;
159 import com.android.launcher3.util.Executors;
160 import com.android.launcher3.util.LauncherBindableItemsContainer;
161 import com.android.launcher3.util.MultiPropertyFactory;
162 import com.android.launcher3.util.NavigationMode;
163 import com.android.launcher3.util.RunnableList;
164 import com.android.launcher3.util.SettingsCache;
165 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
166 import com.android.launcher3.util.TraceHelper;
167 import com.android.launcher3.util.VibratorWrapper;
168 import com.android.launcher3.views.ActivityContext;
169 import com.android.launcher3.views.BaseDragLayer;
170 import com.android.quickstep.NavHandle;
171 import com.android.quickstep.RecentsModel;
172 import com.android.quickstep.SystemUiProxy;
173 import com.android.quickstep.util.DesktopTask;
174 import com.android.quickstep.util.GroupTask;
175 import com.android.quickstep.util.SingleTask;
176 import com.android.quickstep.util.SplitTask;
177 import com.android.quickstep.views.DesktopTaskView;
178 import com.android.quickstep.views.RecentsView;
179 import com.android.quickstep.views.TaskView;
180 import com.android.systemui.animation.ViewRootSync;
181 import com.android.systemui.shared.recents.model.Task;
182 import com.android.systemui.shared.rotation.RotationButtonController;
183 import com.android.systemui.shared.statusbar.phone.BarTransitions;
184 import com.android.systemui.shared.system.ActivityManagerWrapper;
185 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
186 import com.android.systemui.unfold.updates.RotationChangeProvider;
187 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
188 import com.android.wm.shell.shared.desktopmode.DesktopTaskToFrontReason;
189 
190 import java.io.PrintWriter;
191 import java.util.Collections;
192 import java.util.List;
193 import java.util.Optional;
194 import java.util.function.Consumer;
195 
196 /**
197  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
198  * that are used by both Launcher and Taskbar (such as Folder) to reference a generic
199  * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
200  */
201 public class TaskbarActivityContext extends BaseTaskbarContext {
202 
203     private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar";
204 
205     private static final String TAG = "TaskbarActivityContext";
206 
207     private static final String WINDOW_TITLE = "Taskbar";
208 
209     protected static final DesktopModeFlag ENABLE_TASKBAR_BEHIND_SHADE = new DesktopModeFlag(
210             Flags::enableTaskbarBehindShade, false);
211 
212     private final @Nullable Context mNavigationBarPanelContext;
213 
214     private final TaskbarDragLayer mDragLayer;
215     private final TaskbarControllers mControllers;
216 
217     private final WindowManager mWindowManager;
218     private final boolean mIsPrimaryDisplay;
219     private DeviceProfile mDeviceProfile;
220     private WindowManager.LayoutParams mWindowLayoutParams;
221     private WindowManager.LayoutParams mLastUpdatedLayoutParams;
222     private boolean mIsFullscreen;
223     private boolean mIsNotificationShadeExpanded = false;
224     // The size we should return to when we call setTaskbarWindowFullscreen(false)
225     private int mLastRequestedNonFullscreenSize;
226     /**
227      * When this is true, the taskbar window size is not updated. Requests to update the window
228      * size are stored in {@link #mLastRequestedNonFullscreenSize} and will take effect after
229      * bubbles no longer animate and {@link #setTaskbarWindowForAnimatingBubble()} is called.
230      */
231     private boolean mIsTaskbarSizeFrozenForAnimatingBubble;
232 
233     private NavigationMode mNavMode;
234     private boolean mImeDrawsImeNavBar;
235 
236     private final boolean mIsSafeModeEnabled;
237     private final boolean mIsUserSetupComplete;
238     private final boolean mIsNavBarForceVisible;
239     private final boolean mIsNavBarKidsMode;
240 
241     private boolean mIsDestroyed = false;
242     // The flag to know if the window is excluded from magnification region computation.
243     private boolean mIsExcludeFromMagnificationRegion = false;
244     private boolean mAddedWindow = false;
245 
246     // The bounds of the taskbar items relative to TaskbarDragLayer
247     private final Rect mTransientTaskbarBounds = new Rect();
248 
249     private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
250 
251     private DeviceProfile mTransientTaskbarDeviceProfile;
252 
253     private DeviceProfile mPersistentTaskbarDeviceProfile;
254 
255     private final LauncherPrefs mLauncherPrefs;
256     private final SystemUiProxy mSysUiProxy;
257 
258     private TaskbarFeatureEvaluator mTaskbarFeatureEvaluator;
259 
260     private TaskbarSpecsEvaluator mTaskbarSpecsEvaluator;
261 
262     // Snapshot is used to temporarily draw taskbar behind the shade.
263     private @Nullable View mTaskbarSnapshotView;
264     private @Nullable TaskbarOverlayContext mTaskbarSnapshotOverlay;
265 
TaskbarActivityContext(Context windowContext, @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp, TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider, boolean isPrimaryDisplay, SystemUiProxy sysUiProxy)266     public TaskbarActivityContext(Context windowContext,
267             @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
268             TaskbarNavButtonController buttonController,
269             ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
270             boolean isPrimaryDisplay, SystemUiProxy sysUiProxy) {
271         super(windowContext, isPrimaryDisplay);
272         mIsPrimaryDisplay = isPrimaryDisplay;
273         mNavigationBarPanelContext = navigationBarPanelContext;
274         mSysUiProxy = sysUiProxy;
275         applyDeviceProfile(launcherDp);
276         final Resources resources = getResources();
277         mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this);
278         mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator(
279                 this,
280                 mTaskbarFeatureEvaluator,
281                 mDeviceProfile.inv.numRows,
282                 mDeviceProfile.inv.numColumns);
283 
284         mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
285         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
286                 () -> getPackageManager().isSafeMode());
287 
288         // TODO(b/244231596) For shared Taskbar window, update this value in applyDeviceProfile()
289         //  instead so to get correct value when recreating the taskbar
290         SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
291         mIsUserSetupComplete = settingsCache.getValue(
292                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
293         mIsNavBarKidsMode = settingsCache.getValue(
294                 Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
295         mIsNavBarForceVisible = mIsNavBarKidsMode;
296 
297         // Get display and corners first, as views might use them in constructor.
298         Context c = getApplicationContext();
299         mWindowManager = c.getSystemService(WindowManager.class);
300 
301         // Inflate views.
302         boolean isTransientTaskbar = isTransientTaskbar();
303         int taskbarLayout = isTransientTaskbar ? R.layout.transient_taskbar : R.layout.taskbar;
304         mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
305         TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
306         TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
307         NearestTouchFrame navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
308         StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
309         BubbleBarView bubbleBarView = mDragLayer.findViewById(R.id.taskbar_bubbles);
310         FrameLayout bubbleBarContainer = mDragLayer.findViewById(R.id.taskbar_bubbles_container);
311         StashedHandleView bubbleHandleView = mDragLayer.findViewById(R.id.stashed_bubble_handle);
312 
313         mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
314 
315         // If Bubble bar is present, TaskbarControllers depends on it so build it first.
316         Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
317         BubbleBarController.onTaskbarRecreated();
318         final boolean deviceBubbleBarEnabled = enableBubbleBarOnPhones()
319                 || (!mDeviceProfile.isPhone && !mDeviceProfile.isVerticalBarLayout());
320         if (BubbleBarController.isBubbleBarEnabled()
321                 && deviceBubbleBarEnabled
322                 && bubbleBarView != null
323                 && isPrimaryDisplay
324         ) {
325             Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
326             Optional<BubbleBarSwipeController> bubbleBarSwipeController = Optional.empty();
327             if (isTransientTaskbar) {
328                 bubbleHandleController = Optional.of(
329                         new BubbleStashedHandleViewController(this, bubbleHandleView));
330                 bubbleBarSwipeController = Optional.of(new BubbleBarSwipeController(this));
331             }
332             TaskbarHotseatDimensionsProvider dimensionsProvider =
333                     new DeviceProfileDimensionsProviderAdapter(this);
334             BubbleStashController bubbleStashController = isTransientTaskbar
335                     ? new TransientBubbleStashController(dimensionsProvider, this)
336                     : new PersistentBubbleStashController(dimensionsProvider);
337             bubbleStashController.setBubbleBarVerticalCenterForHome(
338                     launcherDp.getBubbleBarVerticalCenterForHome());
339             bubbleControllersOptional = Optional.of(new BubbleControllers(
340                     new BubbleBarController(this, bubbleBarView),
341                     new BubbleBarViewController(this, bubbleBarView, bubbleBarContainer),
342                     bubbleStashController,
343                     bubbleHandleController,
344                     new BubbleDragController(this, mDragLayer),
345                     new BubbleDismissController(this, mDragLayer),
346                     new BubbleBarPinController(this, bubbleBarContainer,
347                             () -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
348                     new BubblePinController(this, bubbleBarContainer,
349                             () -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
350                     bubbleBarSwipeController,
351                     new BubbleCreator(this)
352             ));
353         }
354 
355         // Construct controllers.
356         RotationButtonController rotationButtonController = new RotationButtonController(this,
357                 c.getColor(R.color.floating_rotation_button_light_color),
358                 c.getColor(R.color.floating_rotation_button_dark_color),
359                 R.drawable.ic_sysbar_rotate_button_ccw_start_0,
360                 R.drawable.ic_sysbar_rotate_button_ccw_start_90,
361                 R.drawable.ic_sysbar_rotate_button_cw_start_0,
362                 R.drawable.ic_sysbar_rotate_button_cw_start_90,
363                 () -> getDisplay().getRotation());
364         rotationButtonController.setBgExecutor(Executors.UI_HELPER_EXECUTOR);
365 
366         mControllers = new TaskbarControllers(this,
367                 new TaskbarDragController(this),
368                 buttonController,
369                 new NavbarButtonsViewController(this, mNavigationBarPanelContext, navButtonsView,
370                         getMainThreadHandler()),
371                 rotationButtonController,
372                 new TaskbarDragLayerController(this, mDragLayer),
373                 new TaskbarViewController(this, taskbarView),
374                 new TaskbarScrimViewController(this, taskbarScrimView),
375                 new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
376                         mWindowManager,
377                         new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
378                                 UI_HELPER_EXECUTOR.getHandler(), getMainThreadHandler())),
379                 new TaskbarKeyguardController(this),
380                 new StashedHandleViewController(this, stashedHandleView),
381                 new TaskbarStashController(this),
382                 new TaskbarAutohideSuspendController(this),
383                 new TaskbarPopupController(this),
384                 new TaskbarForceVisibleImmersiveController(this),
385                 new TaskbarOverlayController(this, launcherDp),
386                 new TaskbarAllAppsController(),
387                 new TaskbarInsetsController(this),
388                 new VoiceInteractionWindowController(this),
389                 new TaskbarTranslationController(this),
390                 new TaskbarSpringOnStashController(this),
391                 new TaskbarRecentAppsController(this, RecentsModel.INSTANCE.get(this)),
392                 TaskbarEduTooltipController.newInstance(this),
393                 new KeyboardQuickSwitchController(),
394                 new TaskbarPinningController(this),
395                 bubbleControllersOptional,
396                 new TaskbarDesktopModeController(this,
397                         DesktopVisibilityController.INSTANCE.get(this)),
398                 new NudgeController(this));
399 
400         mLauncherPrefs = LauncherPrefs.get(this);
401         onViewCreated();
402     }
403 
404     /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
updateDeviceProfile(DeviceProfile launcherDp)405     public void updateDeviceProfile(DeviceProfile launcherDp) {
406         applyDeviceProfile(launcherDp);
407         mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
408         mControllers.bubbleControllers.ifPresent(bubbleControllers -> {
409             int bubbleBarVerticalCenter = launcherDp.getBubbleBarVerticalCenterForHome();
410             bubbleControllers.bubbleStashController
411                     .setBubbleBarVerticalCenterForHome(bubbleBarVerticalCenter);
412         });
413         AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
414         // Reapply fullscreen to take potential new screen size into account.
415         setTaskbarWindowFullscreen(mIsFullscreen);
416 
417         dispatchDeviceProfileChanged();
418     }
419 
420     @Override
isTransientTaskbar()421     public boolean isTransientTaskbar() {
422         return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay && !isPhoneMode();
423     }
424 
425     @Override
isPinnedTaskbar()426     public boolean isPinnedTaskbar() {
427         return DisplayController.isPinnedTaskbar(this);
428     }
429 
430     @Override
getNavigationMode()431     public NavigationMode getNavigationMode() {
432         return isPrimaryDisplay() ? DisplayController.getNavigationMode(this)
433                 : NavigationMode.THREE_BUTTONS;
434     }
435 
436     @Override
isInDesktopMode()437     public boolean isInDesktopMode() {
438         return mControllers != null
439                 && mControllers.taskbarDesktopModeController.isInDesktopMode(getDisplayId());
440     }
441 
442     @Override
showLockedTaskbarOnHome()443     public boolean showLockedTaskbarOnHome() {
444         return DisplayController.showLockedTaskbarOnHome(this);
445     }
446 
447     @Override
showDesktopTaskbarForFreeformDisplay()448     public boolean showDesktopTaskbarForFreeformDisplay() {
449         return DisplayController.showDesktopTaskbarForFreeformDisplay(this);
450     }
451 
452     @Override
isPrimaryDisplay()453     public boolean isPrimaryDisplay() {
454         return mIsPrimaryDisplay;
455     }
456 
457     /**
458      * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
459      * the icon size
460      */
applyDeviceProfile(DeviceProfile originDeviceProfile)461     private void applyDeviceProfile(DeviceProfile originDeviceProfile) {
462         Consumer<DeviceProfile> overrideProvider = deviceProfile -> {
463             // Taskbar should match the number of icons of hotseat
464             deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
465             // Same QSB width to have a smooth animation
466             deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
467 
468             // Update icon size
469             deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
470             deviceProfile.updateIconSize(1f, this);
471         };
472         mDeviceProfile = originDeviceProfile.toBuilder(this)
473                 .withDimensionsOverride(overrideProvider).build();
474 
475         if (isTransientTaskbar()) {
476             mTransientTaskbarDeviceProfile = mDeviceProfile;
477             mPersistentTaskbarDeviceProfile = mDeviceProfile
478                     .toBuilder(this)
479                     .withDimensionsOverride(overrideProvider)
480                     .setIsTransientTaskbar(false)
481                     .build();
482         } else {
483             mPersistentTaskbarDeviceProfile = mDeviceProfile;
484             mTransientTaskbarDeviceProfile = mDeviceProfile
485                     .toBuilder(this)
486                     .withDimensionsOverride(overrideProvider)
487                     .setIsTransientTaskbar(true)
488                     .build();
489         }
490         mNavMode = getNavigationMode();
491     }
492 
493     /** Called when the visibility of the bubble bar changed. */
bubbleBarVisibilityChanged(boolean isVisible)494     public void bubbleBarVisibilityChanged(boolean isVisible) {
495         mControllers.uiController.adjustHotseatForBubbleBar(isVisible);
496         mControllers.taskbarViewController.adjustTaskbarForBubbleBar();
497     }
498 
499     /**
500      * Init of taskbar activity context.
501      * @param duration If duration is greater than 0, it will be used to create an animation
502  *                     for the taskbar create/recreate process.
503      */
init(@onNull TaskbarSharedState sharedState, int duration)504     public void init(@NonNull TaskbarSharedState sharedState, int duration) {
505         mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
506         mLastRequestedNonFullscreenSize = getDefaultTaskbarWindowSize();
507         mWindowLayoutParams = createAllWindowParams();
508         mLastUpdatedLayoutParams = new WindowManager.LayoutParams();
509 
510 
511         AnimatorSet recreateAnim = null;
512         if (duration > 0) {
513             recreateAnim = onRecreateAnimation(duration);
514         }
515 
516         // Initialize controllers after all are constructed.
517         mControllers.init(sharedState, recreateAnim);
518         // This may not be necessary and can be reverted once we move towards recreating all
519         // controllers without re-creating the window
520         mControllers.rotationButtonController.onNavigationModeChanged(mNavMode.resValue);
521         updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
522         disableNavBarElements(sharedState.disableNavBarDisplayId, sharedState.disableNavBarState1,
523                 sharedState.disableNavBarState2, false /* animate */);
524         onSystemBarAttributesChanged(sharedState.systemBarAttrsDisplayId,
525                 sharedState.systemBarAttrsBehavior);
526         onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity);
527         onNavigationBarLumaSamplingEnabled(sharedState.mLumaSamplingDisplayId,
528                 sharedState.mIsLumaSamplingEnabled);
529         setWallpaperVisible(sharedState.wallpaperVisible);
530         onTransitionModeUpdated(sharedState.barMode, true /* checkBarModes */);
531 
532         if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
533             // W/ the flag not set this entire class gets re-created, which resets the value of
534             // mIsDestroyed. We re-use the class for small-screen, so we explicitly have to mark
535             // this class as non-destroyed
536             mIsDestroyed = false;
537         }
538 
539         if (!enableTaskbarNoRecreate() && !mAddedWindow) {
540             mWindowManager.addView(mDragLayer, mWindowLayoutParams);
541             mAddedWindow = true;
542         } else {
543             notifyUpdateLayoutParams();
544         }
545 
546 
547         if (recreateAnim != null) {
548             recreateAnim.start();
549         }
550     }
551 
552     /**
553      * Create AnimatorSet for taskbar create/recreate animation. Further used in init
554      */
onRecreateAnimation(int duration)555     public AnimatorSet onRecreateAnimation(int duration) {
556         AnimatorSet animatorSet = new AnimatorSet();
557         animatorSet.setDuration(duration);
558         return animatorSet;
559     }
560 
561     /**
562      * Called when we want destroy current taskbar with animation as part of recreate process.
563      */
onDestroyAnimation(int duration)564     public AnimatorSet onDestroyAnimation(int duration) {
565         mIsDestroyed = true;
566         AnimatorSet animatorSet = new AnimatorSet();
567         mControllers.taskbarViewController.onDestroyAnimation(animatorSet);
568         mControllers.taskbarDragLayerController.onDestroyAnimation(animatorSet);
569         animatorSet.setInterpolator(LINEAR);
570         animatorSet.setDuration(duration);
571         return animatorSet;
572     }
573 
574     /**
575      * @return {@code true} if the device profile isn't a large screen profile and we are using a
576      * single window for taskbar and navbar.
577      */
isPhoneMode()578     public boolean isPhoneMode() {
579         return ENABLE_TASKBAR_NAVBAR_UNIFICATION
580                 && mDeviceProfile.isPhone
581                 && !mDeviceProfile.isTaskbarPresent;
582     }
583 
584     /**
585      * @return {@code true} if {@link #isPhoneMode()} is true and we're using 3 button-nav
586      */
isPhoneButtonNavMode()587     public boolean isPhoneButtonNavMode() {
588         return isPhoneMode() && isThreeButtonNav();
589     }
590 
591     /**
592      * @return {@code true} if {@link #isPhoneMode()} is true and we're using gesture nav
593      */
isPhoneGestureNavMode()594     public boolean isPhoneGestureNavMode() {
595         return isPhoneMode() && !isThreeButtonNav();
596     }
597 
598     /** Returns {@code true} iff a tiny version of taskbar is shown on phone. */
isTinyTaskbar()599     public boolean isTinyTaskbar() {
600         return enableTinyTaskbar() && mDeviceProfile.isPhone && mDeviceProfile.isTaskbarPresent;
601     }
602 
isBubbleBarOnPhone()603     public boolean isBubbleBarOnPhone() {
604         return enableBubbleBarOnPhones() && enableBubbleBar() && mDeviceProfile.isPhone;
605     }
606 
607     /**
608      * Returns {@code true} iff bubble bar is enabled (but not necessarily visible /
609      * containing bubbles).
610      */
611     @Override
isBubbleBarEnabled()612     public boolean isBubbleBarEnabled() {
613         return getBubbleControllers() != null && BubbleBarController.isBubbleBarEnabled();
614     }
615 
isBubbleBarAnimating()616     private boolean isBubbleBarAnimating() {
617         return mControllers
618                 .bubbleControllers
619                 .map(controllers -> controllers.bubbleBarViewController.isAnimatingNewBubble())
620                 .orElse(false);
621     }
622 
623     /**
624      * Returns if software keyboard is docked or input toolbar is placed at the taskbar area
625      */
isImeDocked()626     public boolean isImeDocked() {
627         View dragLayer = getDragLayer();
628         WindowInsets insets = dragLayer.getRootWindowInsets();
629         if (insets == null) {
630             return false;
631         }
632 
633         WindowInsetsCompat insetsCompat =
634                 WindowInsetsCompat.toWindowInsetsCompat(insets, dragLayer.getRootView());
635 
636         if (insetsCompat.isVisible(WindowInsetsCompat.Type.ime())) {
637             Insets imeInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.ime());
638             return imeInsets.bottom >= getResources().getDimensionPixelSize(
639                     R.dimen.floating_ime_inset_height);
640         } else {
641             return false;
642         }
643     }
644 
645     /**
646      * Show Taskbar upon receiving broadcast
647      */
showTaskbarFromBroadcast()648     public void showTaskbarFromBroadcast() {
649         // If user is in middle of taskbar education handle go to next step of education
650         if (mControllers.taskbarEduTooltipController.isBeforeTooltipFeaturesStep()) {
651             mControllers.taskbarEduTooltipController.hide();
652             mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
653         }
654         mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
655     }
656 
657     @Override
getDeviceProfile()658     public DeviceProfile getDeviceProfile() {
659         return mDeviceProfile;
660     }
661 
662     @Override
dispatchDeviceProfileChanged()663     public void dispatchDeviceProfileChanged() {
664         super.dispatchDeviceProfileChanged();
665         Trace.instantForTrack(TRACE_TAG_APP, "TaskbarActivityContext#DeviceProfileChanged",
666                 getDeviceProfile().toSmallString());
667     }
668 
669     @NonNull
getLauncherPrefs()670     public LauncherPrefs getLauncherPrefs() {
671         return mLauncherPrefs;
672     }
673 
674     /**
675      * Returns the View bounds of transient taskbar.
676      */
getTransientTaskbarBounds()677     public Rect getTransientTaskbarBounds() {
678         return mTransientTaskbarBounds;
679     }
680 
getCurrentTaskbarWidth()681     protected float getCurrentTaskbarWidth() {
682         return mControllers.taskbarViewController.getCurrentVisualTaskbarWidth();
683     }
684 
685     @Override
getStatsLogManager()686     public StatsLogManager getStatsLogManager() {
687         // Used to mock, can't mock a default interface method directly
688         return super.getStatsLogManager();
689     }
690 
691     /**
692      * Creates LayoutParams for adding a view directly to WindowManager as a new window.
693      *
694      * @param type  The window type to pass to the created WindowManager.LayoutParams.
695      * @param title The window title to pass to the created WindowManager.LayoutParams.
696      */
createDefaultWindowLayoutParams(int type, String title)697     public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) {
698         int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
699                 | WindowManager.LayoutParams.FLAG_SLIPPERY;
700         boolean watchOutside = isTransientTaskbar() || isThreeButtonNav();
701         if (watchOutside && !isRunningInTestHarness()) {
702             windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
703                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
704         }
705         WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
706                 MATCH_PARENT,
707                 mLastRequestedNonFullscreenSize,
708                 type,
709                 windowFlags,
710                 PixelFormat.TRANSLUCENT);
711         windowLayoutParams.setTitle(title);
712         windowLayoutParams.packageName = getPackageName();
713         windowLayoutParams.gravity = Gravity.BOTTOM;
714         windowLayoutParams.setFitInsetsTypes(0);
715         windowLayoutParams.receiveInsetsIgnoringZOrder = true;
716         windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
717         windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
718         windowLayoutParams.privateFlags =
719                 WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
720         windowLayoutParams.accessibilityTitle = getString(
721                 isPhoneMode() ? R.string.taskbar_phone_a11y_title : R.string.taskbar_a11y_title);
722 
723         return windowLayoutParams;
724     }
725 
726     /**
727      * Creates {@link WindowManager.LayoutParams} for Taskbar, and also sets LP.paramsForRotation
728      * for taskbar
729      */
createAllWindowParams()730     private WindowManager.LayoutParams createAllWindowParams() {
731         final int windowType =
732                 (ENABLE_TASKBAR_NAVBAR_UNIFICATION && mIsPrimaryDisplay) ? TYPE_NAVIGATION_BAR
733                         : TYPE_NAVIGATION_BAR_PANEL;
734         WindowManager.LayoutParams windowLayoutParams =
735                 createDefaultWindowLayoutParams(windowType, TaskbarActivityContext.WINDOW_TITLE);
736 
737         windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4];
738         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
739             WindowManager.LayoutParams lp =
740                     createDefaultWindowLayoutParams(windowType,
741                             TaskbarActivityContext.WINDOW_TITLE);
742             if (isPhoneButtonNavMode()) {
743                 populatePhoneButtonNavModeWindowLayoutParams(rot, lp);
744             }
745             windowLayoutParams.paramsForRotation[rot] = lp;
746         }
747 
748         // Override with current layout params
749         WindowManager.LayoutParams currentParams =
750                 windowLayoutParams.paramsForRotation[getDisplay().getRotation()];
751         windowLayoutParams.width = currentParams.width;
752         windowLayoutParams.height = currentParams.height;
753         windowLayoutParams.gravity = currentParams.gravity;
754 
755         return windowLayoutParams;
756     }
757 
758     /**
759      * Update {@link WindowManager.LayoutParams} with values specific to phone and 3 button
760      * navigation users
761      */
populatePhoneButtonNavModeWindowLayoutParams(int rot, WindowManager.LayoutParams lp)762     private void populatePhoneButtonNavModeWindowLayoutParams(int rot,
763             WindowManager.LayoutParams lp) {
764         lp.width = WindowManager.LayoutParams.MATCH_PARENT;
765         lp.height = WindowManager.LayoutParams.MATCH_PARENT;
766         lp.gravity = Gravity.BOTTOM;
767 
768         // Override with per-rotation specific values
769         switch (rot) {
770             case Surface.ROTATION_0, Surface.ROTATION_180 -> {
771                 lp.height = mLastRequestedNonFullscreenSize;
772             }
773             case Surface.ROTATION_90 -> {
774                 lp.width = mLastRequestedNonFullscreenSize;
775                 lp.gravity = Gravity.END;
776             }
777             case Surface.ROTATION_270 -> {
778                 lp.width = mLastRequestedNonFullscreenSize;
779                 lp.gravity = Gravity.START;
780             }
781         }
782     }
783 
onConfigurationChanged(@onfig int configChanges)784     public void onConfigurationChanged(@Config int configChanges) {
785         mControllers.onConfigurationChanged(configChanges);
786         if (!mIsUserSetupComplete) {
787             setTaskbarWindowSize(getSetupWindowSize());
788         }
789     }
790 
isThreeButtonNav()791     public boolean isThreeButtonNav() {
792         return mNavMode == NavigationMode.THREE_BUTTONS;
793     }
794 
795     /** Returns whether taskbar should start align. */
shouldStartAlignTaskbar()796     public boolean shouldStartAlignTaskbar() {
797         return isThreeButtonNav() && mDeviceProfile.startAlignTaskbar;
798     }
799 
isGestureNav()800     public boolean isGestureNav() {
801         return mNavMode == NavigationMode.NO_BUTTON;
802     }
803 
imeDrawsImeNavBar()804     public boolean imeDrawsImeNavBar() {
805         return mImeDrawsImeNavBar;
806     }
807 
getCornerRadius()808     public int getCornerRadius() {
809         return isPhoneMode() ? 0 : getResources().getDimensionPixelSize(
810                 R.dimen.persistent_taskbar_corner_radius);
811     }
812 
getWindowLayoutParams()813     public WindowManager.LayoutParams getWindowLayoutParams() {
814         return mWindowLayoutParams;
815     }
816 
817     @Override
getDragLayer()818     public TaskbarDragLayer getDragLayer() {
819         return mDragLayer;
820     }
821 
822     @Override
getFolderBoundingBox()823     public Rect getFolderBoundingBox() {
824         return mControllers.taskbarDragLayerController.getFolderBoundingBox();
825     }
826 
827     @Override
getDragController()828     public TaskbarDragController getDragController() {
829         return mControllers.taskbarDragController;
830     }
831 
832     @Nullable
getBubbleControllers()833     public BubbleControllers getBubbleControllers() {
834         return mControllers.bubbleControllers.orElse(null);
835     }
836 
837     @NonNull
getNavHandle()838     public NavHandle getNavHandle() {
839         return mControllers.stashedHandleViewController;
840     }
841 
842     @Override
getItemOnClickListener()843     public View.OnClickListener getItemOnClickListener() {
844         return this::onTaskbarIconClicked;
845     }
846 
847     /**
848      * Change from hotseat/predicted hotseat to taskbar container.
849      */
850     @Override
applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder)851     public void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) {
852         if (!itemInfoBuilder.hasContainerInfo()) {
853             return;
854         }
855         LauncherAtom.ContainerInfo oldContainer = itemInfoBuilder.getContainerInfo();
856 
857         LauncherAtom.TaskBarContainer.Builder taskbarBuilder =
858                 LauncherAtom.TaskBarContainer.newBuilder();
859         if (mControllers.uiController.isInOverviewUi()) {
860             taskbarBuilder.setTaskSwitcherContainer(
861                     LauncherAtom.TaskSwitcherContainer.newBuilder());
862         }
863 
864         if (oldContainer.hasPredictedHotseatContainer()) {
865             LauncherAtom.PredictedHotseatContainer predictedHotseat =
866                     oldContainer.getPredictedHotseatContainer();
867 
868             if (predictedHotseat.hasIndex()) {
869                 taskbarBuilder.setIndex(predictedHotseat.getIndex());
870             }
871             if (predictedHotseat.hasCardinality()) {
872                 taskbarBuilder.setCardinality(predictedHotseat.getCardinality());
873             }
874 
875             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
876                     .setTaskBarContainer(taskbarBuilder));
877         } else if (oldContainer.hasHotseat()) {
878             LauncherAtom.HotseatContainer hotseat = oldContainer.getHotseat();
879 
880             if (hotseat.hasIndex()) {
881                 taskbarBuilder.setIndex(hotseat.getIndex());
882             }
883 
884             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
885                     .setTaskBarContainer(taskbarBuilder));
886         } else if (oldContainer.hasFolder() && oldContainer.getFolder().hasHotseat()) {
887             LauncherAtom.FolderContainer.Builder folderBuilder = oldContainer.getFolder()
888                     .toBuilder();
889             LauncherAtom.HotseatContainer hotseat = folderBuilder.getHotseat();
890 
891             if (hotseat.hasIndex()) {
892                 taskbarBuilder.setIndex(hotseat.getIndex());
893             }
894 
895             folderBuilder.setTaskbar(taskbarBuilder);
896             folderBuilder.clearHotseat();
897             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
898                     .setFolder(folderBuilder));
899         } else if (oldContainer.hasAllAppsContainer()) {
900             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
901                     .setAllAppsContainer(oldContainer.getAllAppsContainer().toBuilder()
902                             .setTaskbarContainer(taskbarBuilder)));
903         } else if (oldContainer.hasPredictionContainer()) {
904             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
905                     .setPredictionContainer(oldContainer.getPredictionContainer().toBuilder()
906                             .setTaskbarContainer(taskbarBuilder)));
907         }
908     }
909 
910     @NonNull
911     @Override
getPopupDataProvider()912     public PopupDataProvider getPopupDataProvider() {
913         return mControllers.taskbarPopupController.getPopupDataProvider();
914     }
915 
916     @NonNull
917     @Override
getContent()918     public LauncherBindableItemsContainer getContent() {
919         return mControllers.taskbarViewController.getContent();
920     }
921 
922     @Override
getAppsView()923     public ActivityAllAppsContainerView<?> getAppsView() {
924         return mControllers.taskbarAllAppsController.getAppsView();
925     }
926 
927     @Override
getAccessibilityDelegate()928     public View.AccessibilityDelegate getAccessibilityDelegate() {
929         return mAccessibilityDelegate;
930     }
931 
932     @Override
onDragStart()933     public void onDragStart() {
934         setTaskbarWindowFullscreen(true);
935     }
936 
937     @Override
onDragEnd()938     public void onDragEnd() {
939         onDragEndOrViewRemoved();
940     }
941 
942     @Override
onPopupVisibilityChanged(boolean isVisible)943     public void onPopupVisibilityChanged(boolean isVisible) {
944         setTaskbarWindowFocusable(isVisible /* focusable */, false /* imeFocusable */);
945     }
946 
947     @Override
onSplitScreenMenuButtonClicked()948     public void onSplitScreenMenuButtonClicked() {
949         PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(this);
950         if (popup != null) {
951             popup.addOnCloseCallback(() -> {
952                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
953             });
954         }
955     }
956 
957     @Override
makeDefaultActivityOptions(int splashScreenStyle)958     public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
959         RunnableList callbacks = new RunnableList();
960         ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0);
961         options.setSplashScreenStyle(splashScreenStyle);
962         options.setPendingIntentBackgroundActivityStartMode(
963                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
964         IRemoteCallback endCallback = completeRunnableListCallback(callbacks, this);
965         options.setOnAnimationAbortListener(endCallback);
966         options.setOnAnimationFinishedListener(endCallback);
967 
968         return new ActivityOptionsWrapper(options, callbacks);
969     }
970 
971     @Override
getActivityLaunchOptions(View v, @Nullable ItemInfo item)972     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
973         return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
974     }
975 
getActivityLaunchDesktopOptions()976     private ActivityOptionsWrapper getActivityLaunchDesktopOptions() {
977         ActivityOptions options = ActivityOptions.makeRemoteTransition(
978                 createDesktopAppLaunchRemoteTransition(
979                         AppLaunchType.LAUNCH, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON));
980         return new ActivityOptionsWrapper(options, new RunnableList());
981     }
982 
983     /**
984      * Sets a new data-source for this taskbar instance
985      */
setUIController(@onNull TaskbarUIController uiController)986     public void setUIController(@NonNull TaskbarUIController uiController) {
987         mControllers.setUiController(uiController);
988         if (BubbleBarController.isBubbleBarEnabled() && mControllers.bubbleControllers.isEmpty()) {
989             // if the bubble bar was visible in a previous configuration of taskbar and is being
990             // recreated now without bubbles, clean up any bubble bar adjustments from hotseat
991             bubbleBarVisibilityChanged(/* isVisible= */ false);
992         }
993     }
994 
995     /**
996      * Sets the flag indicating setup UI is visible
997      */
setSetupUIVisible(boolean isVisible)998     public void setSetupUIVisible(boolean isVisible) {
999         mControllers.taskbarStashController.setSetupUIVisible(isVisible);
1000     }
1001 
setWallpaperVisible(boolean isVisible)1002     public void setWallpaperVisible(boolean isVisible) {
1003         mControllers.navbarButtonsViewController.setWallpaperVisible(isVisible);
1004     }
1005 
checkNavBarModes()1006     public void checkNavBarModes() {
1007         mControllers.navbarButtonsViewController.checkNavBarModes();
1008     }
1009 
finishBarAnimations()1010     public void finishBarAnimations() {
1011         mControllers.navbarButtonsViewController.finishBarAnimations();
1012     }
1013 
touchAutoDim(boolean reset)1014     public void touchAutoDim(boolean reset) {
1015         mControllers.navbarButtonsViewController.touchAutoDim(reset);
1016     }
1017 
transitionTo(@arTransitions.TransitionMode int barMode, boolean animate)1018     public void transitionTo(@BarTransitions.TransitionMode int barMode,
1019             boolean animate) {
1020         mControllers.navbarButtonsViewController.transitionTo(barMode, animate);
1021     }
1022 
appTransitionPending(boolean pending)1023     public void appTransitionPending(boolean pending) {
1024         mControllers.stashedHandleViewController.setIsAppTransitionPending(pending);
1025     }
1026 
1027     /**
1028      * Called when this instance of taskbar is no longer needed
1029      */
onDestroy()1030     public void onDestroy() {
1031         onViewDestroyed();
1032         mIsDestroyed = true;
1033         mTaskbarFeatureEvaluator.onDestroy();
1034         setUIController(TaskbarUIController.DEFAULT);
1035         mControllers.onDestroy();
1036         if (!enableTaskbarNoRecreate() && !ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
1037             mWindowManager.removeViewImmediate(mDragLayer);
1038             mAddedWindow = false;
1039         }
1040         mTaskbarSnapshotView = null;
1041         mTaskbarSnapshotOverlay = null;
1042     }
1043 
isDestroyed()1044     public boolean isDestroyed() {
1045         return mIsDestroyed;
1046     }
1047 
updateSysuiStateFlags(@ystemUiStateFlags long systemUiStateFlags, boolean fromInit)1048     public void updateSysuiStateFlags(@SystemUiStateFlags long systemUiStateFlags,
1049             boolean fromInit) {
1050         mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
1051                 fromInit);
1052         boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
1053         onNotificationShadeExpandChanged(isShadeVisible, fromInit || isPhoneMode());
1054         mControllers.taskbarViewController.setRecentsButtonDisabled(
1055                 mControllers.navbarButtonsViewController.isRecentsDisabled()
1056                         || isNavBarKidsModeActive());
1057         mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
1058                 mControllers.navbarButtonsViewController.isHomeDisabled());
1059         mControllers.stashedHandleViewController.updateStateForSysuiFlags(systemUiStateFlags);
1060         mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
1061         mControllers.taskbarStashController.updateStateForSysuiFlags(
1062                 systemUiStateFlags, fromInit || !isUserSetupComplete());
1063         mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
1064                 fromInit);
1065         mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
1066         mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
1067         mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
1068                 (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
1069         mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags);
1070         mControllers.bubbleControllers.ifPresent(controllers -> {
1071             controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags);
1072             controllers.bubbleStashedHandleViewController.ifPresent(controller ->
1073                     controller.setIsHomeButtonDisabled(
1074                             mControllers.navbarButtonsViewController.isHomeDisabled()));
1075         });
1076     }
1077 
1078     /**
1079      * Hides the taskbar icons and background when the notification shade is expanded.
1080      */
onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim)1081     private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
1082         boolean isExpandedUpdated = isExpanded != mIsNotificationShadeExpanded;
1083         mIsNotificationShadeExpanded = isExpanded;
1084         // Close all floating views within the Taskbar window to make sure nothing is shown over
1085         // the notification shade.
1086         if (isExpanded) {
1087             AbstractFloatingView.closeAllOpenViewsExcept(this, TYPE_TASKBAR_OVERLAY_PROXY);
1088         }
1089 
1090         float alpha = isExpanded ? 0 : 1;
1091         AnimatorSet anim = new AnimatorSet();
1092         anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().get(
1093                 TaskbarViewController.ALPHA_INDEX_NOTIFICATION_EXPANDED).animateToValue(alpha));
1094         anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar()
1095                 .animateToValue(alpha));
1096 
1097         if (isExpandedUpdated) {
1098             mControllers.bubbleControllers.ifPresent(controllers -> {
1099                 BubbleBarViewController bubbleBarViewController =
1100                         controllers.bubbleBarViewController;
1101                 anim.play(bubbleBarViewController.getBubbleBarAlpha().get(0).animateToValue(alpha));
1102                 MultiPropertyFactory<View>.MultiProperty handleAlpha =
1103                         controllers.bubbleStashController.getHandleViewAlpha();
1104                 if (handleAlpha != null) {
1105                     anim.play(handleAlpha.animateToValue(alpha));
1106                 }
1107             });
1108         }
1109         anim.start();
1110         if (skipAnim) {
1111             anim.end();
1112         }
1113 
1114         updateTaskbarSnapshot(anim, isExpanded);
1115     }
1116 
updateTaskbarSnapshot(AnimatorSet anim, boolean isExpanded)1117     private void updateTaskbarSnapshot(AnimatorSet anim, boolean isExpanded) {
1118         if (!ENABLE_TASKBAR_BEHIND_SHADE.isTrue()) {
1119             return;
1120         }
1121         if (mTaskbarSnapshotView == null) {
1122             mTaskbarSnapshotView = new View(this);
1123         }
1124         if (isExpanded) {
1125             if (!mTaskbarSnapshotView.isAttachedToWindow()
1126                     && mDragLayer.isAttachedToWindow()
1127                     && mDragLayer.isLaidOut()
1128                     && mTaskbarSnapshotView.getParent() == null) {
1129                 NearestTouchFrame navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
1130                 int oldNavButtonsVisibility = navButtonsView.getVisibility();
1131                 navButtonsView.setVisibility(View.INVISIBLE);
1132 
1133                 Drawable drawable = new FastBitmapDrawable(BitmapRenderer.createHardwareBitmap(
1134                         mDragLayer.getWidth(),
1135                         mDragLayer.getHeight(),
1136                         mDragLayer::draw));
1137 
1138                 navButtonsView.setVisibility(oldNavButtonsVisibility);
1139                 mTaskbarSnapshotView.setBackground(drawable);
1140                 mTaskbarSnapshotView.setAlpha(0f);
1141 
1142                 mTaskbarSnapshotView.addOnAttachStateChangeListener(
1143                         new View.OnAttachStateChangeListener() {
1144                             @Override
1145                             public void onViewAttachedToWindow(@NonNull View v) {
1146                                 mTaskbarSnapshotView.removeOnAttachStateChangeListener(this);
1147                                 anim.end();
1148                                 mTaskbarSnapshotView.setAlpha(1f);
1149                                 if (!Utilities.isRunningInTestHarness()) {
1150                                     ViewRootSync.synchronizeNextDraw(mDragLayer,
1151                                             mTaskbarSnapshotView,
1152                                             () -> {});
1153                                 }
1154                             }
1155 
1156                             @Override
1157                             public void onViewDetachedFromWindow(@NonNull View v) {}
1158                         });
1159                 BaseDragLayer.LayoutParams layoutParams = new BaseDragLayer.LayoutParams(
1160                         mDragLayer.getWidth(), mDragLayer.getHeight());
1161                 layoutParams.gravity = mWindowLayoutParams.gravity;
1162                 layoutParams.ignoreInsets = true;
1163                 mTaskbarSnapshotOverlay = mControllers.taskbarOverlayController.requestWindow();
1164                 mTaskbarSnapshotOverlay.getDragLayer().addView(mTaskbarSnapshotView, layoutParams);
1165             }
1166         } else {
1167             Runnable removeSnapshotView = () -> {
1168                 if (mTaskbarSnapshotOverlay != null) {
1169                     mTaskbarSnapshotOverlay.getDragLayer().removeView(mTaskbarSnapshotView);
1170                     mTaskbarSnapshotView = null;
1171                     mTaskbarSnapshotOverlay = null;
1172                 }
1173             };
1174             if (mTaskbarSnapshotView.isAttachedToWindow()) {
1175                 mTaskbarSnapshotView.setAlpha(0f);
1176                 anim.end();
1177                 if (Utilities.isRunningInTestHarness()) {
1178                     removeSnapshotView.run();
1179                 } else {
1180                     ViewRootSync.synchronizeNextDraw(mDragLayer, mTaskbarSnapshotView,
1181                             removeSnapshotView);
1182                 }
1183             } else {
1184                 removeSnapshotView.run();
1185             }
1186         }
1187     }
1188 
onRotationProposal(int rotation, boolean isValid)1189     public void onRotationProposal(int rotation, boolean isValid) {
1190         mControllers.rotationButtonController.onRotationProposal(rotation, isValid);
1191     }
1192 
disableNavBarElements(int displayId, int state1, int state2, boolean animate)1193     public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) {
1194         if (displayId != getDisplayId()) {
1195             return;
1196         }
1197         mControllers.rotationButtonController.onDisable2FlagChanged(state2);
1198     }
1199 
onSystemBarAttributesChanged(int displayId, int behavior)1200     public void onSystemBarAttributesChanged(int displayId, int behavior) {
1201         mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior);
1202     }
1203 
onTransitionModeUpdated(int barMode, boolean checkBarModes)1204     public void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
1205         mControllers.navbarButtonsViewController.onTransitionModeUpdated(barMode, checkBarModes);
1206     }
1207 
onNavButtonsDarkIntensityChanged(float darkIntensity)1208     public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
1209         mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity().updateValue(
1210                 darkIntensity);
1211     }
1212 
onNavigationBarLumaSamplingEnabled(int displayId, boolean enable)1213     public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
1214         mControllers.stashedHandleViewController.onNavigationBarLumaSamplingEnabled(displayId,
1215                 enable);
1216     }
1217 
1218     /**
1219      * Called to update a {@link AutohideSuspendFlag} with a new value.
1220      */
setAutohideSuspendFlag(@utohideSuspendFlag int flag, boolean newValue)1221     public void setAutohideSuspendFlag(@AutohideSuspendFlag int flag, boolean newValue) {
1222         mControllers.taskbarAutohideSuspendController.updateFlag(flag, newValue);
1223     }
1224 
1225     /**
1226      * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
1227      */
setTaskbarWindowFullscreen(boolean fullscreen)1228     public void setTaskbarWindowFullscreen(boolean fullscreen) {
1229         setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen);
1230         mIsFullscreen = fullscreen;
1231         setTaskbarWindowSize(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenSize);
1232     }
1233 
1234     /**
1235      * Updates the taskbar window size according to whether bubbles are animating.
1236      *
1237      * <p>This method should be called when bubbles start animating and again after the animation is
1238      * complete.
1239      */
setTaskbarWindowForAnimatingBubble()1240     public void setTaskbarWindowForAnimatingBubble() {
1241         if (isBubbleBarAnimating()) {
1242             // the default window size accounts for the bubble flyout
1243             setTaskbarWindowSize(getDefaultTaskbarWindowSize());
1244             mIsTaskbarSizeFrozenForAnimatingBubble = true;
1245         } else {
1246             mIsTaskbarSizeFrozenForAnimatingBubble = false;
1247             setTaskbarWindowSize(
1248                     mLastRequestedNonFullscreenSize != 0
1249                             ? mLastRequestedNonFullscreenSize : getDefaultTaskbarWindowSize());
1250         }
1251     }
1252 
1253     /**
1254      * Called when drag ends or when a view is removed from the DragLayer.
1255      */
onDragEndOrViewRemoved()1256     void onDragEndOrViewRemoved() {
1257         boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress();
1258 
1259         // Overlay AFVs are in a separate window and do not require Taskbar to be fullscreen.
1260         if (!isDragInProgress
1261                 && !AbstractFloatingView.hasOpenView(
1262                 this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
1263             // Reverts Taskbar window to its original size
1264             setTaskbarWindowFullscreen(false);
1265         }
1266 
1267         setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, isDragInProgress);
1268     }
1269 
isTaskbarWindowFullscreen()1270     public boolean isTaskbarWindowFullscreen() {
1271         return mIsFullscreen;
1272     }
1273 
1274     /**
1275      * Updates the TaskbarContainer size (pass {@link #getDefaultTaskbarWindowSize()} to reset).
1276      */
setTaskbarWindowSize(int size)1277     public void setTaskbarWindowSize(int size) {
1278         // In landscape phone button nav mode, we should set the task bar width instead of height
1279         // because this is the only case in which the nav bar is not on the display bottom.
1280         boolean landscapePhoneButtonNav = isPhoneButtonNavMode() && mDeviceProfile.isLandscape;
1281         if ((landscapePhoneButtonNav ? mWindowLayoutParams.width : mWindowLayoutParams.height)
1282                 == size || mIsDestroyed) {
1283             return;
1284         }
1285         if (size == MATCH_PARENT) {
1286             size = mDeviceProfile.heightPx;
1287         } else {
1288             mLastRequestedNonFullscreenSize = size;
1289             if (mIsFullscreen || mIsTaskbarSizeFrozenForAnimatingBubble) {
1290                 // We either still need to be fullscreen or a bubble is still animating, so defer
1291                 // any change to our height until setTaskbarWindowFullscreen(false) is called or
1292                 // setTaskbarWindowForAnimatingBubble() is called after the bubble animation
1293                 // completed. For example, this could happen when dragging from the gesture region,
1294                 // as the drag will cancel the gesture and reset launcher's state, which in turn
1295                 // normally would reset the taskbar window height as well.
1296                 return;
1297             }
1298         }
1299         if (landscapePhoneButtonNav) {
1300             mWindowLayoutParams.width = size;
1301             for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
1302                 mWindowLayoutParams.paramsForRotation[rot].width = size;
1303             }
1304         } else {
1305             mWindowLayoutParams.height = size;
1306             for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
1307                 mWindowLayoutParams.paramsForRotation[rot].height = size;
1308             }
1309         }
1310         mControllers.runAfterInit(
1311                 mControllers.taskbarInsetsController
1312                         ::onTaskbarOrBubblebarWindowHeightOrInsetsChanged);
1313         notifyUpdateLayoutParams();
1314     }
1315 
1316     /**
1317      * Returns the default size (in most cases height, but in 3-button phone mode, width) of the
1318      * window, including the static corner radii above taskbar.
1319      */
getDefaultTaskbarWindowSize()1320     public int getDefaultTaskbarWindowSize() {
1321         Resources resources = getResources();
1322 
1323         if (isPhoneMode()) {
1324             return isThreeButtonNav() ?
1325                     resources.getDimensionPixelSize(R.dimen.taskbar_phone_size) :
1326                     resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
1327         }
1328 
1329         if (!isUserSetupComplete()) {
1330             return getSetupWindowSize();
1331         }
1332 
1333         int bubbleBarTop = mControllers.bubbleControllers.map(bubbleControllers ->
1334                 bubbleControllers.bubbleBarViewController.getBubbleBarWithFlyoutMaximumHeight()
1335         ).orElse(0);
1336         int taskbarWindowSize;
1337         boolean shouldTreatAsTransient =
1338                 isTransientTaskbar() || (enableTaskbarPinning() && !isThreeButtonNav());
1339 
1340         int extraHeightForTaskbarTooltips = enableCursorHoverStates()
1341                 ? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
1342                 + (resources.getDimensionPixelSize(R.dimen.taskbar_tooltip_vertical_padding) * 2)
1343                 + calculateTextHeight(
1344                 resources.getDimensionPixelSize(R.dimen.arrow_toast_text_size))
1345                 : 0;
1346 
1347         // Return transient taskbar window height when pinning feature is enabled, so taskbar view
1348         // does not get cut off during pinning animation.
1349         if (shouldTreatAsTransient) {
1350             DeviceProfile transientTaskbarDp = mDeviceProfile.toBuilder(this)
1351                     .setIsTransientTaskbar(true).build();
1352 
1353             taskbarWindowSize = transientTaskbarDp.taskbarHeight
1354                     + (2 * transientTaskbarDp.taskbarBottomMargin)
1355                     + Math.max(extraHeightForTaskbarTooltips, resources.getDimensionPixelSize(
1356                     R.dimen.transient_taskbar_shadow_blur));
1357             return Math.max(taskbarWindowSize, bubbleBarTop);
1358         }
1359 
1360 
1361         taskbarWindowSize =  mDeviceProfile.taskbarHeight
1362                 + getCornerRadius()
1363                 + extraHeightForTaskbarTooltips;
1364         return Math.max(taskbarWindowSize, bubbleBarTop);
1365     }
1366 
getSetupWindowSize()1367     public int getSetupWindowSize() {
1368         return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
1369     }
1370 
getTransientTaskbarDeviceProfile()1371     public DeviceProfile getTransientTaskbarDeviceProfile() {
1372         return mTransientTaskbarDeviceProfile;
1373     }
1374 
getPersistentTaskbarDeviceProfile()1375     public DeviceProfile getPersistentTaskbarDeviceProfile() {
1376         return mPersistentTaskbarDeviceProfile;
1377     }
1378 
1379     /**
1380      * Sets whether the taskbar window should be focusable and IME focusable. This won't be IME
1381      * focusable unless it is also focusable.
1382      *
1383      * @param focusable    whether it should be focusable.
1384      * @param imeFocusable whether it should be IME focusable.
1385      *
1386      * @see WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE
1387      * @see WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
1388      */
setTaskbarWindowFocusable(boolean focusable, boolean imeFocusable)1389     public void setTaskbarWindowFocusable(boolean focusable, boolean imeFocusable) {
1390         if (isPhoneMode()) {
1391             return;
1392         }
1393         if (focusable) {
1394             mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
1395             if (imeFocusable) {
1396                 mWindowLayoutParams.flags &= ~FLAG_ALT_FOCUSABLE_IM;
1397             } else {
1398                 mWindowLayoutParams.flags |= FLAG_ALT_FOCUSABLE_IM;
1399             }
1400         } else {
1401             mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
1402             mWindowLayoutParams.flags &= ~FLAG_ALT_FOCUSABLE_IM;
1403         }
1404         notifyUpdateLayoutParams();
1405     }
1406 
1407     /**
1408      * Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
1409      */
applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow)1410     public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
1411         if (!isTransientTaskbar() || isPhoneMode()) {
1412             return;
1413         }
1414         if (shouldForceShow) {
1415             mWindowLayoutParams.forciblyShownTypes |= WindowInsets.Type.navigationBars();
1416         } else {
1417             mWindowLayoutParams.forciblyShownTypes &= ~WindowInsets.Type.navigationBars();
1418         }
1419         notifyUpdateLayoutParams();
1420     }
1421 
1422     /**
1423      * Sets whether the taskbar window should be focusable, as well as IME focusable. If we're now
1424      * focusable, also move nav buttons to a separate window above IME.
1425      *
1426      * @param focusable whether it should be focusable.
1427      *
1428      * @see WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE
1429      */
setTaskbarWindowFocusableForIme(boolean focusable)1430     public void setTaskbarWindowFocusableForIme(boolean focusable) {
1431         if (focusable) {
1432             mControllers.navbarButtonsViewController.moveNavButtonsToNewWindow();
1433         } else {
1434             mControllers.navbarButtonsViewController.moveNavButtonsBackToTaskbarWindow();
1435         }
1436         setTaskbarWindowFocusable(focusable, true /* imeFocusable */);
1437     }
1438 
1439     /** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */
addWindowView(View view, WindowManager.LayoutParams windowLayoutParams)1440     public void addWindowView(View view, WindowManager.LayoutParams windowLayoutParams) {
1441         if (!view.isAttachedToWindow()) {
1442             mWindowManager.addView(view, windowLayoutParams);
1443         }
1444     }
1445 
1446     /** Removes the given view from WindowManager. See {@link #addWindowView}. */
removeWindowView(View view)1447     public void removeWindowView(View view) {
1448         if (view.isAttachedToWindow()) {
1449             mWindowManager.removeViewImmediate(view);
1450         }
1451     }
1452 
1453     @Override
startSplitSelection(SplitSelectSource splitSelectSource)1454     public void startSplitSelection(SplitSelectSource splitSelectSource) {
1455         mControllers.uiController.startSplitSelection(splitSelectSource);
1456     }
1457 
onTaskbarIconClicked(View view)1458     protected void onTaskbarIconClicked(View view) {
1459         TaskbarUIController taskbarUIController = mControllers.uiController;
1460         RecentsView recents = taskbarUIController.getRecentsView();
1461         boolean shouldCloseAllOpenViews = true;
1462         Object tag = view.getTag();
1463 
1464         mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
1465 
1466         // TODO: b/316004172, b/343289567: Handle `DesktopTask` and `SplitTask`.
1467         if (tag instanceof SingleTask singleTask) {
1468             RemoteTransition remoteTransition =
1469                     (isInDesktopMode() && canUnminimizeDesktopTask(
1470                             singleTask.getTask().key.id))
1471                             ? createDesktopAppLaunchRemoteTransition(AppLaunchType.UNMINIMIZE,
1472                             Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
1473                             : null;
1474             if (isInDesktopMode() && mControllers.uiController.isInOverviewUi()) {
1475                 RunnableList runnableList = recents.launchRunningDesktopTaskView();
1476                 // Wrapping it in runnable so we post after DW is ready for the app
1477                 // launch.
1478                 if (runnableList != null) {
1479                     runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
1480                             () -> handleGroupTaskLaunch(singleTask, remoteTransition,
1481                                     isInDesktopMode(),
1482                                     DesktopTaskToFrontReason.TASKBAR_TAP)));
1483                 }
1484             } else {
1485                 handleGroupTaskLaunch(singleTask, remoteTransition, isInDesktopMode(),
1486                         DesktopTaskToFrontReason.TASKBAR_TAP);
1487             }
1488             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
1489         } else if (tag instanceof FolderInfo) {
1490             // Tapping an expandable folder icon on Taskbar
1491             shouldCloseAllOpenViews = false;
1492             expandFolder((FolderIcon) view);
1493         } else if (tag instanceof AppPairInfo api) {
1494             // Tapping an app pair icon on Taskbar
1495             if (recents != null && recents.isSplitSelectionActive()) {
1496                 Toast.makeText(this, "Unable to split with an app pair. Select another app.",
1497                         Toast.LENGTH_SHORT).show();
1498             } else {
1499                 // Else launch the selected app pair
1500                 launchFromTaskbar(recents, view, api.getContents());
1501                 mControllers.uiController.onTaskbarIconLaunched(api);
1502                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
1503             }
1504         } else if (tag instanceof TaskItemInfo info) {
1505             RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId())
1506                     ? createDesktopAppLaunchRemoteTransition(
1507                             AppLaunchType.UNMINIMIZE, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
1508                     : null;
1509 
1510 
1511             if (isInDesktopMode() && mControllers.uiController.isInOverviewUi()) {
1512                 RunnableList runnableList = recents.launchRunningDesktopTaskView();
1513                 if (runnableList != null) {
1514                     runnableList.add(() ->
1515                             // wrapped it in runnable here since we need the post for DW to be
1516                             // ready. if we don't other DW will be gone and only the launched
1517                             // task will show.
1518                             UI_HELPER_EXECUTOR.execute(() ->
1519                                     SystemUiProxy.INSTANCE.get(this).showDesktopApp(
1520                                             info.getTaskId(), remoteTransition,
1521                                             DesktopTaskToFrontReason.TASKBAR_TAP)));
1522                 }
1523             } else {
1524                 UI_HELPER_EXECUTOR.execute(() ->
1525                         SystemUiProxy.INSTANCE.get(this).showDesktopApp(
1526                                 info.getTaskId(), remoteTransition,
1527                                 DesktopTaskToFrontReason.TASKBAR_TAP));
1528             }
1529 
1530             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
1531                     /* stash= */ true);
1532         } else if (tag instanceof WorkspaceItemInfo) {
1533             // Tapping a launchable icon on Taskbar
1534             WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
1535             if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) {
1536                 if (recents != null && recents.isSplitSelectionActive()) {
1537                     // If we are selecting a second app for split, launch the split tasks
1538                     taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
1539                 } else {
1540                     // Else launch the selected task
1541                     Intent intent = new Intent(info.getIntent())
1542                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1543                     try {
1544                         if (mIsSafeModeEnabled
1545                                 && !new ApplicationInfoWrapper(this, intent).isSystem()) {
1546                             Toast.makeText(this, R.string.safemode_shortcut_error,
1547                                     Toast.LENGTH_SHORT).show();
1548                         } else if (info.isPromise()) {
1549                             TestLogging.recordEvent(
1550                                     TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
1551                             intent = ApiWrapper.INSTANCE.get(this).getAppMarketActivityIntent(
1552                                     info.getTargetPackage(), Process.myUserHandle());
1553                             startActivity(intent);
1554 
1555                         } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
1556                             TestLogging.recordEvent(
1557                                     TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
1558                             String id = info.getDeepShortcutId();
1559                             String packageName = intent.getPackage();
1560                             getSystemService(LauncherApps.class)
1561                                     .startShortcut(packageName, id, null, null, info.user);
1562                         } else {
1563                             launchFromTaskbar(recents, view, Collections.singletonList(info));
1564                         }
1565 
1566                     } catch (NullPointerException
1567                              | ActivityNotFoundException
1568                              | SecurityException e) {
1569                         Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
1570                                 .show();
1571                         Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
1572                         return;
1573                     }
1574                 }
1575 
1576                 // If the app was launched from a folder, stash the taskbar after it closes
1577                 Folder f = Folder.getOpen(this);
1578                 if (f != null && f.getInfo().id == info.container) {
1579                     f.addOnFolderStateChangedListener(new Folder.OnFolderStateChangedListener() {
1580                         @Override
1581                         public void onFolderStateChanged(int newState) {
1582                             if (newState == Folder.STATE_CLOSED) {
1583                                 f.removeOnFolderStateChangedListener(this);
1584                                 mControllers.taskbarStashController
1585                                         .updateAndAnimateTransientTaskbar(true);
1586                             }
1587                         }
1588                     });
1589                 }
1590                 mControllers.uiController.onTaskbarIconLaunched(info);
1591                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
1592             }
1593         } else if (tag instanceof AppInfo) {
1594             // Tapping an item in AllApps
1595             AppInfo info = (AppInfo) tag;
1596             if (recents != null && recents.isSplitSelectionActive()) {
1597                 // If we are selecting a second app for split, launch the split tasks
1598                 taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
1599             } else {
1600                 launchFromTaskbar(recents, view, Collections.singletonList(info));
1601             }
1602             mControllers.uiController.onTaskbarIconLaunched(info);
1603             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
1604         } else if (tag instanceof ItemClickProxy) {
1605             ((ItemClickProxy) tag).onItemClicked(view);
1606         } else {
1607             Log.e(TAG, "Unknown type clicked: " + tag);
1608         }
1609 
1610         mControllers.taskbarPopupController.maybeCloseMultiInstanceMenu();
1611         if (shouldCloseAllOpenViews) {
1612             AbstractFloatingView.closeAllOpenViews(this);
1613         }
1614     }
1615 
handleGroupTaskLaunch( GroupTask task, @Nullable RemoteTransition remoteTransition, boolean onDesktop, DesktopTaskToFrontReason toFrontReason)1616     public void handleGroupTaskLaunch(
1617             GroupTask task,
1618             @Nullable RemoteTransition remoteTransition,
1619             boolean onDesktop,
1620             DesktopTaskToFrontReason toFrontReason) {
1621         handleGroupTaskLaunch(task, remoteTransition, onDesktop, toFrontReason,
1622                 /* onStartCallback= */ null, /* onFinishCallback= */ null);
1623     }
1624 
1625     /**
1626      * Launches the given GroupTask with the following behavior:
1627      * - If the GroupTask is a DesktopTask, launch the tasks in that Desktop.
1628      * - If {@code onDesktop}, bring the given GroupTask to the front.
1629      * - If the GroupTask is a single task, launch it via startActivityFromRecents.
1630      * - Otherwise, we assume the GroupTask is a Split pair and launch them together.
1631      * <p>
1632      * Given start and/or finish callbacks, they will be run before an after the app launch
1633      * respectively in cases where we can't use the remote transition, otherwise we will assume that
1634      * these callbacks are included in the remote transition.
1635      */
handleGroupTaskLaunch( GroupTask task, @Nullable RemoteTransition remoteTransition, boolean onDesktop, DesktopTaskToFrontReason toFrontReason, @Nullable Runnable onStartCallback, @Nullable Runnable onFinishCallback)1636     public void handleGroupTaskLaunch(
1637             GroupTask task,
1638             @Nullable RemoteTransition remoteTransition,
1639             boolean onDesktop,
1640             DesktopTaskToFrontReason toFrontReason,
1641             @Nullable Runnable onStartCallback,
1642             @Nullable Runnable onFinishCallback) {
1643         if (task instanceof DesktopTask) {
1644             UI_HELPER_EXECUTOR.execute(() ->
1645                     SystemUiProxy.INSTANCE.get(this).showDesktopApps(getDisplay().getDisplayId(),
1646                             remoteTransition));
1647             return;
1648         }
1649         if (onDesktop && task instanceof SingleTask singleTask) {
1650             boolean useRemoteTransition = canUnminimizeDesktopTask(singleTask.getTask().key.id);
1651             UI_HELPER_EXECUTOR.execute(() -> {
1652                 if (onStartCallback != null) {
1653                     onStartCallback.run();
1654                 }
1655                 SystemUiProxy.INSTANCE.get(this).showDesktopApp(singleTask.getTask().key.id,
1656                         useRemoteTransition ? remoteTransition : null, toFrontReason);
1657                 if (onFinishCallback != null) {
1658                     onFinishCallback.run();
1659                 }
1660             });
1661             return;
1662         }
1663         if (task instanceof SingleTask singleTask) {
1664             UI_HELPER_EXECUTOR.execute(() -> {
1665                 ActivityOptions activityOptions =
1666                         makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED).options;
1667                 activityOptions.setRemoteTransition(remoteTransition);
1668 
1669                 ActivityManagerWrapper.getInstance().startActivityFromRecents(
1670                         singleTask.getTask().key, activityOptions);
1671             });
1672             return;
1673         }
1674         assert task instanceof SplitTask;
1675         mControllers.uiController.launchSplitTasks((SplitTask) task, remoteTransition);
1676     }
1677 
1678     /** Returns whether the given task is minimized and can be unminimized. */
canUnminimizeDesktopTask(int taskId)1679     public boolean canUnminimizeDesktopTask(int taskId) {
1680         BubbleTextView.RunningAppState runningAppState =
1681                 mControllers.taskbarRecentAppsController.getRunningAppState(taskId);
1682         Log.d(TAG, "Task id=" + taskId + ", Running app state=" + runningAppState);
1683         return runningAppState == RunningAppState.MINIMIZED
1684                 && DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX.isTrue();
1685     }
1686 
createDesktopAppLaunchRemoteTransition( AppLaunchType appLaunchType, @Cuj.CujType int cujType)1687     private RemoteTransition createDesktopAppLaunchRemoteTransition(
1688             AppLaunchType appLaunchType, @Cuj.CujType int cujType) {
1689         return new RemoteTransition(
1690                 new DesktopAppLaunchTransition(
1691                         this,
1692                         appLaunchType,
1693                         cujType,
1694                         getMainExecutor()
1695                 ),
1696                 "TaskbarDesktopAppLaunch");
1697     }
1698 
1699     /**
1700      * Runs when the user taps a Taskbar icon in TaskbarActivityContext (Overview or inside an app),
1701      * and calls the appropriate method to animate and launch.
1702      */
launchFromTaskbar(@ullable RecentsView recents, @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos)1703     private void launchFromTaskbar(@Nullable RecentsView recents, @Nullable View launchingIconView,
1704             List<? extends ItemInfo> itemInfos) {
1705         if (isInApp()) {
1706             launchFromInAppTaskbar(recents, launchingIconView, itemInfos);
1707         } else {
1708             launchFromOverviewTaskbar(recents, launchingIconView, itemInfos);
1709         }
1710     }
1711 
1712     /**
1713      * Runs when the user taps a Taskbar icon while inside an app.
1714      */
launchFromInAppTaskbar(@ullable RecentsView recents, @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos)1715     private void launchFromInAppTaskbar(@Nullable RecentsView recents,
1716             @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos) {
1717         boolean launchedFromExternalDisplay =
1718                 DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
1719                         && !mIsPrimaryDisplay;
1720         if (recents == null && !launchedFromExternalDisplay) {
1721             return;
1722         }
1723 
1724         boolean tappedAppPair = itemInfos.size() == 2;
1725 
1726         if (tappedAppPair) {
1727             // If the icon is an app pair, the logic gets a bit complicated because we play
1728             // different animations depending on which app (or app pair) is currently running on
1729             // screen, so delegate logic to appPairsController.
1730             recents.getSplitSelectController().getAppPairsController()
1731                     .handleAppPairLaunchInApp((AppPairIcon) launchingIconView, itemInfos);
1732         } else {
1733             // Tapped a single app, nothing complicated here.
1734             startItemInfoActivity(itemInfos.get(0), null /*foundTask*/);
1735         }
1736     }
1737 
1738     /**
1739      * Run when the user taps a Taskbar icon while in Overview. If the tapped app is currently
1740      * visible to the user in Overview, or is part of a visible split pair, we expand the TaskView
1741      * as if the user tapped on it (preserving the split pair). Otherwise, launch it normally
1742      * (potentially breaking a split pair).
1743      */
launchFromOverviewTaskbar(@ullable RecentsView recents, @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos)1744     private void launchFromOverviewTaskbar(@Nullable RecentsView recents,
1745             @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos) {
1746         if (recents == null) {
1747             return;
1748         }
1749 
1750         boolean isLaunchingAppPair = itemInfos.size() == 2;
1751         // Convert the list of ItemInfo instances to a list of ComponentKeys
1752         List<ComponentKey> componentKeys =
1753                 itemInfos.stream().map(ItemInfo::getComponentKey).toList();
1754         recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
1755                 componentKeys,
1756                 isLaunchingAppPair,
1757                 foundTasks -> {
1758                     @Nullable Task foundTask = foundTasks[0];
1759                     if (foundTask != null) {
1760                         TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
1761                         if (foundTaskView != null
1762                                 && foundTaskView.isVisibleToUser()
1763                                 && !(foundTaskView instanceof DesktopTaskView)) {
1764                             TestLogging.recordEvent(
1765                                     TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
1766                             foundTaskView.launchWithAnimation();
1767                             return;
1768                         }
1769                     }
1770 
1771                     if (isLaunchingAppPair) {
1772                         // Finish recents animation if it's running before launching to ensure
1773                         // we get both leashes for the animation
1774                         mControllers.uiController.setSkipNextRecentsAnimEnd();
1775                         recents.switchToScreenshot(() ->
1776                                 recents.finishRecentsAnimation(true /*toRecents*/,
1777                                         false /*shouldPip*/,
1778                                         () -> recents
1779                                                 .getSplitSelectController()
1780                                                 .getAppPairsController()
1781                                                 .launchAppPair((AppPairIcon) launchingIconView,
1782                                                         -1 /*cuj*/)));
1783                     } else {
1784                         if (isInDesktopMode()
1785                                 && mControllers.uiController.isInOverviewUi()) {
1786                             RunnableList runnableList = recents.launchRunningDesktopTaskView();
1787                             // Wrapping it in runnable so we post after DW is ready for the app
1788                             // launch.
1789                             if (runnableList != null) {
1790                                 runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
1791                                         () -> startItemInfoActivity(itemInfos.get(0), foundTask)));
1792                             }
1793                         } else {
1794                             startItemInfoActivity(itemInfos.get(0), foundTask);
1795                         }
1796                     }
1797                 }
1798         );
1799     }
1800 
1801     /**
1802      * Starts an activity with the information provided by the "info" param. However, if
1803      * taskInRecents is present, it will prioritize re-launching an existing instance via
1804      * {@link ActivityManagerWrapper#startActivityFromRecents(int, ActivityOptions)}
1805      */
startItemInfoActivity(ItemInfo info, @Nullable Task taskInRecents)1806     private void startItemInfoActivity(ItemInfo info, @Nullable Task taskInRecents) {
1807         Intent intent = new Intent(info.getIntent())
1808                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1809         try {
1810             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
1811             if (!info.user.equals(Process.myUserHandle())) {
1812                 // TODO b/376819104: support Desktop launch animations for apps in managed profiles
1813                 getSystemService(LauncherApps.class).startMainActivity(
1814                         intent.getComponent(), info.user, intent.getSourceBounds(), null);
1815                 return;
1816             }
1817             int displayId = getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId();
1818             // TODO(b/216683257): Use startActivityForResult for search results that require it.
1819             if (taskInRecents != null) {
1820                 // Re launch instance from recents
1821                 ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
1822                 opts.options.setLaunchDisplayId(displayId);
1823                 if (ActivityManagerWrapper.getInstance()
1824                         .startActivityFromRecents(taskInRecents.key, opts.options)) {
1825                     mControllers.uiController.getRecentsView()
1826                             .addSideTaskLaunchCallback(opts.onEndCallback);
1827                     return;
1828                 }
1829             }
1830             if (isInDesktopMode()
1831                     && DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue()) {
1832                 launchDesktopApp(intent, info, displayId);
1833             } else {
1834                 startActivity(intent, null);
1835             }
1836         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
1837             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
1838                     .show();
1839             Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
1840         }
1841     }
1842 
launchDesktopApp(Intent intent, ItemInfo info, int displayId)1843     private void launchDesktopApp(Intent intent, ItemInfo info, int displayId) {
1844         TaskbarRecentAppsController.TaskState taskState =
1845                 mControllers.taskbarRecentAppsController.getDesktopItemState(info);
1846         RunningAppState appState = taskState.getRunningAppState();
1847         if (appState == RunningAppState.RUNNING || appState == RunningAppState.MINIMIZED) {
1848             // We only need a custom animation (a RemoteTransition) if the task is minimized - if
1849             // it's already visible it will just be brought forward.
1850             RemoteTransition remoteTransition = (appState == RunningAppState.MINIMIZED)
1851                     ? createDesktopAppLaunchRemoteTransition(
1852                             AppLaunchType.UNMINIMIZE, Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
1853                     : null;
1854             UI_HELPER_EXECUTOR.execute(() ->
1855                     SystemUiProxy.INSTANCE.get(this).showDesktopApp(taskState.getTaskId(),
1856                             remoteTransition, DesktopTaskToFrontReason.TASKBAR_TAP));
1857             return;
1858         }
1859         // There is no task associated with this launch - launch a new task through an intent
1860         ActivityOptionsWrapper opts = getActivityLaunchDesktopOptions();
1861         if (DesktopModeFlags.ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX.isTrue()) {
1862             mSysUiProxy.startLaunchIntentTransition(intent, opts.options.toBundle(), displayId);
1863         } else {
1864             startActivity(intent, opts.options.toBundle());
1865         }
1866     }
1867 
1868     /** Expands a folder icon when it is clicked */
expandFolder(FolderIcon folderIcon)1869     private void expandFolder(FolderIcon folderIcon) {
1870         Folder folder = folderIcon.getFolder();
1871 
1872         folder.setPriorityOnFolderStateChangedListener(
1873                 new Folder.OnFolderStateChangedListener() {
1874                     @Override
1875                     public void onFolderStateChanged(int newState) {
1876                         if (newState == Folder.STATE_OPEN) {
1877                             setTaskbarWindowFocusableForIme(true);
1878                         } else if (newState == Folder.STATE_CLOSED) {
1879                             // Defer by a frame to ensure we're no longer fullscreen and thus
1880                             // won't jump.
1881                             getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false));
1882                             folder.setPriorityOnFolderStateChangedListener(null);
1883                         }
1884                     }
1885                 });
1886 
1887         setTaskbarWindowFullscreen(true);
1888 
1889         getDragLayer().post(() -> {
1890             folder.animateOpen();
1891             getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
1892 
1893             folder.mapOverItems((itemInfo, itemView) -> {
1894                 mControllers.taskbarViewController
1895                         .setClickAndLongClickListenersForIcon(itemView);
1896                 // To play haptic when dragging, like other Taskbar items do.
1897                 itemView.setHapticFeedbackEnabled(true);
1898                 return false;
1899             });
1900 
1901             // Close any open taskbar tooltips.
1902             if (AbstractFloatingView.hasOpenView(this, TYPE_ON_BOARD_POPUP)) {
1903                 AbstractFloatingView.getOpenView(this, TYPE_ON_BOARD_POPUP)
1904                         .close(/* animate= */ false);
1905             }
1906         });
1907     }
1908 
1909     /**
1910      * Returns whether the taskbar is currently visually stashed.
1911      */
isTaskbarStashed()1912     public boolean isTaskbarStashed() {
1913         return mControllers.taskbarStashController.isStashed();
1914     }
1915 
1916     /**
1917      * Called when we want to unstash taskbar when user performs swipes up gesture.
1918      *
1919      * @param delayTaskbarBackground whether we will delay the taskbar background animation
1920      */
onSwipeToUnstashTaskbar(boolean delayTaskbarBackground)1921     public void onSwipeToUnstashTaskbar(boolean delayTaskbarBackground) {
1922         mControllers.uiController.onSwipeToUnstashTaskbar();
1923 
1924         boolean wasStashed = mControllers.taskbarStashController.isStashed();
1925         mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false,
1926                 SHOULD_BUBBLES_FOLLOW_DEFAULT_VALUE, delayTaskbarBackground);
1927         boolean isStashed = mControllers.taskbarStashController.isStashed();
1928         if (isStashed != wasStashed) {
1929             VibratorWrapper.INSTANCE.get(this).vibrateForTaskbarUnstash();
1930         }
1931         mControllers.taskbarEduTooltipController.hide();
1932     }
1933 
1934     /** Returns {@code true} if Taskbar All Apps is open. */
isTaskbarAllAppsOpen()1935     public boolean isTaskbarAllAppsOpen() {
1936         return mControllers.taskbarAllAppsController.isOpen();
1937     }
1938 
1939     /** Toggles the Taskbar's stash state. */
toggleTaskbarStash()1940     public void toggleTaskbarStash() {
1941         mControllers.taskbarStashController.toggleTaskbarStash();
1942     }
1943 
1944     /**
1945      * Plays the taskbar background alpha animation if one is not currently playing.
1946      */
playTaskbarBackgroundAlphaAnimation()1947     public void playTaskbarBackgroundAlphaAnimation() {
1948         mControllers.taskbarStashController.playTaskbarBackgroundAlphaAnimation();
1949     }
1950 
1951     /**
1952      * Called to start the taskbar translation spring to its settled translation (0).
1953      */
startTranslationSpring()1954     public void startTranslationSpring() {
1955         mControllers.taskbarTranslationController.startSpring();
1956     }
1957 
1958     /**
1959      * Returns a callback to help monitor the swipe gesture.
1960      */
getTranslationCallbacks()1961     public TransitionCallback getTranslationCallbacks() {
1962         return mControllers.taskbarTranslationController.getTransitionCallback();
1963     }
1964 
1965     /**
1966      * Called when a transient Autohide flag suspend status changes.
1967      */
onTransientAutohideSuspendFlagChanged(boolean isSuspended)1968     public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) {
1969         mControllers.taskbarStashController.updateTaskbarTimeout(isSuspended);
1970     }
1971 
1972     /**
1973      * Called when we detect a motion down or up/cancel in the nav region while stashed.
1974      *
1975      * @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
1976      */
startTaskbarUnstashHint(boolean animateForward)1977     public void startTaskbarUnstashHint(boolean animateForward) {
1978         mControllers.taskbarStashController.startUnstashHint(animateForward);
1979     }
1980 
1981     /**
1982      * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar
1983      * testing.
1984      */
1985     @VisibleForTesting
enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout)1986     public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) {
1987         mControllers.taskbarStashController.enableBlockingTimeoutDuringTests(enableBlockingTimeout);
1988     }
1989 
1990     /**
1991      * Unstashes the Taskbar if it is stashed.
1992      */
1993     @VisibleForTesting
unstashTaskbarIfStashed()1994     public void unstashTaskbarIfStashed() {
1995         if (isTransientTaskbar()) {
1996             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
1997         }
1998     }
1999 
2000     /** Unstashes the Bubble Bar if it is stashed. */
2001     @VisibleForTesting
unstashBubbleBarIfStashed()2002     public void unstashBubbleBarIfStashed() {
2003         mControllers.bubbleControllers.ifPresent(bubbleControllers -> {
2004             if (bubbleControllers.bubbleStashController.isStashed()) {
2005                 bubbleControllers.bubbleStashController.showBubbleBar(false);
2006             }
2007         });
2008     }
2009 
isUserSetupComplete()2010     public boolean isUserSetupComplete() {
2011         return mIsUserSetupComplete;
2012     }
2013 
isNavBarKidsModeActive()2014     public boolean isNavBarKidsModeActive() {
2015         return mIsNavBarKidsMode && isThreeButtonNav();
2016     }
2017 
2018     @VisibleForTesting(otherwise = PROTECTED)
isNavBarForceVisible()2019     public boolean isNavBarForceVisible() {
2020         return mIsNavBarForceVisible;
2021     }
2022 
2023     /**
2024      * Displays a single frame of the Launcher start from SUW animation.
2025      *
2026      * This animation is a combination of the Launcher resume animation, which animates the hotseat
2027      * icons into position, the Taskbar unstash to hotseat animation, which animates the Taskbar
2028      * stash bar into the hotseat icons, and an override to prevent showing the Taskbar all apps
2029      * button.
2030      *
2031      * This should be used to run a Taskbar unstash to hotseat animation whose progress matches a
2032      * swipe progress.
2033      *
2034      * @param duration a placeholder duration to be used to ensure all full-length
2035      *                 sub-animations are properly coordinated. This duration should not actually
2036      *                 be used since this animation tracks a swipe progress.
2037      */
createLauncherStartFromSuwAnim(int duration)2038     protected AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) {
2039         AnimatorSet fullAnimation = new AnimatorSet();
2040         fullAnimation.setDuration(duration);
2041 
2042         TaskbarUIController uiController = mControllers.uiController;
2043         if (uiController instanceof LauncherTaskbarUIController) {
2044             ((LauncherTaskbarUIController) uiController).addLauncherVisibilityChangedAnimation(
2045                     fullAnimation, duration);
2046         }
2047         mControllers.taskbarStashController.addUnstashToHotseatAnimationFromSuw(fullAnimation,
2048                 duration);
2049 
2050         View allAppsButton = mControllers.taskbarViewController.getAllAppsButtonView();
2051         if (!FeatureFlags.enableAllAppsButtonInHotseat()) {
2052             ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1);
2053             alphaOverride.setDuration(duration);
2054             alphaOverride.addUpdateListener(a -> {
2055                 // Override the alpha updates in the icon alignment animation.
2056                 allAppsButton.setAlpha(0);
2057             });
2058             alphaOverride.addListener(AnimatorListeners.forSuccessCallback(
2059                     () -> allAppsButton.setAlpha(1f)));
2060             fullAnimation.play(alphaOverride);
2061         }
2062 
2063         return AnimatorPlaybackController.wrap(fullAnimation, duration);
2064     }
2065 
2066     /**
2067      * Called when we determine the touchable region.
2068      *
2069      * @param exclude {@code true} then the magnification region computation will omit the window.
2070      */
excludeFromMagnificationRegion(boolean exclude)2071     public void excludeFromMagnificationRegion(boolean exclude) {
2072         if (mIsExcludeFromMagnificationRegion == exclude || isPhoneMode()) {
2073             return;
2074         }
2075 
2076         if (removeExcludeFromScreenMagnificationFlagUsage()) {
2077             return;
2078         }
2079 
2080         mIsExcludeFromMagnificationRegion = exclude;
2081         if (exclude) {
2082             mWindowLayoutParams.privateFlags |=
2083                     WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
2084         } else {
2085             mWindowLayoutParams.privateFlags &=
2086                     ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
2087         }
2088         notifyUpdateLayoutParams();
2089     }
2090 
notifyUpdateLayoutParams()2091     void notifyUpdateLayoutParams() {
2092         if (mDragLayer.isAttachedToWindow()) {
2093             // Copy the current windowLayoutParams to mLastUpdatedLayoutParams and compare the diff.
2094             // If there is no change, we will skip the call to updateViewLayout.
2095             int changes = mLastUpdatedLayoutParams.copyFrom(mWindowLayoutParams);
2096             if (changes == 0) {
2097                 return;
2098             }
2099             if (enableTaskbarNoRecreate()) {
2100                 mWindowManager.updateViewLayout(mDragLayer.getRootView(), mWindowLayoutParams);
2101             } else {
2102                 mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
2103             }
2104         }
2105     }
2106 
showPopupMenuForIcon(BubbleTextView btv)2107     public void showPopupMenuForIcon(BubbleTextView btv) {
2108         setTaskbarWindowFullscreen(true);
2109         btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
2110     }
2111 
launchKeyboardFocusedTask()2112     public void launchKeyboardFocusedTask() {
2113         mControllers.uiController.launchKeyboardFocusedTask();
2114     }
2115 
isInApp()2116     public boolean isInApp() {
2117         return mControllers.taskbarStashController.isInApp();
2118     }
2119 
isInOverview()2120     public boolean isInOverview() {
2121         return mControllers.taskbarStashController.isInOverview();
2122     }
2123 
isInStashedLauncherState()2124     public boolean isInStashedLauncherState() {
2125         return mControllers.taskbarStashController.isInStashedLauncherState();
2126     }
2127 
getTaskbarFeatureEvaluator()2128     public TaskbarFeatureEvaluator getTaskbarFeatureEvaluator() {
2129         return mTaskbarFeatureEvaluator;
2130     }
2131 
getTaskbarSpecsEvaluator()2132     public TaskbarSpecsEvaluator getTaskbarSpecsEvaluator() {
2133         return mTaskbarSpecsEvaluator;
2134     }
2135 
dumpLogs(String prefix, PrintWriter pw)2136     protected void dumpLogs(String prefix, PrintWriter pw) {
2137         pw.println(prefix + "TaskbarActivityContext:");
2138 
2139         pw.println(String.format(
2140                 "%s\tmNavMode=%s", prefix, mNavMode));
2141         pw.println(String.format(
2142                 "%s\tmImeDrawsImeNavBar=%b", prefix, mImeDrawsImeNavBar));
2143         pw.println(String.format(
2144                 "%s\tmIsUserSetupComplete=%b", prefix, mIsUserSetupComplete));
2145         pw.println(String.format(
2146                 "%s\tmWindowLayoutParams.height=%dpx", prefix, mWindowLayoutParams.height));
2147         mControllers.dumpLogs(prefix + "\t", pw);
2148         mDeviceProfile.dump(this, prefix, pw);
2149     }
2150 
2151     @VisibleForTesting
getTaskbarAllAppsTopPadding()2152     public int getTaskbarAllAppsTopPadding() {
2153         return mControllers.taskbarAllAppsController.getTaskbarAllAppsTopPadding();
2154     }
2155 
2156     @VisibleForTesting
getTaskbarAllAppsScroll()2157     public int getTaskbarAllAppsScroll() {
2158         return mControllers.taskbarAllAppsController.getTaskbarAllAppsScroll();
2159     }
2160 
2161     @VisibleForTesting
getStashedTaskbarScale()2162     public float getStashedTaskbarScale() {
2163         return mControllers.stashedHandleViewController.getStashedHandleHintScale().value;
2164     }
2165 
2166     /** Closes the KeyboardQuickSwitchView without an animation if open. */
closeKeyboardQuickSwitchView()2167     public void closeKeyboardQuickSwitchView() {
2168         mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
2169     }
2170 
isIconAlignedWithHotseat()2171     boolean isIconAlignedWithHotseat() {
2172         return mControllers.uiController.isIconAlignedWithHotseat();
2173     }
2174 
2175     // TODO(b/395061396): Remove `otherwise` when overview in widow is enabled.
2176     @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
getControllers()2177     public TaskbarControllers getControllers() {
2178         return mControllers;
2179     }
2180 }
2181