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