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