• 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.content.pm.PackageManager.FEATURE_PC;
19 import static android.os.Trace.TRACE_TAG_APP;
20 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
22 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
23 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
24 import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
25 
26 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
27 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
28 import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
29 import static com.android.launcher3.Utilities.isRunningInTestHarness;
30 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
31 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
32 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
33 import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW;
34 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
35 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
37 
38 import android.animation.AnimatorSet;
39 import android.animation.ValueAnimator;
40 import android.app.ActivityOptions;
41 import android.content.ActivityNotFoundException;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.pm.ActivityInfo.Config;
45 import android.content.pm.LauncherApps;
46 import android.content.res.Resources;
47 import android.graphics.Color;
48 import android.graphics.PixelFormat;
49 import android.graphics.Rect;
50 import android.hardware.display.DisplayManager;
51 import android.os.Process;
52 import android.os.Trace;
53 import android.provider.Settings;
54 import android.util.Log;
55 import android.view.Display;
56 import android.view.Gravity;
57 import android.view.RoundedCorner;
58 import android.view.Surface;
59 import android.view.View;
60 import android.view.WindowInsets;
61 import android.view.WindowManager;
62 import android.widget.FrameLayout;
63 import android.widget.Toast;
64 
65 import androidx.annotation.NonNull;
66 import androidx.annotation.Nullable;
67 import androidx.annotation.VisibleForTesting;
68 
69 import com.android.launcher3.AbstractFloatingView;
70 import com.android.launcher3.BubbleTextView;
71 import com.android.launcher3.DeviceProfile;
72 import com.android.launcher3.LauncherSettings.Favorites;
73 import com.android.launcher3.R;
74 import com.android.launcher3.anim.AnimatorPlaybackController;
75 import com.android.launcher3.config.FeatureFlags;
76 import com.android.launcher3.dot.DotInfo;
77 import com.android.launcher3.folder.Folder;
78 import com.android.launcher3.folder.FolderIcon;
79 import com.android.launcher3.logger.LauncherAtom;
80 import com.android.launcher3.logging.StatsLogManager;
81 import com.android.launcher3.model.data.AppInfo;
82 import com.android.launcher3.model.data.FolderInfo;
83 import com.android.launcher3.model.data.ItemInfo;
84 import com.android.launcher3.model.data.WorkspaceItemInfo;
85 import com.android.launcher3.popup.PopupContainerWithArrow;
86 import com.android.launcher3.popup.PopupDataProvider;
87 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
88 import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
89 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
90 import com.android.launcher3.taskbar.bubbles.BubbleBarController;
91 import com.android.launcher3.taskbar.bubbles.BubbleBarView;
92 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
93 import com.android.launcher3.taskbar.bubbles.BubbleControllers;
94 import com.android.launcher3.taskbar.bubbles.BubbleDismissController;
95 import com.android.launcher3.taskbar.bubbles.BubbleDragController;
96 import com.android.launcher3.taskbar.bubbles.BubbleStashController;
97 import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController;
98 import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
99 import com.android.launcher3.testing.TestLogging;
100 import com.android.launcher3.testing.shared.TestProtocol;
101 import com.android.launcher3.touch.ItemClickHandler;
102 import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
103 import com.android.launcher3.util.ActivityOptionsWrapper;
104 import com.android.launcher3.util.DisplayController;
105 import com.android.launcher3.util.Executors;
106 import com.android.launcher3.util.NavigationMode;
107 import com.android.launcher3.util.PackageManagerHelper;
108 import com.android.launcher3.util.RunnableList;
109 import com.android.launcher3.util.SettingsCache;
110 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
111 import com.android.launcher3.util.TraceHelper;
112 import com.android.launcher3.util.ViewCache;
113 import com.android.launcher3.views.ActivityContext;
114 import com.android.quickstep.views.RecentsView;
115 import com.android.quickstep.views.TaskView;
116 import com.android.systemui.shared.recents.model.Task;
117 import com.android.systemui.shared.rotation.RotationButtonController;
118 import com.android.systemui.shared.system.ActivityManagerWrapper;
119 import com.android.systemui.unfold.updates.RotationChangeProvider;
120 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
121 
122 import java.io.PrintWriter;
123 import java.util.Collections;
124 import java.util.Optional;
125 
126 /**
127  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
128  * that are used by both Launcher and Taskbar (such as Folder) to reference a generic
129  * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
130  */
131 public class TaskbarActivityContext extends BaseTaskbarContext {
132 
133     private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar";
134 
135     private static final String TAG = "TaskbarActivityContext";
136 
137     private static final String WINDOW_TITLE = "Taskbar";
138 
139     private final TaskbarDragLayer mDragLayer;
140     private final TaskbarControllers mControllers;
141 
142     private final WindowManager mWindowManager;
143     private final @Nullable RoundedCorner mLeftCorner, mRightCorner;
144     private DeviceProfile mDeviceProfile;
145     private WindowManager.LayoutParams mWindowLayoutParams;
146     private boolean mIsFullscreen;
147     // The size we should return to when we call setTaskbarWindowFullscreen(false)
148     private int mLastRequestedNonFullscreenHeight;
149 
150     private NavigationMode mNavMode;
151     private boolean mImeDrawsImeNavBar;
152     private final ViewCache mViewCache = new ViewCache();
153 
154     private final boolean mIsSafeModeEnabled;
155     private final boolean mIsUserSetupComplete;
156     private final boolean mIsNavBarForceVisible;
157     private final boolean mIsNavBarKidsMode;
158 
159     private boolean mIsDestroyed = false;
160     // The flag to know if the window is excluded from magnification region computation.
161     private boolean mIsExcludeFromMagnificationRegion = false;
162     private boolean mBindingItems = false;
163     private boolean mAddedWindow = false;
164 
165     // The bounds of the taskbar items relative to TaskbarDragLayer
166     private final Rect mTransientTaskbarBounds = new Rect();
167 
168     private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
169 
TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp, TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider)170     public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp,
171             TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
172             unfoldTransitionProgressProvider) {
173         super(windowContext);
174 
175         applyDeviceProfile(launcherDp);
176 
177         final Resources resources = getResources();
178 
179         mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
180         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
181                 () -> getPackageManager().isSafeMode());
182 
183         // TODO(b/244231596) For shared Taskbar window, update this value in applyDeviceProfile()
184         //  instead so to get correct value when recreating the taskbar
185         SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
186         mIsUserSetupComplete = settingsCache.getValue(
187                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
188         mIsNavBarKidsMode = settingsCache.getValue(
189                 Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
190         mIsNavBarForceVisible = mIsNavBarKidsMode;
191 
192         // Get display and corners first, as views might use them in constructor.
193         Display display = windowContext.getDisplay();
194         Context c = getApplicationContext();
195         mWindowManager = c.getSystemService(WindowManager.class);
196         mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
197         mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
198 
199         // Inflate views.
200         boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
201         int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode
202                 ? R.layout.transient_taskbar
203                 : R.layout.taskbar;
204         mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
205         TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
206         TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
207         FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
208         StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
209         BubbleBarView bubbleBarView = mDragLayer.findViewById(R.id.taskbar_bubbles);
210         StashedHandleView bubbleHandleView = mDragLayer.findViewById(R.id.stashed_bubble_handle);
211 
212         mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
213 
214         final boolean isDesktopMode = getPackageManager().hasSystemFeature(FEATURE_PC);
215 
216         // If Bubble bar is present, TaskbarControllers depends on it so build it first.
217         Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
218         if (BubbleBarController.BUBBLE_BAR_ENABLED && bubbleBarView != null) {
219             bubbleControllersOptional = Optional.of(new BubbleControllers(
220                     new BubbleBarController(this, bubbleBarView),
221                     new BubbleBarViewController(this, bubbleBarView),
222                     new BubbleStashController(this),
223                     new BubbleStashedHandleViewController(this, bubbleHandleView),
224                     new BubbleDragController(this),
225                     new BubbleDismissController(this, mDragLayer)));
226         }
227 
228         // Construct controllers.
229         RotationButtonController rotationButtonController = new RotationButtonController(this,
230                 c.getColor(R.color.floating_rotation_button_light_color),
231                 c.getColor(R.color.floating_rotation_button_dark_color),
232                 R.drawable.ic_sysbar_rotate_button_ccw_start_0,
233                 R.drawable.ic_sysbar_rotate_button_ccw_start_90,
234                 R.drawable.ic_sysbar_rotate_button_cw_start_0,
235                 R.drawable.ic_sysbar_rotate_button_cw_start_90,
236                 () -> getDisplay().getRotation());
237         rotationButtonController.setBgExecutor(Executors.THREAD_POOL_EXECUTOR);
238 
239         mControllers = new TaskbarControllers(this,
240                 new TaskbarDragController(this),
241                 buttonController,
242                 isDesktopMode
243                         ? new DesktopNavbarButtonsViewController(this, navButtonsView)
244                         : new NavbarButtonsViewController(this, navButtonsView),
245                 rotationButtonController,
246                 new TaskbarDragLayerController(this, mDragLayer),
247                 new TaskbarViewController(this, taskbarView),
248                 new TaskbarScrimViewController(this, taskbarScrimView),
249                 new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
250                     mWindowManager,
251                     new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
252                         getMainThreadHandler())),
253                 new TaskbarKeyguardController(this),
254                 new StashedHandleViewController(this, stashedHandleView),
255                 new TaskbarStashController(this),
256                 new TaskbarAutohideSuspendController(this),
257                 new TaskbarPopupController(this),
258                 new TaskbarForceVisibleImmersiveController(this),
259                 new TaskbarOverlayController(this, launcherDp),
260                 new TaskbarAllAppsController(),
261                 new TaskbarInsetsController(this),
262                 new VoiceInteractionWindowController(this),
263                 new TaskbarTranslationController(this),
264                 new TaskbarSpringOnStashController(this),
265                 isDesktopMode
266                         ? new DesktopTaskbarRecentAppsController(this)
267                         : TaskbarRecentAppsController.DEFAULT,
268                 new TaskbarEduTooltipController(this),
269                 new KeyboardQuickSwitchController(),
270                 new TaskbarDividerPopupController(this),
271                 bubbleControllersOptional);
272     }
273 
274     /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
updateDeviceProfile(DeviceProfile launcherDp)275     public void updateDeviceProfile(DeviceProfile launcherDp) {
276         applyDeviceProfile(launcherDp);
277 
278         mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
279         AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
280         // Reapply fullscreen to take potential new screen size into account.
281         setTaskbarWindowFullscreen(mIsFullscreen);
282 
283         dispatchDeviceProfileChanged();
284     }
285 
286     /**
287      * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
288      * the icon size
289      */
applyDeviceProfile(DeviceProfile originDeviceProfile)290     private void applyDeviceProfile(DeviceProfile originDeviceProfile) {
291         mDeviceProfile = originDeviceProfile.toBuilder(this)
292                 .withDimensionsOverride(deviceProfile -> {
293                     // Taskbar should match the number of icons of hotseat
294                     deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
295                     // Same QSB width to have a smooth animation
296                     deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
297 
298                     // Update icon size
299                     deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
300                     deviceProfile.updateIconSize(1f, getResources());
301                 }).build();
302         mNavMode = DisplayController.getNavigationMode(this);
303     }
304 
305 
init(@onNull TaskbarSharedState sharedState)306     public void init(@NonNull TaskbarSharedState sharedState) {
307         mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
308         mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
309         mWindowLayoutParams = createAllWindowParams();
310 
311         // Initialize controllers after all are constructed.
312         mControllers.init(sharedState);
313         // This may not be necessary and can be reverted once we move towards recreating all
314         // controllers without re-creating the window
315         mControllers.rotationButtonController.onNavigationModeChanged(mNavMode.resValue);
316         updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
317         disableNavBarElements(sharedState.disableNavBarDisplayId, sharedState.disableNavBarState1,
318                 sharedState.disableNavBarState2, false /* animate */);
319         onSystemBarAttributesChanged(sharedState.systemBarAttrsDisplayId,
320                 sharedState.systemBarAttrsBehavior);
321         onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity);
322 
323         if (FLAG_HIDE_NAVBAR_WINDOW) {
324             // W/ the flag not set this entire class gets re-created, which resets the value of
325             // mIsDestroyed. We re-use the class for small-screen, so we explicitly have to mark
326             // this class as non-destroyed
327             mIsDestroyed = false;
328         }
329 
330         if (!mAddedWindow) {
331             mWindowManager.addView(mDragLayer, mWindowLayoutParams);
332             mAddedWindow = true;
333         } else {
334             mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
335         }
336     }
337 
338     /**
339      * Show Taskbar upon receiving broadcast
340      */
showTaskbarFromBroadcast()341     public void showTaskbarFromBroadcast() {
342         mControllers.taskbarStashController.showTaskbarFromBroadcast();
343     }
344 
345     /** Toggles Taskbar All Apps overlay. */
toggleAllApps()346     public void toggleAllApps() {
347         mControllers.taskbarAllAppsController.toggle();
348     }
349 
350     @Override
getDeviceProfile()351     public DeviceProfile getDeviceProfile() {
352         return mDeviceProfile;
353     }
354 
355     @Override
dispatchDeviceProfileChanged()356     public void dispatchDeviceProfileChanged() {
357         super.dispatchDeviceProfileChanged();
358         Trace.instantForTrack(TRACE_TAG_APP, "TaskbarActivityContext#DeviceProfileChanged",
359                 getDeviceProfile().toSmallString());
360     }
361 
362     /**
363      * Returns the View bounds of transient taskbar.
364      */
getTransientTaskbarBounds()365     public Rect getTransientTaskbarBounds() {
366         return mTransientTaskbarBounds;
367     }
368 
369     @Override
getStatsLogManager()370     public StatsLogManager getStatsLogManager() {
371         // Used to mock, can't mock a default interface method directly
372         return super.getStatsLogManager();
373     }
374 
375     /**
376      * Creates LayoutParams for adding a view directly to WindowManager as a new window.
377      * @param type The window type to pass to the created WindowManager.LayoutParams.
378      * @param title The window title to pass to the created WindowManager.LayoutParams.
379      */
createDefaultWindowLayoutParams(int type, String title)380     public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) {
381         int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
382                 | WindowManager.LayoutParams.FLAG_SLIPPERY
383                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
384         if (DisplayController.isTransientTaskbar(this) && !isRunningInTestHarness()) {
385             windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
386                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
387         }
388         WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
389                 MATCH_PARENT,
390                 mLastRequestedNonFullscreenHeight,
391                 type,
392                 windowFlags,
393                 PixelFormat.TRANSLUCENT);
394         windowLayoutParams.setTitle(title);
395         windowLayoutParams.packageName = getPackageName();
396         windowLayoutParams.gravity = Gravity.BOTTOM;
397         windowLayoutParams.setFitInsetsTypes(0);
398         windowLayoutParams.receiveInsetsIgnoringZOrder = true;
399         windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
400         windowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
401         windowLayoutParams.privateFlags =
402                 WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
403         windowLayoutParams.accessibilityTitle = getString(
404                 TaskbarManager.isPhoneMode(mDeviceProfile)
405                         ? R.string.taskbar_phone_a11y_title
406                         : R.string.taskbar_a11y_title);
407 
408         return windowLayoutParams;
409     }
410 
411     /**
412      * Creates {@link WindowManager.LayoutParams} for Taskbar, and also sets LP.paramsForRotation
413      * for taskbar showing as navigation bar
414      */
createAllWindowParams()415     private WindowManager.LayoutParams createAllWindowParams() {
416         WindowManager.LayoutParams windowLayoutParams =
417                 createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL,
418                         TaskbarActivityContext.WINDOW_TITLE);
419         boolean isPhoneNavMode = TaskbarManager.isPhoneButtonNavMode(this);
420         if (!isPhoneNavMode) {
421             return windowLayoutParams;
422         }
423 
424         // Provide WM layout params for all rotations to cache, see NavigationBar#getBarLayoutParams
425         int width = WindowManager.LayoutParams.MATCH_PARENT;
426         int height = WindowManager.LayoutParams.MATCH_PARENT;
427         int gravity = Gravity.BOTTOM;
428         windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4];
429         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
430             WindowManager.LayoutParams lp =
431                     createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL,
432                             TaskbarActivityContext.WINDOW_TITLE);
433             switch (rot) {
434                 case Surface.ROTATION_0, Surface.ROTATION_180 -> {
435                     // Defaults are fine
436                     width = WindowManager.LayoutParams.MATCH_PARENT;
437                     height = mLastRequestedNonFullscreenHeight;
438                     gravity = Gravity.BOTTOM;
439                 }
440                 case Surface.ROTATION_90 -> {
441                     width = mLastRequestedNonFullscreenHeight;
442                     height = WindowManager.LayoutParams.MATCH_PARENT;
443                     gravity = Gravity.END;
444                 }
445                 case Surface.ROTATION_270 -> {
446                     width = mLastRequestedNonFullscreenHeight;
447                     height = WindowManager.LayoutParams.MATCH_PARENT;
448                     gravity = Gravity.START;
449                 }
450 
451             }
452             lp.width = width;
453             lp.height = height;
454             lp.gravity = gravity;
455             windowLayoutParams.paramsForRotation[rot] = lp;
456         }
457 
458         // Override current layout params
459         WindowManager.LayoutParams currentParams =
460                 windowLayoutParams.paramsForRotation[getDisplay().getRotation()];
461         windowLayoutParams.width = currentParams.width;
462         windowLayoutParams.height = currentParams.height;
463         windowLayoutParams.gravity = currentParams.gravity;
464 
465         return windowLayoutParams;
466     }
467 
onConfigurationChanged(@onfig int configChanges)468     public void onConfigurationChanged(@Config int configChanges) {
469         mControllers.onConfigurationChanged(configChanges);
470         if (!mIsUserSetupComplete) {
471             setTaskbarWindowHeight(getSetupWindowHeight());
472         }
473     }
474 
isThreeButtonNav()475     public boolean isThreeButtonNav() {
476         return mNavMode == NavigationMode.THREE_BUTTONS;
477     }
478 
isGestureNav()479     public boolean isGestureNav() {
480         return mNavMode == NavigationMode.NO_BUTTON;
481     }
482 
imeDrawsImeNavBar()483     public boolean imeDrawsImeNavBar() {
484         return mImeDrawsImeNavBar;
485     }
486 
getLeftCornerRadius()487     public int getLeftCornerRadius() {
488         return mLeftCorner == null ? 0 : mLeftCorner.getRadius();
489     }
490 
getRightCornerRadius()491     public int getRightCornerRadius() {
492         return mRightCorner == null ? 0 : mRightCorner.getRadius();
493     }
494 
getWindowLayoutParams()495     public WindowManager.LayoutParams getWindowLayoutParams() {
496         return mWindowLayoutParams;
497     }
498 
499     @Override
getDragLayer()500     public TaskbarDragLayer getDragLayer() {
501         return mDragLayer;
502     }
503 
504     @Override
getFolderBoundingBox()505     public Rect getFolderBoundingBox() {
506         return mControllers.taskbarDragLayerController.getFolderBoundingBox();
507     }
508 
509     @Override
getDragController()510     public TaskbarDragController getDragController() {
511         return mControllers.taskbarDragController;
512     }
513 
514     @Nullable
getBubbleControllers()515     public BubbleControllers getBubbleControllers() {
516         return mControllers.bubbleControllers.orElse(null);
517     }
518 
519     @Override
getViewCache()520     public ViewCache getViewCache() {
521         return mViewCache;
522     }
523 
524     @Override
getItemOnClickListener()525     public View.OnClickListener getItemOnClickListener() {
526         return this::onTaskbarIconClicked;
527     }
528 
529     /**
530      * Change from hotseat/predicted hotseat to taskbar container.
531      */
532     @Override
applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder)533     public void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) {
534         if (!itemInfoBuilder.hasContainerInfo()) {
535             return;
536         }
537         LauncherAtom.ContainerInfo oldContainer = itemInfoBuilder.getContainerInfo();
538 
539         LauncherAtom.TaskBarContainer.Builder taskbarBuilder =
540                 LauncherAtom.TaskBarContainer.newBuilder();
541         if (mControllers.uiController.isInOverview()) {
542             taskbarBuilder.setTaskSwitcherContainer(
543                     LauncherAtom.TaskSwitcherContainer.newBuilder());
544         }
545 
546         if (oldContainer.hasPredictedHotseatContainer()) {
547             LauncherAtom.PredictedHotseatContainer predictedHotseat =
548                     oldContainer.getPredictedHotseatContainer();
549 
550             if (predictedHotseat.hasIndex()) {
551                 taskbarBuilder.setIndex(predictedHotseat.getIndex());
552             }
553             if (predictedHotseat.hasCardinality()) {
554                 taskbarBuilder.setCardinality(predictedHotseat.getCardinality());
555             }
556 
557             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
558                     .setTaskBarContainer(taskbarBuilder));
559         } else if (oldContainer.hasHotseat()) {
560             LauncherAtom.HotseatContainer hotseat = oldContainer.getHotseat();
561 
562             if (hotseat.hasIndex()) {
563                 taskbarBuilder.setIndex(hotseat.getIndex());
564             }
565 
566             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
567                     .setTaskBarContainer(taskbarBuilder));
568         } else if (oldContainer.hasFolder() && oldContainer.getFolder().hasHotseat()) {
569             LauncherAtom.FolderContainer.Builder folderBuilder = oldContainer.getFolder()
570                     .toBuilder();
571             LauncherAtom.HotseatContainer hotseat = folderBuilder.getHotseat();
572 
573             if (hotseat.hasIndex()) {
574                 taskbarBuilder.setIndex(hotseat.getIndex());
575             }
576 
577             folderBuilder.setTaskbar(taskbarBuilder);
578             folderBuilder.clearHotseat();
579             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
580                     .setFolder(folderBuilder));
581         } else if (oldContainer.hasAllAppsContainer()) {
582             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
583                     .setAllAppsContainer(oldContainer.getAllAppsContainer().toBuilder()
584                             .setTaskbarContainer(taskbarBuilder)));
585         } else if (oldContainer.hasPredictionContainer()) {
586             itemInfoBuilder.setContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
587                     .setPredictionContainer(oldContainer.getPredictionContainer().toBuilder()
588                             .setTaskbarContainer(taskbarBuilder)));
589         }
590     }
591 
592     @Override
getDotInfoForItem(ItemInfo info)593     public DotInfo getDotInfoForItem(ItemInfo info) {
594         return getPopupDataProvider().getDotInfoForItem(info);
595     }
596 
597     @NonNull
598     @Override
getPopupDataProvider()599     public PopupDataProvider getPopupDataProvider() {
600         return mControllers.taskbarPopupController.getPopupDataProvider();
601     }
602 
603     @Override
getAccessibilityDelegate()604     public View.AccessibilityDelegate getAccessibilityDelegate() {
605         return mAccessibilityDelegate;
606     }
607 
608     @Override
isBindingItems()609     public boolean isBindingItems() {
610         return mBindingItems;
611     }
612 
setBindingItems(boolean bindingItems)613     public void setBindingItems(boolean bindingItems) {
614         mBindingItems = bindingItems;
615     }
616 
617     @Override
onDragStart()618     public void onDragStart() {
619         setTaskbarWindowFullscreen(true);
620     }
621 
622     @Override
onDragEnd()623     public void onDragEnd() {
624         onDragEndOrViewRemoved();
625     }
626 
627     @Override
onPopupVisibilityChanged(boolean isVisible)628     public void onPopupVisibilityChanged(boolean isVisible) {
629         setTaskbarWindowFocusable(isVisible);
630     }
631 
632     @Override
onSplitScreenMenuButtonClicked()633     public void onSplitScreenMenuButtonClicked() {
634         PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(this);
635         if (popup != null) {
636             popup.addOnCloseCallback(() -> {
637                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
638             });
639         }
640     }
641 
642     @Override
makeDefaultActivityOptions(int splashScreenStyle)643     public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
644         RunnableList callbacks = new RunnableList();
645         ActivityOptions options = ActivityOptions.makeCustomAnimation(
646                 this, 0, 0, Color.TRANSPARENT,
647                 Executors.MAIN_EXECUTOR.getHandler(), null,
648                 elapsedRealTime -> callbacks.executeAllAndDestroy());
649         options.setSplashScreenStyle(splashScreenStyle);
650         options.setPendingIntentBackgroundActivityStartMode(
651                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
652         return new ActivityOptionsWrapper(options, callbacks);
653     }
654 
655     @Override
getActivityLaunchOptions(View v, @Nullable ItemInfo item)656     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
657         return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
658     }
659 
660     /**
661      * Sets a new data-source for this taskbar instance
662      */
setUIController(@onNull TaskbarUIController uiController)663     public void setUIController(@NonNull TaskbarUIController uiController) {
664         mControllers.setUiController(uiController);
665     }
666 
667     /**
668      * Sets the flag indicating setup UI is visible
669      */
setSetupUIVisible(boolean isVisible)670     public void setSetupUIVisible(boolean isVisible) {
671         mControllers.taskbarStashController.setSetupUIVisible(isVisible);
672     }
673 
674     /**
675      * Called when this instance of taskbar is no longer needed
676      */
onDestroy()677     public void onDestroy() {
678         mIsDestroyed = true;
679         setUIController(TaskbarUIController.DEFAULT);
680         mControllers.onDestroy();
681         if (!FLAG_HIDE_NAVBAR_WINDOW) {
682             mWindowManager.removeViewImmediate(mDragLayer);
683             mAddedWindow = false;
684         }
685     }
686 
isDestroyed()687     public boolean isDestroyed() {
688         return mIsDestroyed;
689     }
690 
updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit)691     public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) {
692         mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
693                 fromInit);
694         boolean isShadeVisible = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
695         onNotificationShadeExpandChanged(isShadeVisible, fromInit);
696         mControllers.taskbarViewController.setRecentsButtonDisabled(
697                 mControllers.navbarButtonsViewController.isRecentsDisabled()
698                         || isNavBarKidsModeActive());
699         mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
700                 mControllers.navbarButtonsViewController.isHomeDisabled());
701         mControllers.stashedHandleViewController.updateStateForSysuiFlags(systemUiStateFlags);
702         mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
703         mControllers.taskbarStashController.updateStateForSysuiFlags(
704                 systemUiStateFlags, fromInit || !isUserSetupComplete());
705         mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
706                 fromInit);
707         mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
708         mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
709         mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
710                 (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
711         mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags);
712         mControllers.bubbleControllers.ifPresent(controllers -> {
713             controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags);
714             controllers.bubbleStashedHandleViewController.setIsHomeButtonDisabled(
715                     mControllers.navbarButtonsViewController.isHomeDisabled());
716         });
717     }
718 
719     /**
720      * Hides the taskbar icons and background when the notication shade is expanded.
721      */
onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim)722     private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
723         float alpha = isExpanded ? 0 : 1;
724         AnimatorSet anim = new AnimatorSet();
725         anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().get(
726                 TaskbarViewController.ALPHA_INDEX_NOTIFICATION_EXPANDED).animateToValue(alpha));
727         anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar()
728                 .animateToValue(alpha));
729         anim.start();
730         if (skipAnim) {
731             anim.end();
732         }
733     }
734 
onRotationProposal(int rotation, boolean isValid)735     public void onRotationProposal(int rotation, boolean isValid) {
736         mControllers.rotationButtonController.onRotationProposal(rotation, isValid);
737     }
738 
disableNavBarElements(int displayId, int state1, int state2, boolean animate)739     public void disableNavBarElements(int displayId, int state1, int state2, boolean animate) {
740         if (displayId != getDisplayId()) {
741             return;
742         }
743         mControllers.rotationButtonController.onDisable2FlagChanged(state2);
744     }
745 
onSystemBarAttributesChanged(int displayId, int behavior)746     public void onSystemBarAttributesChanged(int displayId, int behavior) {
747         mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior);
748     }
749 
onNavButtonsDarkIntensityChanged(float darkIntensity)750     public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
751         mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
752                 .updateValue(darkIntensity);
753     }
754 
755     /**
756      * Called to update a {@link AutohideSuspendFlag} with a new value.
757      */
setAutohideSuspendFlag(@utohideSuspendFlag int flag, boolean newValue)758     public void setAutohideSuspendFlag(@AutohideSuspendFlag int flag, boolean newValue) {
759         mControllers.taskbarAutohideSuspendController.updateFlag(flag, newValue);
760     }
761 
762     /**
763      * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
764      */
setTaskbarWindowFullscreen(boolean fullscreen)765     public void setTaskbarWindowFullscreen(boolean fullscreen) {
766         setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen);
767         mIsFullscreen = fullscreen;
768         setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
769     }
770 
771     /**
772      * Called when drag ends or when a view is removed from the DragLayer.
773      */
onDragEndOrViewRemoved()774     void onDragEndOrViewRemoved() {
775         boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress();
776 
777         // Overlay AFVs are in a separate window and do not require Taskbar to be fullscreen.
778         if (!isDragInProgress
779                 && !AbstractFloatingView.hasOpenView(
780                         this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
781             // Reverts Taskbar window to its original size
782             setTaskbarWindowFullscreen(false);
783         }
784 
785         setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, isDragInProgress);
786     }
787 
isTaskbarWindowFullscreen()788     public boolean isTaskbarWindowFullscreen() {
789         return mIsFullscreen;
790     }
791 
792     /**
793      * Updates the TaskbarContainer height (pass {@link #getDefaultTaskbarWindowHeight()} to reset).
794      */
setTaskbarWindowHeight(int height)795     public void setTaskbarWindowHeight(int height) {
796         if (mWindowLayoutParams.height == height || mIsDestroyed) {
797             return;
798         }
799         if (height == MATCH_PARENT) {
800             height = mDeviceProfile.heightPx;
801         } else {
802             mLastRequestedNonFullscreenHeight = height;
803             if (mIsFullscreen) {
804                 // We still need to be fullscreen, so defer any change to our height until we call
805                 // setTaskbarWindowFullscreen(false). For example, this could happen when dragging
806                 // from the gesture region, as the drag will cancel the gesture and reset launcher's
807                 // state, which in turn normally would reset the taskbar window height as well.
808                 return;
809             }
810         }
811         mWindowLayoutParams.height = height;
812         mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
813         mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
814     }
815 
816     /**
817      * Returns the default height of the window, including the static corner radii above taskbar.
818      */
getDefaultTaskbarWindowHeight()819     public int getDefaultTaskbarWindowHeight() {
820         Resources resources = getResources();
821 
822         if (FLAG_HIDE_NAVBAR_WINDOW && mDeviceProfile.isPhone) {
823             return isThreeButtonNav() ?
824                     resources.getDimensionPixelSize(R.dimen.taskbar_size) :
825                     resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
826         }
827 
828         if (!isUserSetupComplete()) {
829             return getSetupWindowHeight();
830         }
831 
832         if (DisplayController.isTransientTaskbar(this)) {
833             return mDeviceProfile.taskbarHeight
834                     + (2 * mDeviceProfile.taskbarBottomMargin)
835                     + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur);
836         }
837 
838         return mDeviceProfile.taskbarHeight
839                 + Math.max(getLeftCornerRadius(), getRightCornerRadius());
840     }
841 
getSetupWindowHeight()842     public int getSetupWindowHeight() {
843         return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
844     }
845 
846     /**
847      * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
848      * window.
849      */
setTaskbarWindowFocusable(boolean focusable)850     public void setTaskbarWindowFocusable(boolean focusable) {
851         if (focusable) {
852             mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
853         } else {
854             mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
855         }
856         mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
857     }
858 
859     /**
860      * Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
861      */
applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow)862     public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
863         if (!DisplayController.isTransientTaskbar(this)) {
864             return;
865         }
866         if (shouldForceShow) {
867             mWindowLayoutParams.forciblyShownTypes |= WindowInsets.Type.navigationBars();
868         } else {
869             mWindowLayoutParams.forciblyShownTypes &= ~WindowInsets.Type.navigationBars();
870         }
871         notifyUpdateLayoutParams();
872     }
873 
874     /**
875      * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
876      * window. If we're now focusable, also move nav buttons to a separate window above IME.
877      */
setTaskbarWindowFocusableForIme(boolean focusable)878     public void setTaskbarWindowFocusableForIme(boolean focusable) {
879         if (focusable) {
880             mControllers.navbarButtonsViewController.moveNavButtonsToNewWindow();
881         } else {
882             mControllers.navbarButtonsViewController.moveNavButtonsBackToTaskbarWindow();
883         }
884         setTaskbarWindowFocusable(focusable);
885     }
886 
887     /** Adds the given view to WindowManager with the provided LayoutParams (creates new window). */
addWindowView(View view, WindowManager.LayoutParams windowLayoutParams)888     public void addWindowView(View view, WindowManager.LayoutParams windowLayoutParams) {
889         if (!view.isAttachedToWindow()) {
890             mWindowManager.addView(view, windowLayoutParams);
891         }
892     }
893 
894     /** Removes the given view from WindowManager. See {@link #addWindowView}. */
removeWindowView(View view)895     public void removeWindowView(View view) {
896         if (view.isAttachedToWindow()) {
897             mWindowManager.removeViewImmediate(view);
898         }
899     }
900 
901     @Override
startSplitSelection(SplitSelectSource splitSelectSource)902     public void startSplitSelection(SplitSelectSource splitSelectSource) {
903         mControllers.uiController.startSplitSelection(splitSelectSource);
904     }
905 
onTaskbarIconClicked(View view)906     protected void onTaskbarIconClicked(View view) {
907         boolean shouldCloseAllOpenViews = true;
908         Object tag = view.getTag();
909         if (tag instanceof Task) {
910             Task task = (Task) tag;
911             ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
912                     ActivityOptions.makeBasic());
913             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
914         } else if (tag instanceof FolderInfo) {
915             shouldCloseAllOpenViews = false;
916             FolderIcon folderIcon = (FolderIcon) view;
917             Folder folder = folderIcon.getFolder();
918 
919             folder.setOnFolderStateChangedListener(newState -> {
920                 if (newState == Folder.STATE_OPEN) {
921                     setTaskbarWindowFocusableForIme(true);
922                 } else if (newState == Folder.STATE_CLOSED) {
923                     // Defer by a frame to ensure we're no longer fullscreen and thus won't jump.
924                     getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false));
925                     folder.setOnFolderStateChangedListener(null);
926                 }
927             });
928 
929             setTaskbarWindowFullscreen(true);
930 
931             getDragLayer().post(() -> {
932                 folder.animateOpen();
933                 getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
934 
935                 folder.iterateOverItems((itemInfo, itemView) -> {
936                     mControllers.taskbarViewController
937                             .setClickAndLongClickListenersForIcon(itemView);
938                     // To play haptic when dragging, like other Taskbar items do.
939                     itemView.setHapticFeedbackEnabled(true);
940                     return false;
941                 });
942             });
943         } else if (tag instanceof WorkspaceItemInfo) {
944             // Tapping a launchable icon on Taskbar
945             WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
946             if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) {
947                 TaskbarUIController taskbarUIController = mControllers.uiController;
948                 RecentsView recents = taskbarUIController.getRecentsView();
949                 if (recents != null && recents.isSplitSelectionActive()) {
950                     // If we are selecting a second app for split, launch the split tasks
951                     taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
952                 } else {
953                     // Else launch the selected task
954                     Intent intent = new Intent(info.getIntent())
955                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
956                     try {
957                         if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
958                             Toast.makeText(this, R.string.safemode_shortcut_error,
959                                     Toast.LENGTH_SHORT).show();
960                         } else if (info.isPromise()) {
961                             TestLogging.recordEvent(
962                                     TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
963                             intent = new PackageManagerHelper(this)
964                                     .getMarketIntent(info.getTargetPackage())
965                                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
966                             startActivity(intent);
967 
968                         } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
969                             TestLogging.recordEvent(
970                                     TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
971                             String id = info.getDeepShortcutId();
972                             String packageName = intent.getPackage();
973                             getSystemService(LauncherApps.class)
974                                     .startShortcut(packageName, id, null, null, info.user);
975                         } else {
976                             launchFromTaskbarPreservingSplitIfVisible(recents, info);
977                         }
978 
979                     } catch (NullPointerException
980                              | ActivityNotFoundException
981                              | SecurityException e) {
982                         Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
983                                 .show();
984                         Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
985                         return;
986                     }
987 
988                 }
989                 mControllers.uiController.onTaskbarIconLaunched(info);
990                 mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
991             }
992         } else if (tag instanceof AppInfo) {
993             // Tapping an item in AllApps
994             AppInfo info = (AppInfo) tag;
995             TaskbarUIController taskbarUIController = mControllers.uiController;
996             RecentsView recents = taskbarUIController.getRecentsView();
997             if (recents != null
998                     && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
999                 // If we are selecting a second app for split, launch the split tasks
1000                 taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
1001             } else {
1002                 launchFromTaskbarPreservingSplitIfVisible(recents, info);
1003             }
1004             mControllers.uiController.onTaskbarIconLaunched(info);
1005             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
1006         } else if (tag instanceof ItemClickProxy) {
1007             ((ItemClickProxy) tag).onItemClicked(view);
1008         } else {
1009             Log.e(TAG, "Unknown type clicked: " + tag);
1010         }
1011 
1012         if (shouldCloseAllOpenViews) {
1013             AbstractFloatingView.closeAllOpenViews(this);
1014         }
1015     }
1016 
1017     /**
1018      * Run when the user taps a Taskbar icon while in Overview. If the tapped app is currently
1019      * visible to the user in Overview, or is part of a visible split pair, we expand the TaskView
1020      * as if the user tapped on it (preserving the split pair). Otherwise, launch it normally
1021      * (potentially breaking a split pair).
1022      */
launchFromTaskbarPreservingSplitIfVisible(@ullable RecentsView recents, ItemInfo info)1023     private void launchFromTaskbarPreservingSplitIfVisible(@Nullable RecentsView recents,
1024             ItemInfo info) {
1025         if (recents == null) {
1026             return;
1027         }
1028         recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
1029                 Collections.singletonList(info.getComponentKey()),
1030                 foundTasks -> {
1031                     @Nullable Task foundTask = foundTasks.get(0);
1032                     if (foundTask != null) {
1033                         TaskView foundTaskView =
1034                                 recents.getTaskViewByTaskId(foundTask.key.id);
1035                         if (foundTaskView != null
1036                                 && foundTaskView.isVisibleToUser()) {
1037                             TestLogging.recordEvent(
1038                                     TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
1039                             foundTaskView.launchTasks();
1040                             return;
1041                         }
1042                     }
1043                     startItemInfoActivity(info);
1044                 });
1045     }
1046 
startItemInfoActivity(ItemInfo info)1047     private void startItemInfoActivity(ItemInfo info) {
1048         Intent intent = new Intent(info.getIntent())
1049                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1050         try {
1051             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
1052             if (info.user.equals(Process.myUserHandle())) {
1053                 // TODO(b/216683257): Use startActivityForResult for search results that require it.
1054                 startActivity(intent);
1055             } else {
1056                 getSystemService(LauncherApps.class).startMainActivity(
1057                         intent.getComponent(), info.user, intent.getSourceBounds(), null);
1058             }
1059         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
1060             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
1061                     .show();
1062             Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
1063         }
1064     }
1065 
1066     /**
1067      * Returns whether the taskbar is currently visually stashed.
1068      */
isTaskbarStashed()1069     public boolean isTaskbarStashed() {
1070         return mControllers.taskbarStashController.isStashed();
1071     }
1072 
1073     /**
1074      * Called when we detect a long press in the nav region before passing the gesture slop.
1075      *
1076      * @return Whether taskbar handled the long press, and thus should cancel the gesture.
1077      */
onLongPressToUnstashTaskbar()1078     public boolean onLongPressToUnstashTaskbar() {
1079         return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
1080     }
1081 
1082     /**
1083      * Called when we want to unstash taskbar when user performs swipes up gesture.
1084      */
onSwipeToUnstashTaskbar()1085     public void onSwipeToUnstashTaskbar() {
1086         mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
1087         mControllers.taskbarEduTooltipController.hide();
1088     }
1089 
1090     /**
1091      * Called when we want to open bubblebar when user performs swipes up gesture.
1092      */
onSwipeToOpenBubblebar()1093     public void onSwipeToOpenBubblebar() {
1094         mControllers.bubbleControllers.ifPresent(controllers -> {
1095             controllers.bubbleStashController.showBubbleBar(/* expandBubbles= */ true);
1096         });
1097     }
1098 
1099     /** Returns {@code true} if Taskbar All Apps is open. */
isTaskbarAllAppsOpen()1100     public boolean isTaskbarAllAppsOpen() {
1101         return mControllers.taskbarAllAppsController.isOpen();
1102     }
1103 
1104     /** Toggles the Taskbar's stash state. */
toggleTaskbarStash()1105     public void toggleTaskbarStash() {
1106         mControllers.taskbarStashController.toggleTaskbarStash();
1107     }
1108 
1109     /**
1110      * Called to start the taskbar translation spring to its settled translation (0).
1111      */
startTranslationSpring()1112     public void startTranslationSpring() {
1113         mControllers.taskbarTranslationController.startSpring();
1114     }
1115 
1116     /**
1117      * Returns a callback to help monitor the swipe gesture.
1118      */
getTranslationCallbacks()1119     public TransitionCallback getTranslationCallbacks() {
1120         return mControllers.taskbarTranslationController.getTransitionCallback();
1121     }
1122 
1123     /**
1124      * Called when a transient Autohide flag suspend status changes.
1125      */
onTransientAutohideSuspendFlagChanged(boolean isSuspended)1126     public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) {
1127         mControllers.taskbarStashController.updateTaskbarTimeout(isSuspended);
1128     }
1129 
1130     /**
1131      * Called when we detect a motion down or up/cancel in the nav region while stashed.
1132      *
1133      * @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
1134      */
startTaskbarUnstashHint(boolean animateForward)1135     public void startTaskbarUnstashHint(boolean animateForward) {
1136         // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code.
1137         startTaskbarUnstashHint(animateForward, /* forceUnstash = */ false);
1138     }
1139 
1140     /**
1141      * Called when we detect a motion down or up/cancel in the nav region while stashed.
1142      *
1143      * @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
1144      * @param forceUnstash   Whether we force the unstash hint.
1145      */
startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash)1146     public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) {
1147         // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code.
1148         mControllers.taskbarStashController.startUnstashHint(animateForward, forceUnstash);
1149     }
1150 
1151     /**
1152      * Enables manual taskbar stashing. This method should only be used for tests that need to
1153      * stash/unstash the taskbar.
1154      */
1155     @VisibleForTesting
enableManualStashingDuringTests(boolean enableManualStashing)1156     public void enableManualStashingDuringTests(boolean enableManualStashing) {
1157         mControllers.taskbarStashController.enableManualStashingDuringTests(enableManualStashing);
1158     }
1159 
1160     /**
1161      * Enables the auto timeout for taskbar stashing. This method should only be used for taskbar
1162      * testing.
1163      */
1164     @VisibleForTesting
enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout)1165     public void enableBlockingTimeoutDuringTests(boolean enableBlockingTimeout) {
1166         mControllers.taskbarStashController.enableBlockingTimeoutDuringTests(enableBlockingTimeout);
1167     }
1168 
1169     /**
1170      * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
1171      * taskbar at the end of a test.
1172      */
1173     @VisibleForTesting
unstashTaskbarIfStashed()1174     public void unstashTaskbarIfStashed() {
1175         if (DisplayController.isTransientTaskbar(this)) {
1176             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
1177         } else {
1178             mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
1179         }
1180     }
1181 
isUserSetupComplete()1182     protected boolean isUserSetupComplete() {
1183         return mIsUserSetupComplete;
1184     }
1185 
isNavBarKidsModeActive()1186     public boolean isNavBarKidsModeActive() {
1187         return mIsNavBarKidsMode && isThreeButtonNav();
1188     }
1189 
isNavBarForceVisible()1190     protected boolean isNavBarForceVisible() {
1191         return mIsNavBarForceVisible;
1192     }
1193 
1194     /**
1195      * Displays a single frame of the Launcher start from SUW animation.
1196      *
1197      * This animation is a combination of the Launcher resume animation, which animates the hotseat
1198      * icons into position, the Taskbar unstash to hotseat animation, which animates the Taskbar
1199      * stash bar into the hotseat icons, and an override to prevent showing the Taskbar all apps
1200      * button.
1201      *
1202      * This should be used to run a Taskbar unstash to hotseat animation whose progress matches a
1203      * swipe progress.
1204      *
1205      * @param duration a placeholder duration to be used to ensure all full-length
1206      *                 sub-animations are properly coordinated. This duration should not actually
1207      *                 be used since this animation tracks a swipe progress.
1208      */
createLauncherStartFromSuwAnim(int duration)1209     protected AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) {
1210         AnimatorSet fullAnimation = new AnimatorSet();
1211         fullAnimation.setDuration(duration);
1212 
1213         TaskbarUIController uiController = mControllers.uiController;
1214         if (uiController instanceof LauncherTaskbarUIController) {
1215             ((LauncherTaskbarUIController) uiController).addLauncherResumeAnimation(
1216                     fullAnimation, duration);
1217         }
1218         mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration);
1219 
1220         View allAppsButton = mControllers.taskbarViewController.getAllAppsButtonView();
1221         if (allAppsButton != null && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
1222             ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1);
1223             alphaOverride.setDuration(duration);
1224             alphaOverride.addUpdateListener(a -> {
1225                 // Override the alpha updates in the icon alignment animation.
1226                 allAppsButton.setAlpha(0);
1227             });
1228             fullAnimation.play(alphaOverride);
1229         }
1230 
1231         return AnimatorPlaybackController.wrap(fullAnimation, duration);
1232     }
1233 
1234     /**
1235      * Called when we determine the touchable region.
1236      *
1237      * @param exclude {@code true} then the magnification region computation will omit the window.
1238      */
excludeFromMagnificationRegion(boolean exclude)1239     public void excludeFromMagnificationRegion(boolean exclude) {
1240         if (mIsExcludeFromMagnificationRegion == exclude) {
1241             return;
1242         }
1243 
1244         mIsExcludeFromMagnificationRegion = exclude;
1245         if (exclude) {
1246             mWindowLayoutParams.privateFlags |=
1247                     WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
1248         } else {
1249             mWindowLayoutParams.privateFlags &=
1250                     ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
1251         }
1252         mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
1253     }
1254 
notifyUpdateLayoutParams()1255     void notifyUpdateLayoutParams() {
1256         if (mDragLayer.isAttachedToWindow()) {
1257             mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
1258         }
1259     }
1260 
showPopupMenuForIcon(BubbleTextView btv)1261     public void showPopupMenuForIcon(BubbleTextView btv) {
1262         setTaskbarWindowFullscreen(true);
1263         btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
1264     }
1265 
isInApp()1266     public boolean isInApp() {
1267         return mControllers.taskbarStashController.isInApp();
1268     }
1269 
isInStashedLauncherState()1270     public boolean isInStashedLauncherState() {
1271         return mControllers.taskbarStashController.isInStashedLauncherState();
1272     }
1273 
dumpLogs(String prefix, PrintWriter pw)1274     protected void dumpLogs(String prefix, PrintWriter pw) {
1275         pw.println(prefix + "TaskbarActivityContext:");
1276 
1277         pw.println(String.format(
1278                 "%s\tmNavMode=%s", prefix, mNavMode));
1279         pw.println(String.format(
1280                 "%s\tmImeDrawsImeNavBar=%b", prefix, mImeDrawsImeNavBar));
1281         pw.println(String.format(
1282                 "%s\tmIsUserSetupComplete=%b", prefix, mIsUserSetupComplete));
1283         pw.println(String.format(
1284                 "%s\tmWindowLayoutParams.height=%dpx", prefix, mWindowLayoutParams.height));
1285         pw.println(String.format(
1286                 "%s\tmBindInProgress=%b", prefix, mBindingItems));
1287         mControllers.dumpLogs(prefix + "\t", pw);
1288         mDeviceProfile.dump(this, prefix, pw);
1289     }
1290 
1291     @VisibleForTesting
getTaskbarAllAppsTopPadding()1292     public int getTaskbarAllAppsTopPadding() {
1293         return mControllers.taskbarAllAppsController.getTaskbarAllAppsTopPadding();
1294     }
1295 
1296     @VisibleForTesting
getTaskbarAllAppsScroll()1297     public int getTaskbarAllAppsScroll() {
1298         return mControllers.taskbarAllAppsController.getTaskbarAllAppsScroll();
1299     }
1300 
1301     @VisibleForTesting
getStashedTaskbarScale()1302     public float getStashedTaskbarScale() {
1303         return mControllers.stashedHandleViewController.getStashedHandleHintScale().value;
1304     }
1305 }
1306