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