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