• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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