1 /* 2 * Copyright (C) 2022 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.uioverrides; 17 18 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 19 import static android.os.Trace.TRACE_TAG_APP; 20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; 21 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; 22 23 import static com.android.app.animation.Interpolators.EMPHASIZED; 24 import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE; 25 import static com.android.launcher3.Flags.enablePredictiveBackGesture; 26 import static com.android.launcher3.Flags.enableUnfoldStateAnimation; 27 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO; 28 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE; 29 import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON; 30 import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND; 31 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; 32 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 33 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; 34 import static com.android.launcher3.LauncherState.ALL_APPS; 35 import static com.android.launcher3.LauncherState.NORMAL; 36 import static com.android.launcher3.LauncherState.NO_OFFSET; 37 import static com.android.launcher3.LauncherState.OVERVIEW; 38 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; 39 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; 40 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; 41 import static com.android.launcher3.config.FeatureFlags.enableSplitContextually; 42 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; 43 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; 44 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED; 45 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; 46 import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition; 47 import static com.android.launcher3.popup.SystemShortcut.APP_INFO; 48 import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP; 49 import static com.android.launcher3.popup.SystemShortcut.INSTALL; 50 import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL; 51 import static com.android.launcher3.popup.SystemShortcut.UNINSTALL_APP; 52 import static com.android.launcher3.popup.SystemShortcut.WIDGETS; 53 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX; 54 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX; 55 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.WIDGETS_PAGE_PROGRESS_INDEX; 56 import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL; 57 import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL; 58 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL; 59 import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; 60 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; 61 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; 62 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 63 import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; 64 import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT; 65 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; 66 import static com.android.window.flags.Flags.enableDesktopWindowingMode; 67 import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity; 68 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; 69 70 import android.animation.Animator; 71 import android.animation.AnimatorListenerAdapter; 72 import android.animation.AnimatorSet; 73 import android.app.ActivityOptions; 74 import android.content.Context; 75 import android.content.Intent; 76 import android.content.IntentSender; 77 import android.content.res.Configuration; 78 import android.graphics.Rect; 79 import android.graphics.RectF; 80 import android.hardware.display.DisplayManager; 81 import android.media.permission.SafeCloseable; 82 import android.os.Build; 83 import android.os.Bundle; 84 import android.os.IBinder; 85 import android.os.IRemoteCallback; 86 import android.os.SystemProperties; 87 import android.os.Trace; 88 import android.util.AttributeSet; 89 import android.view.Display; 90 import android.view.HapticFeedbackConstants; 91 import android.view.KeyEvent; 92 import android.view.View; 93 import android.widget.AnalogClock; 94 import android.widget.TextClock; 95 import android.window.BackEvent; 96 import android.window.OnBackAnimationCallback; 97 import android.window.OnBackInvokedDispatcher; 98 import android.window.RemoteTransition; 99 import android.window.SplashScreen; 100 101 import androidx.annotation.BinderThread; 102 import androidx.annotation.NonNull; 103 import androidx.annotation.Nullable; 104 import androidx.annotation.RequiresApi; 105 106 import com.android.app.viewcapture.ViewCaptureFactory; 107 import com.android.launcher3.AbstractFloatingView; 108 import com.android.launcher3.DeviceProfile; 109 import com.android.launcher3.Flags; 110 import com.android.launcher3.InvariantDeviceProfile; 111 import com.android.launcher3.Launcher; 112 import com.android.launcher3.LauncherSettings.Favorites; 113 import com.android.launcher3.LauncherState; 114 import com.android.launcher3.QuickstepAccessibilityDelegate; 115 import com.android.launcher3.QuickstepTransitionManager; 116 import com.android.launcher3.R; 117 import com.android.launcher3.Utilities; 118 import com.android.launcher3.Workspace; 119 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; 120 import com.android.launcher3.anim.AnimatorPlaybackController; 121 import com.android.launcher3.anim.PendingAnimation; 122 import com.android.launcher3.apppairs.AppPairIcon; 123 import com.android.launcher3.appprediction.PredictionRowView; 124 import com.android.launcher3.config.FeatureFlags; 125 import com.android.launcher3.desktop.DesktopRecentsTransitionController; 126 import com.android.launcher3.hybridhotseat.HotseatPredictionController; 127 import com.android.launcher3.logging.InstanceId; 128 import com.android.launcher3.logging.StatsLogManager; 129 import com.android.launcher3.logging.StatsLogManager.StatsLogger; 130 import com.android.launcher3.model.BgDataModel.FixedContainerItems; 131 import com.android.launcher3.model.WellbeingModel; 132 import com.android.launcher3.model.data.ItemInfo; 133 import com.android.launcher3.popup.SystemShortcut; 134 import com.android.launcher3.proxy.ProxyActivityStarter; 135 import com.android.launcher3.statehandlers.DepthController; 136 import com.android.launcher3.statehandlers.DesktopVisibilityController; 137 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; 138 import com.android.launcher3.statemanager.StateManager.StateHandler; 139 import com.android.launcher3.taskbar.LauncherTaskbarUIController; 140 import com.android.launcher3.taskbar.TaskbarManager; 141 import com.android.launcher3.testing.TestLogging; 142 import com.android.launcher3.testing.shared.TestProtocol; 143 import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory; 144 import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory; 145 import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; 146 import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController; 147 import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController; 148 import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; 149 import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController; 150 import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; 151 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController; 152 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController; 153 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController; 154 import com.android.launcher3.util.ActivityOptionsWrapper; 155 import com.android.launcher3.util.DisplayController; 156 import com.android.launcher3.util.IntSet; 157 import com.android.launcher3.util.NavigationMode; 158 import com.android.launcher3.util.ObjectWrapper; 159 import com.android.launcher3.util.PendingRequestArgs; 160 import com.android.launcher3.util.PendingSplitSelectInfo; 161 import com.android.launcher3.util.RunnableList; 162 import com.android.launcher3.util.SplitConfigurationOptions; 163 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; 164 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; 165 import com.android.launcher3.util.StartActivityParams; 166 import com.android.launcher3.util.TouchController; 167 import com.android.launcher3.widget.LauncherWidgetHolder; 168 import com.android.quickstep.OverviewCommandHelper; 169 import com.android.quickstep.OverviewComponentObserver; 170 import com.android.quickstep.RecentsAnimationDeviceState; 171 import com.android.quickstep.RecentsModel; 172 import com.android.quickstep.SystemUiProxy; 173 import com.android.quickstep.TaskUtils; 174 import com.android.quickstep.TouchInteractionService.TISBinder; 175 import com.android.quickstep.util.AsyncClockEventDelegate; 176 import com.android.quickstep.util.GroupTask; 177 import com.android.quickstep.util.LauncherUnfoldAnimationController; 178 import com.android.quickstep.util.QuickstepOnboardingPrefs; 179 import com.android.quickstep.util.SplitSelectStateController; 180 import com.android.quickstep.util.SplitToWorkspaceController; 181 import com.android.quickstep.util.SplitWithKeyboardShortcutController; 182 import com.android.quickstep.util.TISBindHelper; 183 import com.android.quickstep.util.unfold.LauncherUnfoldTransitionController; 184 import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider; 185 import com.android.quickstep.views.FloatingTaskView; 186 import com.android.quickstep.views.OverviewActionsView; 187 import com.android.quickstep.views.RecentsView; 188 import com.android.quickstep.views.RecentsViewContainer; 189 import com.android.quickstep.views.TaskView; 190 import com.android.systemui.shared.recents.model.Task; 191 import com.android.systemui.shared.system.ActivityManagerWrapper; 192 import com.android.systemui.unfold.RemoteUnfoldSharedComponent; 193 import com.android.systemui.unfold.UnfoldTransitionFactory; 194 import com.android.systemui.unfold.UnfoldTransitionProgressProvider; 195 import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig; 196 import com.android.systemui.unfold.config.UnfoldTransitionConfig; 197 import com.android.systemui.unfold.dagger.UnfoldMain; 198 import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver; 199 import com.android.systemui.unfold.updates.RotationChangeProvider; 200 201 import kotlin.Unit; 202 203 import java.io.FileDescriptor; 204 import java.io.PrintWriter; 205 import java.util.ArrayList; 206 import java.util.Arrays; 207 import java.util.Collections; 208 import java.util.List; 209 import java.util.Objects; 210 import java.util.Optional; 211 import java.util.function.BiConsumer; 212 import java.util.function.Predicate; 213 import java.util.stream.Stream; 214 215 public class QuickstepLauncher extends Launcher implements RecentsViewContainer { 216 private static final boolean TRACE_LAYOUTS = 217 SystemProperties.getBoolean("persist.debug.trace_layouts", false); 218 private static final String TRACE_RELAYOUT_CLASS = 219 SystemProperties.get("persist.debug.trace_request_layout_class", null); 220 public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; 221 222 protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t"; 223 224 private FixedContainerItems mAllAppsPredictions; 225 private HotseatPredictionController mHotseatPredictionController; 226 private DepthController mDepthController; 227 private @Nullable DesktopVisibilityController mDesktopVisibilityController; 228 private QuickstepTransitionManager mAppTransitionManager; 229 230 private OverviewActionsView<?> mActionsView; 231 private TISBindHelper mTISBindHelper; 232 private @Nullable LauncherTaskbarUIController mTaskbarUIController; 233 // Will be updated when dragging from taskbar. 234 private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; 235 private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController; 236 237 private SplitSelectStateController mSplitSelectStateController; 238 private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController; 239 private SplitToWorkspaceController mSplitToWorkspaceController; 240 241 /** 242 * If Launcher restarted while in the middle of an Overview split select, it needs this data to 243 * recover. In all other cases this will remain null. 244 */ 245 private PendingSplitSelectInfo mPendingSplitSelectInfo = null; 246 247 @Nullable 248 private DesktopRecentsTransitionController mDesktopRecentsTransitionController; 249 250 private SafeCloseable mViewCapture; 251 252 private boolean mEnableWidgetDepth; 253 254 private boolean mIsPredictiveBackToHomeInProgress; 255 getLauncher(Context context)256 public static QuickstepLauncher getLauncher(Context context) { 257 return fromContext(context); 258 } 259 260 @Override setupViews()261 protected void setupViews() { 262 super.setupViews(); 263 264 mActionsView = findViewById(R.id.overview_actions_view); 265 RecentsView<?,?> overviewPanel = getOverviewPanel(); 266 SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this); 267 mSplitSelectStateController = 268 new SplitSelectStateController(this, mHandler, getStateManager(), 269 getDepthController(), getStatsLogManager(), 270 systemUiProxy, RecentsModel.INSTANCE.get(this), 271 () -> onStateBack()); 272 RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(asContext()); 273 // TODO(b/337863494): Explore use of the same OverviewComponentObserver across launcher 274 OverviewComponentObserver overviewComponentObserver = new OverviewComponentObserver( 275 asContext(), deviceState); 276 if (enableDesktopWindowingMode()) { 277 mDesktopRecentsTransitionController = new DesktopRecentsTransitionController( 278 getStateManager(), systemUiProxy, getIApplicationThread(), 279 getDepthController()); 280 } 281 overviewPanel.init(mActionsView, mSplitSelectStateController, 282 mDesktopRecentsTransitionController); 283 mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this, 284 mSplitSelectStateController, overviewComponentObserver, deviceState); 285 mSplitToWorkspaceController = new SplitToWorkspaceController(this, 286 mSplitSelectStateController); 287 mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize()); 288 mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this)); 289 290 mAppTransitionManager = buildAppTransitionManager(); 291 mAppTransitionManager.registerRemoteAnimations(); 292 mAppTransitionManager.registerRemoteTransitions(); 293 294 mTISBindHelper = new TISBindHelper(this, this::onTISConnected); 295 mDepthController = new DepthController(this); 296 if (enableDesktopWindowingMode()) { 297 mDesktopVisibilityController = new DesktopVisibilityController(this); 298 mDesktopVisibilityController.registerSystemUiListener(); 299 mSplitSelectStateController.initSplitFromDesktopController(this, 300 overviewComponentObserver); 301 } 302 mHotseatPredictionController = new HotseatPredictionController(this); 303 304 mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true); 305 getWorkspace().addOverlayCallback(progress -> 306 onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX)); 307 addBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler()); 308 } 309 310 @Override logAppLaunch(StatsLogManager statsLogManager, ItemInfo info, InstanceId instanceId)311 public void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info, 312 InstanceId instanceId) { 313 // If the app launch is from any of the surfaces in AllApps then add the InstanceId from 314 // LiveSearchManager to recreate the AllApps session on the server side. 315 if (mAllAppsSessionLogId != null && ALL_APPS.equals( 316 getStateManager().getCurrentStableState())) { 317 instanceId = mAllAppsSessionLogId; 318 } 319 320 StatsLogger logger = statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId); 321 322 if (mAllAppsPredictions != null 323 && (info.itemType == ITEM_TYPE_APPLICATION 324 || info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) { 325 int count = mAllAppsPredictions.items.size(); 326 for (int i = 0; i < count; i++) { 327 ItemInfo targetInfo = mAllAppsPredictions.items.get(i); 328 if (targetInfo.itemType == info.itemType 329 && targetInfo.user.equals(info.user) 330 && Objects.equals(targetInfo.getIntent(), info.getIntent())) { 331 logger.withRank(i); 332 break; 333 } 334 335 } 336 } 337 logger.log(LAUNCHER_APP_LAUNCH_TAP); 338 339 mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId); 340 } 341 342 @Override completeAddShortcut(Intent data, int container, int screenId, int cellX, int cellY, PendingRequestArgs args)343 protected void completeAddShortcut(Intent data, int container, int screenId, int cellX, 344 int cellY, PendingRequestArgs args) { 345 if (container == CONTAINER_HOTSEAT) { 346 mHotseatPredictionController.onDeferredDrop(cellX, cellY); 347 } 348 super.completeAddShortcut(data, container, screenId, cellX, cellY, args); 349 } 350 351 @Override createAccessibilityDelegate()352 protected LauncherAccessibilityDelegate createAccessibilityDelegate() { 353 return new QuickstepAccessibilityDelegate(this); 354 } 355 356 /** 357 * Returns Prediction controller for hybrid hotseat 358 */ getHotseatPredictionController()359 public HotseatPredictionController getHotseatPredictionController() { 360 return mHotseatPredictionController; 361 } 362 363 @Override enableHotseatEdu(boolean enable)364 public void enableHotseatEdu(boolean enable) { 365 super.enableHotseatEdu(enable); 366 mHotseatPredictionController.enableHotseatEdu(enable); 367 } 368 369 /** 370 * Builds the {@link QuickstepTransitionManager} instance to use for managing transitions. 371 */ buildAppTransitionManager()372 protected QuickstepTransitionManager buildAppTransitionManager() { 373 return new QuickstepTransitionManager(this); 374 } 375 376 @Override onConfigurationChanged(Configuration newConfig)377 public void onConfigurationChanged(Configuration newConfig) { 378 super.onConfigurationChanged(newConfig); 379 onStateOrResumeChanging(false /* inTransition */); 380 } 381 382 @Override startActivitySafely(View v, Intent intent, ItemInfo item)383 public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) { 384 // Only pause is taskbar controller is not present until the transition (if it exists) ends 385 mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null); 386 PredictionRowView<?> predictionRowView = 387 getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class); 388 // Pause the prediction row updates until the transition (if it exists) ends. 389 predictionRowView.setPredictionUiUpdatePaused(true); 390 RunnableList result = super.startActivitySafely(v, intent, item); 391 if (result == null) { 392 mHotseatPredictionController.setPauseUIUpdate(false); 393 predictionRowView.setPredictionUiUpdatePaused(false); 394 } else { 395 result.add(() -> { 396 mHotseatPredictionController.setPauseUIUpdate(false); 397 predictionRowView.setPredictionUiUpdatePaused(false); 398 }); 399 } 400 return result; 401 } 402 403 @Override startBinding()404 public void startBinding() { 405 super.startBinding(); 406 mHotseatPredictionController.verifyUIUpdateNotPaused(); 407 } 408 409 @Override onActivityFlagsChanged(int changeBits)410 protected void onActivityFlagsChanged(int changeBits) { 411 if ((changeBits & ACTIVITY_STATE_STARTED) != 0) { 412 mDepthController.setActivityStarted(isStarted()); 413 } 414 415 if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) { 416 if (!FeatureFlags.enableHomeTransitionListener() && mTaskbarUIController != null) { 417 mTaskbarUIController.onLauncherVisibilityChanged(hasBeenResumed()); 418 } 419 } 420 421 super.onActivityFlagsChanged(changeBits); 422 if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED 423 | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) { 424 onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0); 425 } 426 } 427 428 @Override showAllAppsFromIntent(boolean alreadyOnHome)429 protected void showAllAppsFromIntent(boolean alreadyOnHome) { 430 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY); 431 super.showAllAppsFromIntent(alreadyOnHome); 432 } 433 onItemClicked(View view)434 protected void onItemClicked(View view) { 435 if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) { 436 QuickstepLauncher.super.getItemOnClickListener().onClick(view); 437 } 438 } 439 440 @Override getItemOnClickListener()441 public View.OnClickListener getItemOnClickListener() { 442 return this::onItemClicked; 443 } 444 445 @Override getSupportedShortcuts()446 public Stream<SystemShortcut.Factory> getSupportedShortcuts() { 447 // Order matters as it affects order of appearance in popup container 448 List<SystemShortcut.Factory> shortcuts = new ArrayList(Arrays.asList( 449 APP_INFO, WellbeingModel.SHORTCUT_FACTORY, mHotseatPredictionController)); 450 shortcuts.addAll(getSplitShortcuts()); 451 shortcuts.add(WIDGETS); 452 shortcuts.add(INSTALL); 453 if (Flags.enablePrivateSpaceInstallShortcut()) { 454 shortcuts.add(PRIVATE_PROFILE_INSTALL); 455 } 456 if (Flags.enableShortcutDontSuggestApp()) { 457 shortcuts.add(DONT_SUGGEST_APP); 458 } 459 if (Flags.enablePrivateSpace()) { 460 shortcuts.add(UNINSTALL_APP); 461 } 462 return shortcuts.stream(); 463 } 464 getSplitShortcuts()465 private List<SystemShortcut.Factory<QuickstepLauncher>> getSplitShortcuts() { 466 if (!mDeviceProfile.isTablet || mSplitSelectStateController.isSplitSelectActive()) { 467 return Collections.emptyList(); 468 } 469 RecentsView recentsView = getOverviewPanel(); 470 // TODO(b/266482558): Pull it out of PagedOrentationHandler for split from workspace. 471 List<SplitPositionOption> positions = 472 recentsView.getPagedOrientationHandler().getSplitPositionOptions( 473 mDeviceProfile); 474 List<SystemShortcut.Factory<QuickstepLauncher>> splitShortcuts = new ArrayList<>(); 475 for (SplitPositionOption position : positions) { 476 splitShortcuts.add(getSplitSelectShortcutByPosition(position)); 477 } 478 return splitShortcuts; 479 } 480 481 /** 482 * Recents logic that triggers when launcher state changes or launcher activity stops/resumes. 483 */ onStateOrResumeChanging(boolean inTransition)484 private void onStateOrResumeChanging(boolean inTransition) { 485 LauncherState state = getStateManager().getState(); 486 boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0; 487 if (started) { 488 DeviceProfile profile = getDeviceProfile(); 489 boolean willUserBeActive = 490 (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0; 491 boolean visible = (state == NORMAL || state == OVERVIEW) 492 && (willUserBeActive || isUserActive()) 493 && !profile.isVerticalBarLayout(); 494 SystemUiProxy.INSTANCE.get(this) 495 .setLauncherKeepClearAreaHeight(visible, profile.hotseatBarSizePx); 496 } 497 if (state == NORMAL && !inTransition) { 498 ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false); 499 } 500 } 501 502 @Override bindExtraContainerItems(FixedContainerItems item)503 public void bindExtraContainerItems(FixedContainerItems item) { 504 if (item.containerId == Favorites.CONTAINER_PREDICTION) { 505 mAllAppsPredictions = item; 506 PredictionRowView<?> predictionRowView = 507 getAppsView().getFloatingHeaderView().findFixedRowByType( 508 PredictionRowView.class); 509 predictionRowView.setPredictedApps(item.items); 510 } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) { 511 mHotseatPredictionController.setPredictedItems(item); 512 } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) { 513 getPopupDataProvider().setRecommendedWidgets(item.items); 514 } 515 } 516 517 @Override bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher)518 public void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { 519 super.bindWorkspaceComponentsRemoved(matcher); 520 mHotseatPredictionController.onModelItemsRemoved(matcher); 521 } 522 523 @Override onDestroy()524 public void onDestroy() { 525 if (mAppTransitionManager != null) { 526 mAppTransitionManager.onActivityDestroyed(); 527 } 528 mAppTransitionManager = null; 529 mIsPredictiveBackToHomeInProgress = false; 530 531 if (mUnfoldTransitionProgressProvider != null) { 532 SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); 533 mUnfoldTransitionProgressProvider.destroy(); 534 } 535 536 mTISBindHelper.onDestroy(); 537 538 if (mLauncherUnfoldAnimationController != null) { 539 mLauncherUnfoldAnimationController.onDestroy(); 540 } 541 542 if (mDesktopVisibilityController != null) { 543 mDesktopVisibilityController.unregisterSystemUiListener(); 544 } 545 546 if (mSplitSelectStateController != null) { 547 mSplitSelectStateController.onDestroy(); 548 } 549 550 super.onDestroy(); 551 mHotseatPredictionController.destroy(); 552 mSplitWithKeyboardShortcutController.onDestroy(); 553 if (mViewCapture != null) mViewCapture.close(); 554 removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler()); 555 } 556 557 @Override onStateSetEnd(LauncherState state)558 public void onStateSetEnd(LauncherState state) { 559 super.onStateSetEnd(state); 560 handlePendingActivityRequest(); 561 562 switch (state.ordinal) { 563 case HINT_STATE_ORDINAL: { 564 Workspace<?> workspace = getWorkspace(); 565 getStateManager().goToState(NORMAL); 566 if (workspace.getNextPage() != Workspace.DEFAULT_PAGE) { 567 workspace.post(workspace::moveToDefaultScreen); 568 } 569 break; 570 } 571 case HINT_STATE_TWO_BUTTON_ORDINAL: { 572 getStateManager().goToState(OVERVIEW); 573 getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 574 break; 575 } 576 case OVERVIEW_STATE_ORDINAL: { 577 RecentsView rv = getOverviewPanel(); 578 sendCustomAccessibilityEvent( 579 rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null); 580 break; 581 } 582 case QUICK_SWITCH_STATE_ORDINAL: { 583 RecentsView rv = getOverviewPanel(); 584 TaskView tasktolaunch = rv.getCurrentPageTaskView(); 585 if (tasktolaunch != null) { 586 tasktolaunch.launchTask(success -> { 587 if (!success) { 588 getStateManager().goToState(OVERVIEW); 589 } else { 590 getStateManager().moveToRestState(); 591 } 592 return Unit.INSTANCE; 593 }); 594 } else { 595 getStateManager().goToState(NORMAL); 596 } 597 break; 598 } 599 600 } 601 } 602 603 @Override createTouchControllers()604 public TouchController[] createTouchControllers() { 605 NavigationMode mode = DisplayController.getNavigationMode(this); 606 607 ArrayList<TouchController> list = new ArrayList<>(); 608 list.add(getDragController()); 609 BiConsumer<AnimatorSet, Long> splitAnimator = (animatorSet, duration) -> 610 animatorSet.play(mSplitSelectStateController.getSplitAnimationController() 611 .createPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_HOME, 612 duration)); 613 switch (mode) { 614 case NO_BUTTON: 615 list.add(new NoButtonQuickSwitchTouchController(this)); 616 list.add(new NavBarToHomeTouchController(this, splitAnimator)); 617 list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator)); 618 break; 619 case TWO_BUTTONS: 620 list.add(new TwoButtonNavbarTouchController(this)); 621 list.add(getDeviceProfile().isVerticalBarLayout() 622 ? new TransposedQuickSwitchTouchController(this) 623 : new QuickSwitchTouchController(this)); 624 list.add(new PortraitStatesTouchController(this)); 625 break; 626 case THREE_BUTTONS: 627 list.add(new NoButtonQuickSwitchTouchController(this)); 628 list.add(new NavBarToHomeTouchController(this, splitAnimator)); 629 list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator)); 630 list.add(new PortraitStatesTouchController(this)); 631 break; 632 default: 633 list.add(new PortraitStatesTouchController(this)); 634 break; 635 } 636 637 if (!getDeviceProfile().isMultiWindowMode) { 638 list.add(new StatusBarTouchController(this)); 639 } 640 641 list.add(new LauncherTaskViewController(this)); 642 return list.toArray(new TouchController[list.size()]); 643 } 644 645 @Override createAtomicAnimationFactory()646 public AtomicAnimationFactory createAtomicAnimationFactory() { 647 return new QuickstepAtomicAnimationFactory(this); 648 } 649 650 @Override createAppWidgetHolder()651 protected LauncherWidgetHolder createAppWidgetHolder() { 652 final QuickstepHolderFactory factory = 653 (QuickstepHolderFactory) LauncherWidgetHolder.HolderFactory.newFactory(this); 654 return factory.newInstance(this, 655 appWidgetId -> getWorkspace().removeWidget(appWidgetId), 656 new QuickstepInteractionHandler(this)); 657 } 658 659 @Override onCreate(Bundle savedInstanceState)660 protected void onCreate(Bundle savedInstanceState) { 661 // Back dispatcher is registered in {@link BaseActivity#onCreate}. For predictive back to 662 // work, we must opt-in BEFORE registering back dispatcher. So we need to call 663 // setEnableOnBackInvokedCallback() before super.onCreate() 664 if (Utilities.ATLEAST_U && enablePredictiveBackGesture()) { 665 getApplicationInfo().setEnableOnBackInvokedCallback(true); 666 } 667 super.onCreate(savedInstanceState); 668 if (savedInstanceState != null) { 669 mPendingSplitSelectInfo = ObjectWrapper.unwrap( 670 savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO)); 671 } 672 addMultiWindowModeChangedListener(mDepthController); 673 initUnfoldTransitionProgressProvider(); 674 if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) { 675 mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow()); 676 } 677 getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE); 678 QuickstepOnboardingPrefs.setup(this); 679 View.setTraceLayoutSteps(TRACE_LAYOUTS); 680 View.setTracedRequestLayoutClassClass(TRACE_RELAYOUT_CLASS); 681 } 682 683 @Override initDeviceProfile(InvariantDeviceProfile idp)684 protected boolean initDeviceProfile(InvariantDeviceProfile idp) { 685 final boolean ret = super.initDeviceProfile(idp); 686 mDeviceProfile.isPredictiveBackSwipe = 687 getApplicationInfo().isOnBackInvokedCallbackEnabled(); 688 return ret; 689 } 690 691 @Override startSplitSelection(SplitSelectSource splitSelectSource)692 public void startSplitSelection(SplitSelectSource splitSelectSource) { 693 RecentsView recentsView = getOverviewPanel(); 694 // Check if there is already an instance of this app running, if so, initiate the split 695 // using that. 696 mSplitSelectStateController.findLastActiveTasksAndRunCallback( 697 Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()), 698 false /* findExactPairMatch */, 699 foundTasks -> { 700 @Nullable Task foundTask = foundTasks[0]; 701 boolean taskWasFound = foundTask != null; 702 splitSelectSource.alreadyRunningTaskId = taskWasFound 703 ? foundTask.key.id 704 : INVALID_TASK_ID; 705 if (enableSplitContextually()) { 706 startSplitToHome(splitSelectSource); 707 } else { 708 recentsView.initiateSplitSelect(splitSelectSource); 709 } 710 } 711 ); 712 } 713 714 /** TODO(b/266482558) Migrate into SplitSelectStateController or someplace split specific. */ startSplitToHome(SplitSelectSource source)715 private void startSplitToHome(SplitSelectSource source) { 716 AbstractFloatingView.closeAllOpenViews(this); 717 int splitPlaceholderSize = getResources().getDimensionPixelSize( 718 R.dimen.split_placeholder_size); 719 int splitPlaceholderInset = getResources().getDimensionPixelSize( 720 R.dimen.split_placeholder_inset); 721 Rect tempRect = new Rect(); 722 723 mSplitSelectStateController.setInitialTaskSelect(source.intent, 724 source.position.stagePosition, source.itemInfo, source.splitEvent, 725 source.alreadyRunningTaskId); 726 727 RecentsView recentsView = getOverviewPanel(); 728 recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds( 729 splitPlaceholderSize, splitPlaceholderInset, getDeviceProfile(), 730 mSplitSelectStateController.getActiveSplitStagePosition(), tempRect); 731 732 PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration()); 733 RectF startingTaskRect = new RectF(); 734 final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(this, 735 source.getView(), null /* thumbnail */, source.getDrawable(), startingTaskRect); 736 floatingTaskView.setAlpha(1); 737 floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect, 738 false /* fadeWithThumbnail */, true /* isStagedTask */); 739 floatingTaskView.setOnClickListener(view -> 740 mSplitSelectStateController.getSplitAnimationController(). 741 playAnimPlaceholderToFullscreen(this, view, Optional.empty())); 742 mSplitSelectStateController.setFirstFloatingTaskView(floatingTaskView); 743 anim.addListener(new AnimatorListenerAdapter() { 744 @Override 745 public void onAnimationCancel(Animator animation) { 746 getDragLayer().removeView(floatingTaskView); 747 mSplitSelectStateController.getSplitAnimationController() 748 .removeSplitInstructionsView(QuickstepLauncher.this); 749 mSplitSelectStateController.resetState(); 750 } 751 }); 752 anim.add(mSplitSelectStateController.getSplitAnimationController() 753 .getShowSplitInstructionsAnim(this).buildAnim()); 754 anim.buildAnim().start(); 755 } 756 757 @Override isSplitSelectionActive()758 public boolean isSplitSelectionActive() { 759 if (mSplitSelectStateController == null) { 760 return false; 761 } 762 return mSplitSelectStateController.isSplitSelectActive(); 763 } 764 areBothSplitAppsConfirmed()765 public boolean areBothSplitAppsConfirmed() { 766 return mSplitSelectStateController.isBothSplitAppsConfirmed(); 767 } 768 769 @Override onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState)770 public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) { 771 if (mTaskbarUIController != null) { 772 mTaskbarUIController.onStateTransitionCompletedAfterSwipeToHome(finalState); 773 } 774 } 775 776 @Override onResume()777 protected void onResume() { 778 super.onResume(); 779 780 if (mLauncherUnfoldAnimationController != null) { 781 mLauncherUnfoldAnimationController.onResume(); 782 } 783 } 784 785 @Override onPause()786 protected void onPause() { 787 if (mLauncherUnfoldAnimationController != null) { 788 mLauncherUnfoldAnimationController.onPause(); 789 } 790 791 super.onPause(); 792 793 if (enableSplitContextually()) { 794 // If Launcher pauses before both split apps are selected, exit split screen. 795 if (!mSplitSelectStateController.isBothSplitAppsConfirmed() && 796 !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) { 797 mSplitSelectStateController 798 .logExitReason(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED); 799 mSplitSelectStateController.getSplitAnimationController() 800 .playPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED); 801 } 802 } 803 } 804 805 @Override onNewIntent(Intent intent)806 protected void onNewIntent(Intent intent) { 807 super.onNewIntent(intent); 808 OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper(); 809 if (overviewCommandHelper != null) { 810 overviewCommandHelper.clearPendingCommands(); 811 } 812 } 813 getAppTransitionManager()814 public QuickstepTransitionManager getAppTransitionManager() { 815 return mAppTransitionManager; 816 } 817 818 @Override onEnterAnimationComplete()819 public void onEnterAnimationComplete() { 820 super.onEnterAnimationComplete(); 821 // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled 822 // as a part of quickstep, so that high-res thumbnails can load the next time we enter 823 // overview 824 RecentsModel.INSTANCE.get(this).getThumbnailCache() 825 .getHighResLoadingState().setVisible(true); 826 } 827 828 @Override handleGestureContract(Intent intent)829 protected void handleGestureContract(Intent intent) { 830 if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) { 831 super.handleGestureContract(intent); 832 } 833 } 834 835 @Override onTrimMemory(int level)836 public void onTrimMemory(int level) { 837 super.onTrimMemory(level); 838 RecentsModel.INSTANCE.get(this).onTrimMemory(level); 839 } 840 841 @Override onUiChangedWhileSleeping()842 public void onUiChangedWhileSleeping() { 843 // Remove the snapshot because the content view may have obvious changes. 844 UI_HELPER_EXECUTOR.execute( 845 () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this)); 846 } 847 848 @Override onAllAppsTransition(float progress)849 public void onAllAppsTransition(float progress) { 850 super.onAllAppsTransition(progress); 851 onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX); 852 } 853 854 @Override onWidgetsTransition(float progress)855 public void onWidgetsTransition(float progress) { 856 super.onWidgetsTransition(progress); 857 onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX); 858 if (mEnableWidgetDepth) { 859 getDepthController().widgetDepth.setValue(Utilities.mapToRange( 860 progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth, EMPHASIZED)); 861 } 862 } 863 864 @Override dispatchKeyEvent(KeyEvent event)865 public boolean dispatchKeyEvent(KeyEvent event) { 866 return tryHandleBackKey(event) || super.dispatchKeyEvent(event); 867 } 868 869 // TODO (b/267248420) Once the recents input consumer has been removed, there is no need to 870 // handle the back key specially. tryHandleBackKey(KeyEvent event)871 private boolean tryHandleBackKey(KeyEvent event) { 872 // Unlike normal activity, recents can receive input event from InputConsumer, so the input 873 // event won't go through ViewRootImpl#InputStage#onProcess. 874 // So when receive back key, try to do the same check thing in 875 // ViewRootImpl#NativePreImeInputStage#onProcess 876 if (!Utilities.ATLEAST_U || !enablePredictiveBackGesture() 877 || event.getKeyCode() != KeyEvent.KEYCODE_BACK 878 || event.getAction() != KeyEvent.ACTION_UP || event.isCanceled()) { 879 return false; 880 } 881 882 getOnBackAnimationCallback().onBackInvoked(); 883 return true; 884 } 885 886 @Override registerBackDispatcher()887 protected void registerBackDispatcher() { 888 if (!enablePredictiveBackGesture()) { 889 super.registerBackDispatcher(); 890 return; 891 } 892 getOnBackInvokedDispatcher().registerOnBackInvokedCallback( 893 OnBackInvokedDispatcher.PRIORITY_DEFAULT, 894 new OnBackAnimationCallback() { 895 896 @Nullable OnBackAnimationCallback mActiveOnBackAnimationCallback; 897 898 @Override 899 public void onBackStarted(@NonNull BackEvent backEvent) { 900 if (mActiveOnBackAnimationCallback != null) { 901 mActiveOnBackAnimationCallback.onBackCancelled(); 902 } 903 mActiveOnBackAnimationCallback = getOnBackAnimationCallback(); 904 mActiveOnBackAnimationCallback.onBackStarted(backEvent); 905 } 906 907 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 908 @Override 909 public void onBackInvoked() { 910 // Recreate mActiveOnBackAnimationCallback if necessary to avoid NPE 911 // because: 912 // 1. b/260636433: In 3-button-navigation mode, onBackStarted() is not 913 // called on ACTION_DOWN before onBackInvoked() is called in ACTION_UP. 914 // 2. Launcher#onBackPressed() will call onBackInvoked() without calling 915 // onBackInvoked() beforehand. 916 if (mActiveOnBackAnimationCallback == null) { 917 mActiveOnBackAnimationCallback = getOnBackAnimationCallback(); 918 } 919 mActiveOnBackAnimationCallback.onBackInvoked(); 920 mActiveOnBackAnimationCallback = null; 921 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); 922 } 923 924 @Override 925 public void onBackProgressed(@NonNull BackEvent backEvent) { 926 if (!FeatureFlags.IS_STUDIO_BUILD 927 && mActiveOnBackAnimationCallback == null) { 928 return; 929 } 930 mActiveOnBackAnimationCallback.onBackProgressed(backEvent); 931 } 932 933 @Override 934 public void onBackCancelled() { 935 if (!FeatureFlags.IS_STUDIO_BUILD 936 && mActiveOnBackAnimationCallback == null) { 937 return; 938 } 939 mActiveOnBackAnimationCallback.onBackCancelled(); 940 mActiveOnBackAnimationCallback = null; 941 } 942 }); 943 } 944 onTaskbarInAppDisplayProgressUpdate(float progress, int flag)945 private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) { 946 TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); 947 if (taskbarManager == null 948 || taskbarManager.getCurrentActivityContext() == null 949 || mTaskbarUIController == null) { 950 return; 951 } 952 mTaskbarUIController.onTaskbarInAppDisplayProgressUpdate(progress, flag); 953 } 954 955 @Override startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)956 public void startIntentSenderForResult(IntentSender intent, int requestCode, 957 Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { 958 if (requestCode != -1) { 959 mPendingActivityRequestCode = requestCode; 960 StartActivityParams params = new StartActivityParams(this, requestCode); 961 params.intentSender = intent; 962 params.fillInIntent = fillInIntent; 963 params.flagsMask = flagsMask; 964 params.flagsValues = flagsValues; 965 params.extraFlags = extraFlags; 966 params.options = options; 967 startActivity(ProxyActivityStarter.getLaunchIntent(this, params)); 968 } else { 969 super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, 970 flagsValues, extraFlags, options); 971 } 972 } 973 974 @Override startActivityForResult(Intent intent, int requestCode, Bundle options)975 public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 976 if (requestCode != -1) { 977 mPendingActivityRequestCode = requestCode; 978 StartActivityParams params = new StartActivityParams(this, requestCode); 979 params.intent = intent; 980 params.options = options; 981 startActivity(ProxyActivityStarter.getLaunchIntent(this, params)); 982 } else { 983 super.startActivityForResult(intent, requestCode, options); 984 } 985 } 986 987 @Override setResumed()988 public void setResumed() { 989 if (!enableDesktopWindowingWallpaperActivity() 990 && mDesktopVisibilityController != null 991 && mDesktopVisibilityController.areDesktopTasksVisible() 992 && !mDesktopVisibilityController.isRecentsGestureInProgress()) { 993 // Return early to skip setting activity to appear as resumed 994 // TODO: b/333533253 - Remove after flag rollout 995 return; 996 } 997 super.setResumed(); 998 } 999 1000 @Override onDeferredResumed()1001 protected void onDeferredResumed() { 1002 super.onDeferredResumed(); 1003 handlePendingActivityRequest(); 1004 } 1005 handlePendingActivityRequest()1006 private void handlePendingActivityRequest() { 1007 if (mPendingActivityRequestCode != -1 && isInState(NORMAL) 1008 && ((getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) { 1009 // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher. 1010 onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null); 1011 // ProxyActivityStarter is started with clear task to reset the task after which it 1012 // removes the task itself. 1013 startActivity(ProxyActivityStarter.getLaunchIntent(this, null)); 1014 } 1015 } 1016 onTISConnected(TISBinder binder)1017 private void onTISConnected(TISBinder binder) { 1018 TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); 1019 if (taskbarManager != null) { 1020 taskbarManager.setActivity(this); 1021 } 1022 mTISBindHelper.setPredictiveBackToHomeInProgress(mIsPredictiveBackToHomeInProgress); 1023 } 1024 1025 @Override runOnBindToTouchInteractionService(Runnable r)1026 public void runOnBindToTouchInteractionService(Runnable r) { 1027 mTISBindHelper.runOnBindToTouchInteractionService(r); 1028 } 1029 initUnfoldTransitionProgressProvider()1030 private void initUnfoldTransitionProgressProvider() { 1031 if (!enableUnfoldStateAnimation()) { 1032 final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig(); 1033 if (config.isEnabled()) { 1034 initRemotelyCalculatedUnfoldAnimation(config); 1035 } 1036 } else { 1037 ProxyUnfoldTransitionProvider provider = 1038 SystemUiProxy.INSTANCE.get(this).getUnfoldTransitionProvider(); 1039 if (provider != null) { 1040 new LauncherUnfoldTransitionController(this, provider); 1041 } 1042 } 1043 } 1044 1045 /** Receives animation progress from sysui process. */ initRemotelyCalculatedUnfoldAnimation(UnfoldTransitionConfig config)1046 private void initRemotelyCalculatedUnfoldAnimation(UnfoldTransitionConfig config) { 1047 RemoteUnfoldSharedComponent unfoldComponent = 1048 UnfoldTransitionFactory.createRemoteUnfoldSharedComponent( 1049 /* context= */ this, 1050 config, 1051 getMainExecutor(), 1052 getMainThreadHandler(), 1053 /* backgroundExecutor= */ UI_HELPER_EXECUTOR, 1054 /* bgHandler= */ UI_HELPER_EXECUTOR.getHandler(), 1055 /* tracingTagPrefix= */ "launcher", 1056 getSystemService(DisplayManager.class) 1057 ); 1058 1059 final RemoteUnfoldTransitionReceiver remoteUnfoldTransitionProgressProvider = 1060 unfoldComponent.getRemoteTransitionProgress().orElseThrow( 1061 () -> new IllegalStateException( 1062 "Trying to create getRemoteTransitionProgress when the transition " 1063 + "is disabled")); 1064 mUnfoldTransitionProgressProvider = remoteUnfoldTransitionProgressProvider; 1065 1066 SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener( 1067 remoteUnfoldTransitionProgressProvider); 1068 1069 initUnfoldAnimationController(mUnfoldTransitionProgressProvider, 1070 unfoldComponent.getRotationChangeProvider()); 1071 } 1072 initUnfoldAnimationController(UnfoldTransitionProgressProvider progressProvider, @UnfoldMain RotationChangeProvider rotationChangeProvider)1073 private void initUnfoldAnimationController(UnfoldTransitionProgressProvider progressProvider, 1074 @UnfoldMain RotationChangeProvider rotationChangeProvider) { 1075 mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController( 1076 /* launcher= */ this, 1077 getWindowManager(), 1078 progressProvider, 1079 rotationChangeProvider 1080 ); 1081 } 1082 setTaskbarUIController(LauncherTaskbarUIController taskbarUIController)1083 public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) { 1084 mTaskbarUIController = taskbarUIController; 1085 } 1086 getTaskbarUIController()1087 public @Nullable LauncherTaskbarUIController getTaskbarUIController() { 1088 return mTaskbarUIController; 1089 } 1090 getSplitToWorkspaceController()1091 public SplitToWorkspaceController getSplitToWorkspaceController() { 1092 return mSplitToWorkspaceController; 1093 } 1094 1095 @Override handleSplitAnimationGoingToHome(StatsLogManager.EventEnum splitDismissReason)1096 protected void handleSplitAnimationGoingToHome(StatsLogManager.EventEnum splitDismissReason) { 1097 super.handleSplitAnimationGoingToHome(splitDismissReason); 1098 mSplitSelectStateController.getSplitAnimationController() 1099 .playPlaceholderDismissAnim(this, splitDismissReason); 1100 } 1101 1102 @Override dismissSplitSelection(StatsLogManager.LauncherEvent splitDismissEvent)1103 public void dismissSplitSelection(StatsLogManager.LauncherEvent splitDismissEvent) { 1104 super.dismissSplitSelection(splitDismissEvent); 1105 mSplitSelectStateController.getSplitAnimationController() 1106 .playPlaceholderDismissAnim(this, splitDismissEvent); 1107 } 1108 1109 @Override getActionsView()1110 public OverviewActionsView<?> getActionsView() { 1111 return mActionsView; 1112 } 1113 1114 @Override closeOpenViews(boolean animate)1115 protected void closeOpenViews(boolean animate) { 1116 super.closeOpenViews(animate); 1117 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY); 1118 } 1119 1120 @Override collectStateHandlers(List<StateHandler<LauncherState>> out)1121 public void collectStateHandlers(List<StateHandler<LauncherState>> out) { 1122 super.collectStateHandlers(out); 1123 out.add(getDepthController()); 1124 out.add(new RecentsViewStateController(this)); 1125 } 1126 getDepthController()1127 public DepthController getDepthController() { 1128 return mDepthController; 1129 } 1130 1131 @Nullable getDesktopVisibilityController()1132 public DesktopVisibilityController getDesktopVisibilityController() { 1133 return mDesktopVisibilityController; 1134 } 1135 1136 @Nullable getUnfoldTransitionProgressProvider()1137 public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() { 1138 return mUnfoldTransitionProgressProvider; 1139 } 1140 1141 @Override supportsAdaptiveIconAnimation(View clickedView)1142 public boolean supportsAdaptiveIconAnimation(View clickedView) { 1143 return true; 1144 } 1145 1146 @Override getNormalOverviewScaleAndOffset()1147 public float[] getNormalOverviewScaleAndOffset() { 1148 return DisplayController.getNavigationMode(this).hasGestures 1149 ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET}; 1150 } 1151 1152 @Override finishBindingItems(IntSet pagesBoundFirst)1153 public void finishBindingItems(IntSet pagesBoundFirst) { 1154 super.finishBindingItems(pagesBoundFirst); 1155 // Instantiate and initialize WellbeingModel now that its loading won't interfere with 1156 // populating workspace. 1157 // TODO: Find a better place for this 1158 WellbeingModel.INSTANCE.get(this); 1159 1160 if (mLauncherUnfoldAnimationController != null) { 1161 // This is needed in case items are rebound while the unfold animation is in progress. 1162 mLauncherUnfoldAnimationController.updateRegisteredViewsIfNeeded(); 1163 } 1164 } 1165 1166 @Override getActivityLaunchOptions(View v, @Nullable ItemInfo item)1167 public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) { 1168 ActivityOptionsWrapper activityOptions = mAppTransitionManager.getActivityLaunchOptions(v); 1169 if (mLastTouchUpTime > 0) { 1170 activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, 1171 mLastTouchUpTime); 1172 } 1173 if (item != null && (item.animationType == DEFAULT_NO_ICON 1174 || item.animationType == VIEW_BACKGROUND)) { 1175 activityOptions.options.setSplashScreenStyle( 1176 SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR); 1177 } else { 1178 activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON); 1179 } 1180 activityOptions.options.setLaunchDisplayId( 1181 (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId() 1182 : Display.DEFAULT_DISPLAY); 1183 activityOptions.options.setPendingIntentBackgroundActivityStartMode( 1184 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); 1185 addLaunchCookie(item, activityOptions.options); 1186 return activityOptions; 1187 } 1188 1189 @Override makeDefaultActivityOptions(int splashScreenStyle)1190 public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) { 1191 RunnableList callbacks = new RunnableList(); 1192 ActivityOptions options = ActivityOptions.makeCustomAnimation(this, 0, 0); 1193 options.setSplashScreenStyle(splashScreenStyle); 1194 options.setPendingIntentBackgroundActivityStartMode( 1195 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); 1196 1197 IRemoteCallback endCallback = completeRunnableListCallback(callbacks); 1198 options.setOnAnimationAbortListener(endCallback); 1199 options.setOnAnimationFinishedListener(endCallback); 1200 return new ActivityOptionsWrapper(options, callbacks); 1201 } 1202 1203 @Override 1204 @BinderThread enterStageSplitFromRunningApp(boolean leftOrTop)1205 public void enterStageSplitFromRunningApp(boolean leftOrTop) { 1206 mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop); 1207 } 1208 1209 /** 1210 * Adds a new launch cookie for the activity launch if supported. 1211 * 1212 * @param info the item info for the launch 1213 * @param opts the options to set the launchCookie on. 1214 */ addLaunchCookie(ItemInfo info, ActivityOptions opts)1215 public void addLaunchCookie(ItemInfo info, ActivityOptions opts) { 1216 IBinder launchCookie = getLaunchCookie(info); 1217 if (launchCookie != null) { 1218 opts.setLaunchCookie(launchCookie); 1219 } 1220 } 1221 1222 /** 1223 * Return a new launch cookie for the activity launch if supported. 1224 * 1225 * @param info the item info for the launch 1226 */ getLaunchCookie(ItemInfo info)1227 public IBinder getLaunchCookie(ItemInfo info) { 1228 if (info == null) { 1229 return null; 1230 } 1231 switch (info.container) { 1232 case Favorites.CONTAINER_DESKTOP: 1233 case Favorites.CONTAINER_HOTSEAT: 1234 case Favorites.CONTAINER_PRIVATESPACE: 1235 // Fall through and continue it's on the workspace (we don't support swiping back 1236 // to other containers like all apps or the hotseat predictions (which can change) 1237 break; 1238 default: 1239 if (info.container >= 0) { 1240 // Also allow swiping to folders 1241 break; 1242 } 1243 // Reset any existing launch cookies associated with the cookie 1244 return ObjectWrapper.wrap(NO_MATCHING_ID); 1245 } 1246 switch (info.itemType) { 1247 case Favorites.ITEM_TYPE_APPLICATION: 1248 case Favorites.ITEM_TYPE_DEEP_SHORTCUT: 1249 case Favorites.ITEM_TYPE_APPWIDGET: 1250 // Fall through and continue if it's an app, shortcut, or widget 1251 break; 1252 default: 1253 // Reset any existing launch cookies associated with the cookie 1254 return ObjectWrapper.wrap(NO_MATCHING_ID); 1255 } 1256 return ObjectWrapper.wrap(new Integer(info.id)); 1257 } 1258 setHintUserWillBeActive()1259 public void setHintUserWillBeActive() { 1260 addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); 1261 } 1262 1263 @Override onDisplayInfoChanged(Context context, DisplayController.Info info, int flags)1264 public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) { 1265 super.onDisplayInfoChanged(context, info, flags); 1266 // When changing screens, force moving to rest state similar to StatefulActivity.onStop, as 1267 // StatefulActivity isn't called consistently. 1268 if ((flags & CHANGE_ACTIVE_SCREEN) != 0) { 1269 // Do not animate moving to rest state, as it can clash with Launcher#onIdpChanged 1270 // where reapplyUi calls StateManager's reapplyState during the state change animation, 1271 // and cancel the state change unexpectedly. The screen will be off during screen 1272 // transition, hiding the unanimated transition. 1273 getStateManager().moveToRestState(/* isAnimated = */false); 1274 } 1275 1276 if ((flags & CHANGE_NAVIGATION_MODE) != 0) { 1277 getDragLayer().recreateControllers(); 1278 if (mActionsView != null) { 1279 mActionsView.updateVerticalMargin(info.getNavigationMode()); 1280 } 1281 } 1282 } 1283 1284 @Override onSaveInstanceState(Bundle outState)1285 protected void onSaveInstanceState(Bundle outState) { 1286 super.onSaveInstanceState(outState); 1287 1288 // If Launcher shuts downs during split select, we save some extra data in the recovery 1289 // bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't 1290 // work in this case because restoring straight to OverviewSplitSelect without staging data, 1291 // or before the tasks themselves have loaded into Overview, causes a crash. So we tell 1292 // Launcher to first restore into Overview state, wait for the relevant tasks and icons to 1293 // load in, and then proceed to OverviewSplitSelect. 1294 if (isInState(OVERVIEW_SPLIT_SELECT)) { 1295 // Launcher will restart in Overview and then transition to OverviewSplitSelect. 1296 outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap( 1297 new PendingSplitSelectInfo( 1298 mSplitSelectStateController.getInitialTaskId(), 1299 mSplitSelectStateController.getActiveSplitStagePosition(), 1300 mSplitSelectStateController.getSplitEvent()) 1301 )); 1302 outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal); 1303 } 1304 } 1305 1306 /** 1307 * When Launcher restarts, it sometimes needs to recover to a split selection state. 1308 * This function checks if such a recovery is needed. 1309 * @return a boolean representing whether the launcher is waiting to recover to 1310 * OverviewSplitSelect state. 1311 */ hasPendingSplitSelectInfo()1312 public boolean hasPendingSplitSelectInfo() { 1313 return mPendingSplitSelectInfo != null; 1314 } 1315 1316 /** 1317 * See {@link #hasPendingSplitSelectInfo()} 1318 */ getPendingSplitSelectInfo()1319 public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() { 1320 return mPendingSplitSelectInfo; 1321 } 1322 1323 /** 1324 * When the launcher has successfully recovered to OverviewSplitSelect state, this function 1325 * deletes the recovery data, returning it to a null state. 1326 */ finishSplitSelectRecovery()1327 public void finishSplitSelectRecovery() { 1328 mPendingSplitSelectInfo = null; 1329 } 1330 1331 /** 1332 * Sets flag whether a predictive back-to-home animation is in progress 1333 */ setPredictiveBackToHomeInProgress(boolean isInProgress)1334 public void setPredictiveBackToHomeInProgress(boolean isInProgress) { 1335 mIsPredictiveBackToHomeInProgress = isInProgress; 1336 mTISBindHelper.setPredictiveBackToHomeInProgress(isInProgress); 1337 } 1338 1339 @Override areDesktopTasksVisible()1340 public boolean areDesktopTasksVisible() { 1341 if (mDesktopVisibilityController != null) { 1342 return mDesktopVisibilityController.areDesktopTasksVisible(); 1343 } 1344 return false; 1345 } 1346 1347 @Override onDeviceProfileInitiated()1348 protected void onDeviceProfileInitiated() { 1349 super.onDeviceProfileInitiated(); 1350 SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx); 1351 } 1352 1353 @Override dispatchDeviceProfileChanged()1354 public void dispatchDeviceProfileChanged() { 1355 super.dispatchDeviceProfileChanged(); 1356 Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged", 1357 getDeviceProfile().toSmallString()); 1358 SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx); 1359 TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager(); 1360 if (taskbarManager != null) { 1361 taskbarManager.debugWhyTaskbarNotDestroyed("QuickstepLauncher#onDeviceProfileChanged"); 1362 } 1363 } 1364 1365 /** 1366 * Launches the given {@link GroupTask} in splitscreen. 1367 */ launchSplitTasks( @onNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition)1368 public void launchSplitTasks( 1369 @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) { 1370 // Top/left and bottom/right tasks respectively. 1371 Task task1 = groupTask.task1; 1372 // task2 should never be null when calling this method. Allow a crash to catch invalid calls 1373 Task task2 = groupTask.task2; 1374 mSplitSelectStateController.launchExistingSplitPair( 1375 null /* launchingTaskView */, 1376 task1.key.id, 1377 task2.key.id, 1378 SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, 1379 /* callback= */ success -> mSplitSelectStateController.resetState(), 1380 /* freezeTaskList= */ false, 1381 groupTask.mSplitBounds == null 1382 ? SNAP_TO_50_50 1383 : groupTask.mSplitBounds.snapPosition, 1384 remoteTransition); 1385 } 1386 1387 /** 1388 * Launches two apps as an app pair. 1389 */ launchAppPair(AppPairIcon appPairIcon)1390 public void launchAppPair(AppPairIcon appPairIcon) { 1391 mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon, 1392 CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE); 1393 } 1394 canStartHomeSafely()1395 public boolean canStartHomeSafely() { 1396 OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper(); 1397 return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely(); 1398 } 1399 1400 @Override isBubbleBarEnabled()1401 public boolean isBubbleBarEnabled() { 1402 return (mTaskbarUIController != null && mTaskbarUIController.isBubbleBarEnabled()); 1403 } 1404 1405 @Override hasBubbles()1406 public boolean hasBubbles() { 1407 return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles()); 1408 } 1409 1410 @NonNull getTISBindHelper()1411 public TISBindHelper getTISBindHelper() { 1412 return mTISBindHelper; 1413 } 1414 1415 @Override handleIncorrectSplitTargetSelection()1416 public boolean handleIncorrectSplitTargetSelection() { 1417 if (!enableSplitContextually() || !mSplitSelectStateController.isSplitSelectActive()) { 1418 return false; 1419 } 1420 mSplitSelectStateController.getSplitInstructionsView().goBoing(); 1421 return true; 1422 } 1423 1424 private static final class LauncherTaskViewController extends 1425 TaskViewTouchController<QuickstepLauncher> { 1426 LauncherTaskViewController(QuickstepLauncher activity)1427 LauncherTaskViewController(QuickstepLauncher activity) { 1428 super(activity); 1429 } 1430 1431 @Override isRecentsInteractive()1432 protected boolean isRecentsInteractive() { 1433 return mContainer.isInState(OVERVIEW) || mContainer.isInState(OVERVIEW_MODAL_TASK); 1434 } 1435 1436 @Override isRecentsModal()1437 protected boolean isRecentsModal() { 1438 return mContainer.isInState(OVERVIEW_MODAL_TASK); 1439 } 1440 1441 @Override onUserControlledAnimationCreated(AnimatorPlaybackController animController)1442 protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) { 1443 mContainer.getStateManager().setCurrentUserControlledAnimation(animController); 1444 } 1445 } 1446 1447 @Override dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1448 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1449 super.dump(prefix, fd, writer, args); 1450 if (mDepthController != null) { 1451 mDepthController.dump(prefix, writer); 1452 } 1453 RecentsView recentsView = getOverviewPanel(); 1454 writer.println("\nQuickstepLauncher:"); 1455 writer.println(prefix + "\tmOrientationState: " + (recentsView == null ? "recentsNull" : 1456 recentsView.getPagedViewOrientedState())); 1457 if (recentsView != null) { 1458 recentsView.getSplitSelectController().dump(prefix, writer); 1459 } 1460 if (mAppTransitionManager != null) { 1461 mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer); 1462 } 1463 if (mHotseatPredictionController != null) { 1464 mHotseatPredictionController.dump(prefix, writer); 1465 } 1466 PredictionRowView<?> predictionRowView = 1467 getAppsView().getFloatingHeaderView().findFixedRowByType( 1468 PredictionRowView.class); 1469 predictionRowView.dump(prefix, writer); 1470 } 1471 1472 @Override onCreateView(View parent, String name, Context context, AttributeSet attrs)1473 public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { 1474 switch (name) { 1475 case "TextClock", "android.widget.TextClock" -> { 1476 TextClock tc = new TextClock(context, attrs); 1477 tc.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this)); 1478 return tc; 1479 } 1480 case "AnalogClock", "android.widget.AnalogClock" -> { 1481 AnalogClock ac = new AnalogClock(context, attrs); 1482 ac.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this)); 1483 return ac; 1484 } 1485 } 1486 return super.onCreateView(parent, name, context, attrs); 1487 } 1488 1489 @Override isRecentsViewVisible()1490 public boolean isRecentsViewVisible() { 1491 return getStateManager().getState().isRecentsViewVisible; 1492 } 1493 } 1494