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.quickstep;
17 
18 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
19 import static android.view.MotionEvent.ACTION_CANCEL;
20 import static android.view.MotionEvent.ACTION_DOWN;
21 import static android.view.MotionEvent.ACTION_MOVE;
22 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
23 import static android.view.MotionEvent.ACTION_POINTER_UP;
24 import static android.view.MotionEvent.ACTION_UP;
25 
26 import static com.android.launcher3.Flags.enableCursorHoverStates;
27 import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
28 import static com.android.launcher3.Flags.useActivityOverlay;
29 import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
30 import static com.android.launcher3.LauncherPrefs.backedUpItem;
31 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
32 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
33 import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
34 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
35 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
36 import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
37 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
38 import static com.android.quickstep.GestureState.DEFAULT_STATE;
39 import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
40 import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
41 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
42 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
43 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
44 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
45 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENTS_ANIMATION_START_PENDING;
46 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
47 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
48 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
49 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
50 import static com.android.wm.shell.Flags.enableBubblesLongPressNavHandle;
51 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
52 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
53 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
54 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
55 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
56 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
57 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
58 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
59 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
60 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
61 
62 import android.app.PendingIntent;
63 import android.app.Service;
64 import android.content.IIntentReceiver;
65 import android.content.IIntentSender;
66 import android.content.Intent;
67 import android.content.res.Configuration;
68 import android.graphics.Region;
69 import android.hardware.input.InputManager;
70 import android.os.Bundle;
71 import android.os.IBinder;
72 import android.os.Looper;
73 import android.os.SystemClock;
74 import android.os.Trace;
75 import android.util.ArraySet;
76 import android.util.Log;
77 import android.view.Choreographer;
78 import android.view.InputDevice;
79 import android.view.InputEvent;
80 import android.view.MotionEvent;
81 
82 import androidx.annotation.BinderThread;
83 import androidx.annotation.NonNull;
84 import androidx.annotation.Nullable;
85 import androidx.annotation.UiThread;
86 import androidx.annotation.VisibleForTesting;
87 
88 import com.android.launcher3.BaseDraggingActivity;
89 import com.android.launcher3.ConstantItem;
90 import com.android.launcher3.EncryptionType;
91 import com.android.launcher3.LauncherPrefs;
92 import com.android.launcher3.anim.AnimatedFloat;
93 import com.android.launcher3.config.FeatureFlags;
94 import com.android.launcher3.provider.RestoreDbTask;
95 import com.android.launcher3.statemanager.StatefulActivity;
96 import com.android.launcher3.taskbar.TaskbarActivityContext;
97 import com.android.launcher3.taskbar.TaskbarManager;
98 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
99 import com.android.launcher3.testing.TestLogging;
100 import com.android.launcher3.testing.shared.ResourceUtils;
101 import com.android.launcher3.testing.shared.TestProtocol;
102 import com.android.launcher3.util.DisplayController;
103 import com.android.launcher3.util.LockedUserState;
104 import com.android.launcher3.util.PluginManagerWrapper;
105 import com.android.launcher3.util.SafeCloseable;
106 import com.android.launcher3.util.ScreenOnTracker;
107 import com.android.launcher3.util.TraceHelper;
108 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
109 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
110 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
111 import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer;
112 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
113 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
114 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
115 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
116 import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer;
117 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
118 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
119 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
120 import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer;
121 import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
122 import com.android.quickstep.util.ActiveGestureLog;
123 import com.android.quickstep.util.ActiveGestureLog.CompoundString;
124 import com.android.quickstep.util.AssistStateManager;
125 import com.android.quickstep.util.AssistUtils;
126 import com.android.quickstep.views.RecentsViewContainer;
127 import com.android.systemui.shared.recents.IOverviewProxy;
128 import com.android.systemui.shared.recents.ISystemUiProxy;
129 import com.android.systemui.shared.system.ActivityManagerWrapper;
130 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
131 import com.android.systemui.shared.system.InputConsumerController;
132 import com.android.systemui.shared.system.InputMonitorCompat;
133 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
134 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
135 import com.android.systemui.unfold.progress.IUnfoldAnimation;
136 import com.android.wm.shell.back.IBackAnimation;
137 import com.android.wm.shell.bubbles.IBubbles;
138 import com.android.wm.shell.common.pip.IPip;
139 import com.android.wm.shell.desktopmode.IDesktopMode;
140 import com.android.wm.shell.draganddrop.IDragAndDrop;
141 import com.android.wm.shell.onehanded.IOneHanded;
142 import com.android.wm.shell.recents.IRecentTasks;
143 import com.android.wm.shell.shared.IShellTransitions;
144 import com.android.wm.shell.splitscreen.ISplitScreen;
145 import com.android.wm.shell.startingsurface.IStartingWindow;
146 
147 import java.io.FileDescriptor;
148 import java.io.PrintWriter;
149 import java.lang.ref.WeakReference;
150 import java.util.Set;
151 import java.util.function.Consumer;
152 import java.util.function.Function;
153 
154 /**
155  * Service connected by system-UI for handling touch interaction.
156  */
157 public class TouchInteractionService extends Service {
158 
159     private static final String SUBSTRING_PREFIX = "; ";
160     private static final String NEWLINE_PREFIX = "\n\t\t\t-> ";
161 
162     private static final String TAG = "TouchInteractionService";
163 
164     private static final ConstantItem<Boolean> HAS_ENABLED_QUICKSTEP_ONCE = backedUpItem(
165             "launcher.has_enabled_quickstep_once", false, EncryptionType.ENCRYPTED);
166 
167     private final TISBinder mTISBinder = new TISBinder(this);
168 
169     /**
170      * Local IOverviewProxy implementation with some methods for local components
171      */
172     public static class TISBinder extends IOverviewProxy.Stub {
173 
174         private final WeakReference<TouchInteractionService> mTis;
175 
176         @Nullable private Runnable mOnOverviewTargetChangeListener = null;
177 
TISBinder(TouchInteractionService tis)178         private TISBinder(TouchInteractionService tis) {
179             mTis = new WeakReference<>(tis);
180         }
181 
182         @BinderThread
onInitialize(Bundle bundle)183         public void onInitialize(Bundle bundle) {
184             ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
185                     bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
186             IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
187             IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_BUBBLES));
188             ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
189                     KEY_EXTRA_SHELL_SPLIT_SCREEN));
190             IOneHanded onehanded = IOneHanded.Stub.asInterface(
191                     bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
192             IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
193                     bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
194             IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
195                     bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
196             ISysuiUnlockAnimationController launcherUnlockAnimationController =
197                     ISysuiUnlockAnimationController.Stub.asInterface(
198                             bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
199             IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
200                     bundle.getBinder(KEY_EXTRA_SHELL_RECENT_TASKS));
201             IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
202                     bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
203             IDesktopMode desktopMode = IDesktopMode.Stub.asInterface(
204                     bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
205             IUnfoldAnimation unfoldTransition = IUnfoldAnimation.Stub.asInterface(
206                     bundle.getBinder(KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER));
207             IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface(
208                     bundle.getBinder(KEY_EXTRA_SHELL_DRAG_AND_DROP));
209             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
210                 SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip,
211                         bubbles, splitscreen, onehanded, shellTransitions, startingWindow,
212                         recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode,
213                         unfoldTransition, dragAndDrop);
214                 tis.initInputMonitor("TISBinder#onInitialize()");
215                 tis.preloadOverview(true /* fromInit */);
216             }));
217             sIsInitialized = true;
218         }
219 
220         @BinderThread
221         @Override
onTaskbarToggled()222         public void onTaskbarToggled() {
223             if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) return;
224             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
225                 TaskbarActivityContext activityContext =
226                         tis.mTaskbarManager.getCurrentActivityContext();
227 
228                 if (activityContext != null) {
229                     activityContext.toggleTaskbarStash();
230                 }
231             }));
232         }
233 
234         @BinderThread
onOverviewToggle()235         public void onOverviewToggle() {
236             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
237             executeForTouchInteractionService(tis -> {
238                 // If currently screen pinning, do not enter overview
239                 if (tis.mDeviceState.isScreenPinningActive()) {
240                     return;
241                 }
242                 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
243                 tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
244             });
245         }
246 
247         @BinderThread
248         @Override
onOverviewShown(boolean triggeredFromAltTab)249         public void onOverviewShown(boolean triggeredFromAltTab) {
250             executeForTouchInteractionService(tis -> {
251                 if (triggeredFromAltTab) {
252                     TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
253                     tis.mOverviewCommandHelper.addCommand(
254                             OverviewCommandHelper.TYPE_KEYBOARD_INPUT);
255                 } else {
256                     tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW);
257                 }
258             });
259         }
260 
261         @BinderThread
262         @Override
onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)263         public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
264             executeForTouchInteractionService(tis -> {
265                 if (triggeredFromAltTab && !triggeredFromHomeKey) {
266                     // onOverviewShownFromAltTab hides the overview and ends at the target app
267                     tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE);
268                 }
269             });
270         }
271 
272         @BinderThread
273         @Override
onAssistantAvailable(boolean available, boolean longPressHomeEnabled)274         public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) {
275             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
276                 tis.mDeviceState.setAssistantAvailable(available);
277                 tis.onAssistantVisibilityChanged();
278                 executeForTaskbarManager(taskbarManager -> taskbarManager
279                         .onLongPressHomeEnabled(longPressHomeEnabled));
280             }));
281         }
282 
283         @BinderThread
284         @Override
onAssistantVisibilityChanged(float visibility)285         public void onAssistantVisibilityChanged(float visibility) {
286             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
287                 tis.mDeviceState.setAssistantVisibility(visibility);
288                 tis.onAssistantVisibilityChanged();
289             }));
290         }
291 
292         /**
293          * Sent when the assistant has been invoked with the given type (defined in AssistManager)
294          * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested
295          * was previously called including this invocation type.
296          */
297         @Override
onAssistantOverrideInvoked(int invocationType)298         public void onAssistantOverrideInvoked(int invocationType) {
299             executeForTouchInteractionService(tis -> {
300                 if (!AssistUtils.newInstance(tis).tryStartAssistOverride(invocationType)) {
301                     Log.w(TAG, "Failed to invoke Assist override");
302                 }
303             });
304         }
305 
306         @BinderThread
onSystemUiStateChanged(@ystemUiStateFlags long stateFlags)307         public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags) {
308             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
309                 long lastFlags = tis.mDeviceState.getSystemUiStateFlags();
310                 tis.mDeviceState.setSystemUiFlags(stateFlags);
311                 tis.onSystemUiFlagsChanged(lastFlags);
312             }));
313         }
314 
315         @BinderThread
onActiveNavBarRegionChanges(Region region)316         public void onActiveNavBarRegionChanges(Region region) {
317             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
318                     tis -> tis.mDeviceState.setDeferredGestureRegion(region)));
319         }
320 
321         @BinderThread
322         @Override
enterStageSplitFromRunningApp(boolean leftOrTop)323         public void enterStageSplitFromRunningApp(boolean leftOrTop) {
324             executeForTouchInteractionService(tis -> {
325                 StatefulActivity activity =
326                         tis.mOverviewComponentObserver.getActivityInterface().getCreatedContainer();
327                 if (activity != null) {
328                     activity.enterStageSplitFromRunningApp(leftOrTop);
329                 }
330             });
331         }
332 
333         /**
334          * Preloads the Overview activity.
335          * <p>
336          * This method should only be used when the All Set page of the SUW is reached to safely
337          * preload the Launcher for the SUW first reveal.
338          */
preloadOverviewForSUWAllSet()339         public void preloadOverviewForSUWAllSet() {
340             executeForTouchInteractionService(tis -> tis.preloadOverview(false, true));
341         }
342 
343         @Override
onRotationProposal(int rotation, boolean isValid)344         public void onRotationProposal(int rotation, boolean isValid) {
345             executeForTaskbarManager(taskbarManager ->
346                     taskbarManager.onRotationProposal(rotation, isValid));
347         }
348 
349         @Override
disable(int displayId, int state1, int state2, boolean animate)350         public void disable(int displayId, int state1, int state2, boolean animate) {
351             executeForTaskbarManager(taskbarManager ->
352                     taskbarManager.disableNavBarElements(displayId, state1, state2, animate));
353         }
354 
355         @Override
onSystemBarAttributesChanged(int displayId, int behavior)356         public void onSystemBarAttributesChanged(int displayId, int behavior) {
357             executeForTaskbarManager(taskbarManager ->
358                     taskbarManager.onSystemBarAttributesChanged(displayId, behavior));
359         }
360 
361         @Override
onNavButtonsDarkIntensityChanged(float darkIntensity)362         public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
363             executeForTaskbarManager(taskbarManager ->
364                     taskbarManager.onNavButtonsDarkIntensityChanged(darkIntensity));
365         }
366 
367         @Override
onNavigationBarLumaSamplingEnabled(int displayId, boolean enable)368         public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
369             executeForTaskbarManager(taskbarManager ->
370                     taskbarManager.onNavigationBarLumaSamplingEnabled(displayId, enable));
371         }
372 
executeForTouchInteractionService( @onNull Consumer<TouchInteractionService> tisConsumer)373         private void executeForTouchInteractionService(
374                 @NonNull Consumer<TouchInteractionService> tisConsumer) {
375             TouchInteractionService tis = mTis.get();
376             if (tis == null) return;
377             tisConsumer.accept(tis);
378         }
379 
executeForTaskbarManager( @onNull Consumer<TaskbarManager> taskbarManagerConsumer)380         private void executeForTaskbarManager(
381                 @NonNull Consumer<TaskbarManager> taskbarManagerConsumer) {
382             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
383                 TaskbarManager taskbarManager = tis.mTaskbarManager;
384                 if (taskbarManager == null) return;
385                 taskbarManagerConsumer.accept(taskbarManager);
386             }));
387         }
388 
389         /**
390          * Returns the {@link TaskbarManager}.
391          * <p>
392          * Returns {@code null} if TouchInteractionService is not connected
393          */
394         @Nullable
getTaskbarManager()395         public TaskbarManager getTaskbarManager() {
396             TouchInteractionService tis = mTis.get();
397             if (tis == null) return null;
398             return tis.mTaskbarManager;
399         }
400 
401         @VisibleForTesting
injectFakeTrackpadForTesting()402         public void injectFakeTrackpadForTesting() {
403             TouchInteractionService tis = mTis.get();
404             if (tis == null) return;
405             tis.mTrackpadsConnected.add(1000);
406             tis.initInputMonitor("tapl testing");
407         }
408 
409         @VisibleForTesting
ejectFakeTrackpadForTesting()410         public void ejectFakeTrackpadForTesting() {
411             TouchInteractionService tis = mTis.get();
412             if (tis == null) return;
413             tis.mTrackpadsConnected.clear();
414             // This method destroys the current input monitor if set up, and only init a new one
415             // in 3-button mode if {@code mTrackpadsConnected} is not empty. So in other words,
416             // it will destroy the input monitor.
417             tis.initInputMonitor("tapl testing");
418         }
419 
420         /**
421          * Sets whether a predictive back-to-home animation is in progress in the device state
422          */
setPredictiveBackToHomeInProgress(boolean isInProgress)423         public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
424             executeForTouchInteractionService(tis ->
425                     tis.mDeviceState.setPredictiveBackToHomeInProgress(isInProgress));
426         }
427 
428         /**
429          * Returns the {@link OverviewCommandHelper}.
430          * <p>
431          * Returns {@code null} if TouchInteractionService is not connected
432          */
433         @Nullable
getOverviewCommandHelper()434         public OverviewCommandHelper getOverviewCommandHelper() {
435             TouchInteractionService tis = mTis.get();
436             if (tis == null) return null;
437             return tis.mOverviewCommandHelper;
438         }
439 
440         /**
441          * Sets a proxy to bypass swipe up behavior
442          */
setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy)443         public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) {
444             executeForTouchInteractionService(
445                     tis -> tis.mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null));
446         }
447 
448         /**
449          * Sets the task id where gestures should be blocked
450          */
setGestureBlockedTaskId(int taskId)451         public void setGestureBlockedTaskId(int taskId) {
452             executeForTouchInteractionService(
453                     tis -> tis.mDeviceState.setGestureBlockingTaskId(taskId));
454         }
455 
456         /** Sets a listener to be run on Overview Target updates. */
setOverviewTargetChangeListener(@ullable Runnable listener)457         public void setOverviewTargetChangeListener(@Nullable Runnable listener) {
458             mOnOverviewTargetChangeListener = listener;
459         }
460 
onOverviewTargetChange()461         protected void onOverviewTargetChange() {
462             if (mOnOverviewTargetChangeListener != null) {
463                 mOnOverviewTargetChangeListener.run();
464                 mOnOverviewTargetChangeListener = null;
465             }
466         }
467 
468         /** Refreshes the current overview target. */
refreshOverviewTarget()469         public void refreshOverviewTarget() {
470             executeForTouchInteractionService(tis -> {
471                 tis.mAllAppsActionManager.onDestroy();
472                 tis.onOverviewTargetChange(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
473             });
474         }
475     }
476 
477     private final InputManager.InputDeviceListener mInputDeviceListener =
478             new InputManager.InputDeviceListener() {
479                 @Override
480                 public void onInputDeviceAdded(int deviceId) {
481                     if (isTrackpadDevice(deviceId)) {
482                         boolean wasEmpty = mTrackpadsConnected.isEmpty();
483                         mTrackpadsConnected.add(deviceId);
484                         if (wasEmpty) {
485                             update();
486                         }
487                     }
488                 }
489 
490                 @Override
491                 public void onInputDeviceChanged(int deviceId) {
492                 }
493 
494                 @Override
495                 public void onInputDeviceRemoved(int deviceId) {
496                     mTrackpadsConnected.remove(deviceId);
497                     if (mTrackpadsConnected.isEmpty()) {
498                         update();
499                     }
500                 }
501 
502                 private void update() {
503                     if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
504                         // Don't destroy and reinitialize input monitor due to trackpad
505                         // connecting when it's already set up.
506                         return;
507                     }
508                     initInputMonitor("onTrackpadConnected()");
509                 }
510 
511                 private boolean isTrackpadDevice(int deviceId) {
512                     InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
513                     if (inputDevice == null) {
514                         return false;
515                     }
516                     return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE
517                             | InputDevice.SOURCE_TOUCHPAD);
518                 }
519             };
520 
521     private static boolean sConnected = false;
522     private static boolean sIsInitialized = false;
523     private RotationTouchHelper mRotationTouchHelper;
524 
isConnected()525     public static boolean isConnected() {
526         return sConnected;
527     }
528 
isInitialized()529     public static boolean isInitialized() {
530         return sIsInitialized;
531     }
532 
533     private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
534             this::createLauncherSwipeHandler;
535     private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
536             this::createFallbackSwipeHandler;
537 
538     private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
539 
540     private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() {
541         @Override
542         public void onNavigateHome() {
543             mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HOME);
544         }
545 
546         @Override
547         public void onToggleOverview() {
548             mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
549         }
550     };
551 
552     private ActivityManagerWrapper mAM;
553     private OverviewCommandHelper mOverviewCommandHelper;
554     private OverviewComponentObserver mOverviewComponentObserver;
555     private InputConsumerController mInputConsumer;
556     private RecentsAnimationDeviceState mDeviceState;
557     private TaskAnimationManager mTaskAnimationManager;
558 
559     private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
560     private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
561     private Choreographer mMainChoreographer;
562     private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
563     private GestureState mGestureState = DEFAULT_STATE;
564 
565     private InputMonitorCompat mInputMonitorCompat;
566     private InputEventReceiver mInputEventReceiver;
567 
568     private TaskbarManager mTaskbarManager;
569     private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
570     private AllAppsActionManager mAllAppsActionManager;
571     private InputManager mInputManager;
572     private final Set<Integer> mTrackpadsConnected = new ArraySet<>();
573 
574     @Override
onCreate()575     public void onCreate() {
576         super.onCreate();
577         // Initialize anything here that is needed in direct boot mode.
578         // Everything else should be initialized in onUserUnlocked() below.
579         mMainChoreographer = Choreographer.getInstance();
580         mAM = ActivityManagerWrapper.getInstance();
581         mDeviceState = new RecentsAnimationDeviceState(this, true);
582         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
583         mAllAppsActionManager = new AllAppsActionManager(
584                 this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
585         mInputManager = getSystemService(InputManager.class);
586         if (ENABLE_TRACKPAD_GESTURE.get()) {
587             mInputManager.registerInputDeviceListener(mInputDeviceListener,
588                     UI_HELPER_EXECUTOR.getHandler());
589             int [] inputDevices = mInputManager.getInputDeviceIds();
590             for (int inputDeviceId : inputDevices) {
591                 mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
592             }
593         }
594         mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks);
595         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
596 
597         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
598         LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
599         LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
600         mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
601         sConnected = true;
602 
603         ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
604     }
605 
disposeEventHandlers(String reason)606     private void disposeEventHandlers(String reason) {
607         Log.d(TAG, "disposeEventHandlers: Reason: " + reason);
608         if (mInputEventReceiver != null) {
609             mInputEventReceiver.dispose();
610             mInputEventReceiver = null;
611         }
612         if (mInputMonitorCompat != null) {
613             mInputMonitorCompat.dispose();
614             mInputMonitorCompat = null;
615         }
616     }
617 
initInputMonitor(String reason)618     private void initInputMonitor(String reason) {
619         disposeEventHandlers("Initializing input monitor due to: " + reason);
620 
621         if (mDeviceState.isButtonNavMode() && (!ENABLE_TRACKPAD_GESTURE.get()
622                 || mTrackpadsConnected.isEmpty())) {
623             return;
624         }
625 
626         mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
627         mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
628                 mMainChoreographer, this::onInputEvent);
629 
630         mRotationTouchHelper.updateGestureTouchRegions();
631     }
632 
633     /**
634      * Called when the navigation mode changes, guaranteed to be after the device state has updated.
635      */
onNavigationModeChanged()636     private void onNavigationModeChanged() {
637         initInputMonitor("onNavigationModeChanged()");
638         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
639     }
640 
641     @UiThread
onUserUnlocked()642     public void onUserUnlocked() {
643         Log.d(TAG, "onUserUnlocked: userId=" + getUserId());
644         mTaskAnimationManager = new TaskAnimationManager(this);
645         mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
646         mOverviewCommandHelper = new OverviewCommandHelper(this,
647                 mOverviewComponentObserver, mTaskAnimationManager);
648         mResetGestureInputConsumer = new ResetGestureInputConsumer(
649                 mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
650         mInputConsumer.registerInputConsumer();
651         onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
652         onAssistantVisibilityChanged();
653 
654         // Initialize the task tracker
655         TopTaskTracker.INSTANCE.get(this);
656 
657         // Temporarily disable model preload
658         // new ModelPreload().start(this);
659         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
660 
661         mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
662         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
663     }
664 
getOverviewCommandHelper()665     public OverviewCommandHelper getOverviewCommandHelper() {
666         return mOverviewCommandHelper;
667     }
668 
resetHomeBounceSeenOnQuickstepEnabledFirstTime()669     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
670         if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
671             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
672             // mode doesn't have gestures
673             return;
674         }
675 
676         // Reset home bounce seen on quick step enabled for first time
677         LauncherPrefs prefs = LauncherPrefs.get(this);
678         if (!prefs.get(HAS_ENABLED_QUICKSTEP_ONCE)) {
679             prefs.put(
680                     HAS_ENABLED_QUICKSTEP_ONCE.to(true),
681                     HOME_BOUNCE_SEEN.to(false));
682         }
683     }
684 
onOverviewTargetChange(boolean isHomeAndOverviewSame)685     private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
686         mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
687 
688         StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
689                 .getCreatedContainer();
690         if (newOverviewActivity != null) {
691             mTaskbarManager.setActivity(newOverviewActivity);
692         }
693         mTISBinder.onOverviewTargetChange();
694     }
695 
createAllAppsPendingIntent()696     private PendingIntent createAllAppsPendingIntent() {
697         if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
698             return new PendingIntent(new IIntentSender.Stub() {
699                 @Override
700                 public void send(int code, Intent intent, String resolvedType,
701                         IBinder allowlistToken, IIntentReceiver finishedReceiver,
702                         String requiredPermission, Bundle options) {
703                     MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps());
704                 }
705             });
706         } else {
707             return PendingIntent.getActivity(
708                     this,
709                     GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
710                     new Intent(mOverviewComponentObserver.getHomeIntent())
711                             .setAction(INTENT_ACTION_ALL_APPS_TOGGLE),
712                     PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
713         }
714     }
715 
716     @UiThread
717     private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags) {
718         if (LockedUserState.get(this).isUserUnlocked()) {
719             long systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
720             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
721             mOverviewComponentObserver.onSystemUiStateChanged();
722             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
723             mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
724         }
725     }
726 
727     @UiThread
728     private void onAssistantVisibilityChanged() {
729         if (LockedUserState.get(this).isUserUnlocked()) {
730             mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
731                     mDeviceState.getAssistantVisibility());
732         }
733     }
734 
735     @Override
736     public void onDestroy() {
737         Log.d(TAG, "Touch service destroyed: user=" + getUserId());
738         sIsInitialized = false;
739         if (LockedUserState.get(this).isUserUnlocked()) {
740             mInputConsumer.unregisterInputConsumer();
741             mOverviewComponentObserver.onDestroy();
742         }
743         disposeEventHandlers("TouchInteractionService onDestroy()");
744         mDeviceState.destroy();
745         SystemUiProxy.INSTANCE.get(this).clearProxy();
746 
747         mAllAppsActionManager.onDestroy();
748 
749         mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
750         mTrackpadsConnected.clear();
751 
752         mTaskbarManager.destroy();
753         sConnected = false;
754 
755         ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
756         super.onDestroy();
757     }
758 
759     @Override
760     public IBinder onBind(Intent intent) {
761         Log.d(TAG, "Touch service connected: user=" + getUserId());
762         return mTISBinder;
763     }
764 
765     protected void onScreenOnChanged(boolean isOn) {
766         if (isOn) {
767             return;
768         }
769         long currentTime = SystemClock.uptimeMillis();
770         MotionEvent cancelEvent = MotionEvent.obtain(
771                 currentTime, currentTime, ACTION_CANCEL, 0f, 0f, 0);
772         onInputEvent(cancelEvent);
773         cancelEvent.recycle();
774     }
775 
776     private void onInputEvent(InputEvent ev) {
777         if (!(ev instanceof MotionEvent)) {
778             ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
779                     .append("Cannot process input event: received unknown event ")
780                     .append(ev.toString()));
781             return;
782         }
783         MotionEvent event = (MotionEvent) ev;
784 
785         TestLogging.recordMotionEvent(
786                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
787 
788         boolean isUserUnlocked = LockedUserState.get(this).isUserUnlocked();
789         if (!isUserUnlocked || (mDeviceState.isButtonNavMode()
790                 && !isTrackpadMotionEvent(event))) {
791             ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
792                     .append("Cannot process input event: ")
793                     .append(!isUserUnlocked
794                             ? "user is locked"
795                             : "using 3-button nav and event is not a trackpad event"));
796             return;
797         }
798 
799         final int action = event.getActionMasked();
800         // Note this will create a new consumer every mouse click, as after ACTION_UP from the click
801         // an ACTION_HOVER_ENTER will fire as well.
802         boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
803                 && isHoverActionWithoutConsumer(event);
804 
805         if (enableHandleDelayedGestureCallbacks()) {
806             if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
807                 mTaskAnimationManager.notifyNewGestureStart();
808             }
809             if (mTaskAnimationManager.shouldIgnoreMotionEvents()) {
810                 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
811                     ActiveGestureLog.INSTANCE.addLog(
812                             new CompoundString("TIS.onMotionEvent: A new gesture has been ")
813                                     .append("started, but a previously-requested recents ")
814                                     .append("animation hasn't started. Ignoring all following ")
815                                     .append("motion events."),
816                             RECENTS_ANIMATION_START_PENDING);
817                 }
818                 return;
819             }
820         }
821 
822         SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
823 
824         CompoundString reasonString = action == ACTION_DOWN
825                 ? new CompoundString("TIS.onMotionEvent: ") : CompoundString.NO_OP;
826         if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
827             mRotationTouchHelper.setOrientationTransformIfNeeded(event);
828 
829             boolean isOneHandedModeActive = mDeviceState.isOneHandedModeActive();
830             boolean isInSwipeUpTouchRegion = mRotationTouchHelper.isInSwipeUpTouchRegion(event);
831             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
832             if (isInSwipeUpTouchRegion && tac != null) {
833                 tac.closeKeyboardQuickSwitchView();
834             }
835             if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
836                     || isHoverActionWithoutConsumer) {
837                 reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion
838                                 ? "one handed mode is not active and event is in swipe up region"
839                                 : "isHoverActionWithoutConsumer == true")
840                         .append(", creating new input consumer");
841                 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
842                 // onConsumerInactive and wipe the previous gesture state
843                 GestureState prevGestureState = new GestureState(mGestureState);
844                 GestureState newGestureState = createGestureState(mGestureState,
845                         getTrackpadGestureType(event));
846                 mConsumer.onConsumerAboutToBeSwitched();
847                 mGestureState = newGestureState;
848                 mConsumer = newConsumer(prevGestureState, mGestureState, event);
849                 mUncheckedConsumer = mConsumer;
850             } else if ((mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event))
851                     && mDeviceState.canTriggerAssistantAction(event)) {
852                 reasonString.append(mDeviceState.isFullyGesturalNavMode()
853                                 ? "using fully gestural nav"
854                                 : "event is a trackpad multi-finger swipe")
855                         .append(" and event can trigger assistant action")
856                         .append(", consuming gesture for assistant action");
857                 mGestureState = createGestureState(mGestureState,
858                         getTrackpadGestureType(event));
859                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
860                 // should not interrupt it. QuickSwitch assumes that interruption can only
861                 // happen if the next gesture is also quick switch.
862                 mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event);
863             } else if (mDeviceState.canTriggerOneHandedAction(event)) {
864                 reasonString.append("event can trigger one-handed action")
865                                 .append(", consuming gesture for one-handed action");
866                 // Consume gesture event for triggering one handed feature.
867                 mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
868                         InputConsumer.NO_OP, mInputMonitorCompat);
869             } else {
870                 mUncheckedConsumer = InputConsumer.NO_OP;
871             }
872         } else {
873             // Other events
874             if (mUncheckedConsumer != InputConsumer.NO_OP) {
875                 // Only transform the event if we are handling it in a proper consumer
876                 mRotationTouchHelper.setOrientationTransformIfNeeded(event);
877             }
878         }
879 
880         if (mUncheckedConsumer != InputConsumer.NO_OP) {
881             switch (action) {
882                 case ACTION_DOWN:
883                     ActiveGestureLog.INSTANCE.addLog(reasonString);
884                     // fall through
885                 case ACTION_UP:
886                     ActiveGestureLog.INSTANCE.addLog(
887                             new CompoundString("onMotionEvent(")
888                                     .append((int) event.getRawX())
889                                     .append(", ")
890                                     .append((int) event.getRawY())
891                                     .append("): ")
892                                     .append(MotionEvent.actionToString(action))
893                                     .append(", ")
894                                     .append(MotionEvent.classificationToString(
895                                             event.getClassification())),
896                             /* gestureEvent= */ action == ACTION_DOWN
897                                     ? MOTION_DOWN
898                                     : MOTION_UP);
899                     break;
900                 case ACTION_MOVE:
901                     ActiveGestureLog.INSTANCE.addLog(
902                             new CompoundString("onMotionEvent: ")
903                                     .append(MotionEvent.actionToString(action))
904                                     .append(",")
905                                     .append(MotionEvent.classificationToString(
906                                             event.getClassification()))
907                                     .append(", pointerCount: ")
908                                     .append(event.getPointerCount()),
909                             MOTION_MOVE);
910                     break;
911                 default: {
912                     ActiveGestureLog.INSTANCE.addLog(
913                             new CompoundString("onMotionEvent: ")
914                                     .append(MotionEvent.actionToString(action))
915                                     .append(",")
916                                     .append(MotionEvent.classificationToString(
917                                             event.getClassification())));
918                 }
919             }
920         }
921 
922         boolean cancelGesture = mGestureState.getContainerInterface() != null
923                 && mGestureState.getContainerInterface().shouldCancelCurrentGesture();
924         boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
925                 && mConsumer != null
926                 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
927         if (cancelGesture) {
928             event.setAction(ACTION_CANCEL);
929         }
930 
931         if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN
932                 || action == ACTION_POINTER_UP)) {
933             // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad.
934         } else if (isCursorHoverEvent(event)) {
935             mUncheckedConsumer.onHoverEvent(event);
936         } else {
937             mUncheckedConsumer.onMotionEvent(event);
938         }
939 
940         if (cleanUpConsumer) {
941             reset();
942         }
943         traceToken.close();
944     }
945 
946     private boolean isHoverActionWithoutConsumer(MotionEvent event) {
947         // Only process these events when taskbar is present.
948         TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
949         boolean isTaskbarPresent = tac != null && tac.getDeviceProfile().isTaskbarPresent
950                 && !tac.isPhoneMode();
951         return event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0
952                 && isTaskbarPresent;
953     }
954 
955     // Talkback generates hover events on touch, which we do not want to consume.
956     private boolean isCursorHoverEvent(MotionEvent event) {
957         return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE;
958     }
959 
960     private InputConsumer tryCreateAssistantInputConsumer(
961             GestureState gestureState, MotionEvent motionEvent) {
962         return tryCreateAssistantInputConsumer(
963                 InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP);
964     }
965 
966     private InputConsumer tryCreateAssistantInputConsumer(
967             InputConsumer base,
968             GestureState gestureState,
969             MotionEvent motionEvent,
970             CompoundString reasonString) {
971         if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
972             reasonString.append(SUBSTRING_PREFIX)
973                     .append("is gesture-blocked task, using base input consumer");
974             return base;
975         } else {
976             reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer");
977             return new AssistantInputConsumer(
978                     this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent);
979         }
980     }
981 
982     public GestureState createGestureState(GestureState previousGestureState,
983             GestureState.TrackpadGestureType trackpadGestureType) {
984         final GestureState gestureState;
985         TopTaskTracker.CachedTaskInfo taskInfo;
986         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
987             gestureState = new GestureState(mOverviewComponentObserver,
988                     ActiveGestureLog.INSTANCE.getLogId());
989             TopTaskTracker.CachedTaskInfo previousTaskInfo = previousGestureState.getRunningTask();
990             // previousTaskInfo can be null iff previousGestureState == GestureState.DEFAULT_STATE
991             taskInfo = previousTaskInfo != null
992                     ? previousTaskInfo
993                     : TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
994             gestureState.updateRunningTask(taskInfo);
995             gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds());
996             gestureState.updatePreviouslyAppearedTaskIds(
997                     previousGestureState.getPreviouslyAppearedTaskIds());
998         } else {
999             gestureState = new GestureState(mOverviewComponentObserver,
1000                     ActiveGestureLog.INSTANCE.incrementLogId());
1001             taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
1002             gestureState.updateRunningTask(taskInfo);
1003         }
1004         gestureState.setTrackpadGestureType(trackpadGestureType);
1005 
1006         // Log initial state for the gesture.
1007         ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=")
1008                 .append(taskInfo.getPackageName()));
1009         ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=")
1010                 .append(mDeviceState.getSystemUiStateString()));
1011         return gestureState;
1012     }
1013 
1014     private InputConsumer newConsumer(
1015             GestureState previousGestureState, GestureState newGestureState, MotionEvent event) {
1016         AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
1017         if (progressProxy != null) {
1018             InputConsumer consumer = new ProgressDelegateInputConsumer(
1019                     this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy);
1020 
1021             logInputConsumerSelectionReason(consumer, newCompoundString(
1022                     "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer"));
1023 
1024             return consumer;
1025         }
1026 
1027         boolean canStartSystemGesture =
1028                 mGestureState.isTrackpadGesture() ? mDeviceState.canStartTrackpadGesture()
1029                         : mDeviceState.canStartSystemGesture();
1030 
1031         if (!LockedUserState.get(this).isUserUnlocked()) {
1032             CompoundString reasonString = newCompoundString("device locked");
1033             InputConsumer consumer;
1034             if (canStartSystemGesture) {
1035                 // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
1036                 // launched while device is locked even after exiting direct boot mode (e.g. camera).
1037                 consumer = createDeviceLockedInputConsumer(
1038                         newGestureState, reasonString.append(SUBSTRING_PREFIX)
1039                                 .append("can start system gesture"));
1040             } else {
1041                 consumer = getDefaultInputConsumer(
1042                         reasonString.append(SUBSTRING_PREFIX)
1043                                 .append("cannot start system gesture"));
1044             }
1045             logInputConsumerSelectionReason(consumer, reasonString);
1046             return consumer;
1047         }
1048 
1049         CompoundString reasonString;
1050         InputConsumer base;
1051         // When there is an existing recents animation running, bypass systemState check as this is
1052         // a followup gesture and the first gesture started in a valid system state.
1053         if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) {
1054             reasonString = newCompoundString(canStartSystemGesture
1055                     ? "can start system gesture" : "recents animation was running")
1056                     .append(", trying to use base consumer");
1057             base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString);
1058         } else {
1059             reasonString = newCompoundString(
1060                     "cannot start system gesture and recents animation was not running")
1061                     .append(", trying to use default input consumer");
1062             base = getDefaultInputConsumer(reasonString);
1063         }
1064         if (mDeviceState.isGesturalNavMode() || newGestureState.isTrackpadGesture()) {
1065             handleOrientationSetup(base);
1066         }
1067         if (mDeviceState.isFullyGesturalNavMode() || newGestureState.isTrackpadGesture()) {
1068             String reasonPrefix =
1069                     "device is in gesture navigation mode or 3-button mode with a trackpad gesture";
1070             if (mDeviceState.canTriggerAssistantAction(event)) {
1071                 reasonString.append(NEWLINE_PREFIX)
1072                         .append(reasonPrefix)
1073                         .append(SUBSTRING_PREFIX)
1074                         .append("gesture can trigger the assistant")
1075                         .append(", trying to use assistant input consumer");
1076                 base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString);
1077             }
1078 
1079             // If Taskbar is present, we listen for swipe or cursor hover events to unstash it.
1080             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
1081             if (tac != null && !(base instanceof AssistantInputConsumer)) {
1082                 // Present always on large screen or on small screen w/ flag
1083                 boolean useTaskbarConsumer = tac.getDeviceProfile().isTaskbarPresent
1084                         && !tac.isPhoneMode()
1085                         && !tac.isInStashedLauncherState();
1086                 if (canStartSystemGesture && useTaskbarConsumer) {
1087                     reasonString.append(NEWLINE_PREFIX)
1088                             .append(reasonPrefix)
1089                             .append(SUBSTRING_PREFIX)
1090                             .append("TaskbarActivityContext != null, ")
1091                             .append("using TaskbarUnstashInputConsumer");
1092                     base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac,
1093                             mOverviewCommandHelper, mGestureState);
1094                 }
1095             }
1096             if (enableBubblesLongPressNavHandle()) {
1097                 // Create bubbles input consumer before NavHandleLongPressInputConsumer.
1098                 // This allows for nav handle to fall back to bubbles.
1099                 if (mDeviceState.isBubblesExpanded()) {
1100                     reasonString = newCompoundString(reasonPrefix)
1101                             .append(SUBSTRING_PREFIX)
1102                             .append("bubbles expanded, trying to use default input consumer");
1103                     // Bubbles can handle home gesture itself.
1104                     base = getDefaultInputConsumer(reasonString);
1105                 }
1106             }
1107 
1108             NavHandle navHandle = tac != null ? tac.getNavHandle()
1109                     : SystemUiProxy.INSTANCE.get(this);
1110             if (canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()
1111                     && navHandle.canNavHandleBeLongPressed()) {
1112                 reasonString.append(NEWLINE_PREFIX)
1113                         .append(reasonPrefix)
1114                         .append(SUBSTRING_PREFIX)
1115                         .append("Not running recents animation, ");
1116                 if (tac != null && tac.getNavHandle().canNavHandleBeLongPressed()) {
1117                     reasonString.append("stashed handle is long-pressable, ");
1118                 }
1119                 reasonString.append("using NavHandleLongPressInputConsumer");
1120                 base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat,
1121                         mDeviceState, navHandle, mGestureState);
1122             }
1123 
1124             if (!enableBubblesLongPressNavHandle()) {
1125                 // Continue overriding nav handle input consumer with bubbles
1126                 if (mDeviceState.isBubblesExpanded()) {
1127                     reasonString = newCompoundString(reasonPrefix)
1128                             .append(SUBSTRING_PREFIX)
1129                             .append("bubbles expanded, trying to use default input consumer");
1130                     // Bubbles can handle home gesture itself.
1131                     base = getDefaultInputConsumer(reasonString);
1132                 }
1133             }
1134 
1135             if (mDeviceState.isSystemUiDialogShowing()) {
1136                 reasonString = newCompoundString(reasonPrefix)
1137                         .append(SUBSTRING_PREFIX)
1138                         .append("system dialog is showing, using SysUiOverlayInputConsumer");
1139                 base = new SysUiOverlayInputConsumer(
1140                         getBaseContext(), mDeviceState, mInputMonitorCompat);
1141             }
1142 
1143             if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture()
1144                     && canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) {
1145                 reasonString = newCompoundString(reasonPrefix)
1146                         .append(SUBSTRING_PREFIX)
1147                         .append("Trackpad 3-finger gesture, using TrackpadStatusBarInputConsumer");
1148                 base = new TrackpadStatusBarInputConsumer(getBaseContext(), base,
1149                         mInputMonitorCompat);
1150             }
1151 
1152             if (mDeviceState.isScreenPinningActive()) {
1153                 reasonString = newCompoundString(reasonPrefix)
1154                         .append(SUBSTRING_PREFIX)
1155                         .append("screen pinning is active, using ScreenPinnedInputConsumer");
1156                 // Note: we only allow accessibility to wrap this, and it replaces the previous
1157                 // base input consumer (which should be NO_OP anyway since topTaskLocked == true).
1158                 base = new ScreenPinnedInputConsumer(this, newGestureState);
1159             }
1160 
1161             if (mDeviceState.canTriggerOneHandedAction(event)) {
1162                 reasonString.append(NEWLINE_PREFIX)
1163                         .append(reasonPrefix)
1164                         .append(SUBSTRING_PREFIX)
1165                         .append("gesture can trigger one handed mode")
1166                         .append(", using OneHandedModeInputConsumer");
1167                 base = new OneHandedModeInputConsumer(
1168                         this, mDeviceState, base, mInputMonitorCompat);
1169             }
1170 
1171             if (mDeviceState.isAccessibilityMenuAvailable()) {
1172                 reasonString.append(NEWLINE_PREFIX)
1173                         .append(reasonPrefix)
1174                         .append(SUBSTRING_PREFIX)
1175                         .append("accessibility menu is available")
1176                         .append(", using AccessibilityInputConsumer");
1177                 base = new AccessibilityInputConsumer(
1178                         this, mDeviceState, mGestureState, base, mInputMonitorCompat);
1179             }
1180         } else {
1181             String reasonPrefix = "device is not in gesture navigation mode";
1182             if (mDeviceState.isScreenPinningActive()) {
1183                 reasonString = newCompoundString(reasonPrefix)
1184                         .append(SUBSTRING_PREFIX)
1185                         .append("screen pinning is active, trying to use default input consumer");
1186                 base = getDefaultInputConsumer(reasonString);
1187             }
1188 
1189             if (mDeviceState.canTriggerOneHandedAction(event)) {
1190                 reasonString.append(NEWLINE_PREFIX)
1191                         .append(reasonPrefix)
1192                         .append(SUBSTRING_PREFIX)
1193                         .append("gesture can trigger one handed mode")
1194                         .append(", using OneHandedModeInputConsumer");
1195                 base = new OneHandedModeInputConsumer(
1196                         this, mDeviceState, base, mInputMonitorCompat);
1197             }
1198         }
1199         logInputConsumerSelectionReason(base, reasonString);
1200         return base;
1201     }
1202 
1203     private CompoundString newCompoundString(String substring) {
1204         return new CompoundString(NEWLINE_PREFIX).append(substring);
1205     }
1206 
1207     private void logInputConsumerSelectionReason(
1208             InputConsumer consumer, CompoundString reasonString) {
1209         ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
1210                 .append(consumer.getName())
1211                 .append(". reason(s):")
1212                 .append(reasonString));
1213         if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
1214             ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
1215         }
1216     }
1217 
1218     private void handleOrientationSetup(InputConsumer baseInputConsumer) {
1219         baseInputConsumer.notifyOrientationSetup();
1220     }
1221 
1222     private InputConsumer newBaseConsumer(
1223             GestureState previousGestureState,
1224             GestureState gestureState,
1225             MotionEvent event,
1226             CompoundString reasonString) {
1227         if (mDeviceState.isKeyguardShowingOccluded()) {
1228             // This handles apps showing over the lockscreen (e.g. camera)
1229             return createDeviceLockedInputConsumer(
1230                     gestureState,
1231                     reasonString.append(SUBSTRING_PREFIX)
1232                             .append("keyguard is showing occluded")
1233                             .append(", trying to use device locked input consumer"));
1234         }
1235 
1236         reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded");
1237 
1238         TopTaskTracker.CachedTaskInfo runningTask = gestureState.getRunningTask();
1239         // Use overview input consumer for sharesheets on top of home.
1240         boolean forceOverviewInputConsumer = gestureState.getContainerInterface().isStarted()
1241                 && runningTask != null
1242                 && runningTask.isRootChooseActivity();
1243 
1244         // In the case where we are in an excluded, translucent overlay, ignore it and treat the
1245         // running activity as the task behind the overlay.
1246         TopTaskTracker.CachedTaskInfo otherVisibleTask = runningTask == null
1247                 ? null
1248                 : runningTask.getVisibleNonExcludedTask();
1249         if (otherVisibleTask != null) {
1250             ActiveGestureLog.INSTANCE.addLog(new CompoundString("Changing active task to ")
1251                     .append(otherVisibleTask.getPackageName())
1252                     .append(" because the previous task running on top of this one (")
1253                     .append(runningTask.getPackageName())
1254                     .append(") was excluded from recents"));
1255             gestureState.updateRunningTask(otherVisibleTask);
1256         }
1257 
1258         boolean previousGestureAnimatedToLauncher =
1259                 previousGestureState.isRunningAnimationToLauncher()
1260                         || mDeviceState.isPredictiveBackToHomeInProgress();
1261         // with shell-transitions, home is resumed during recents animation, so
1262         // explicitly check against recents animation too.
1263         boolean launcherResumedThroughShellTransition =
1264                 gestureState.getContainerInterface().isResumed()
1265                         && !previousGestureState.isRecentsAnimationRunning();
1266         // If a task fragment within Launcher is resumed
1267         boolean launcherChildActivityResumed = useActivityOverlay()
1268                 && runningTask != null
1269                 && runningTask.isHomeTask()
1270                 && mOverviewComponentObserver.isHomeAndOverviewSame()
1271                 && !launcherResumedThroughShellTransition
1272                 && !previousGestureState.isRecentsAnimationRunning();
1273 
1274         if (gestureState.getContainerInterface().isInLiveTileMode()) {
1275             return createOverviewInputConsumer(
1276                     previousGestureState,
1277                     gestureState,
1278                     event,
1279                     forceOverviewInputConsumer,
1280                     reasonString.append(SUBSTRING_PREFIX)
1281                             .append("is in live tile mode, trying to use overview input consumer"));
1282         } else if (runningTask == null) {
1283             return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
1284                     .append("running task == null"));
1285         } else if (previousGestureAnimatedToLauncher
1286                 || launcherResumedThroughShellTransition
1287                 || forceOverviewInputConsumer) {
1288             return createOverviewInputConsumer(
1289                     previousGestureState,
1290                     gestureState,
1291                     event,
1292                     forceOverviewInputConsumer,
1293                     reasonString.append(SUBSTRING_PREFIX)
1294                             .append(previousGestureAnimatedToLauncher
1295                                     ? "previous gesture animated to launcher"
1296                                     : (launcherResumedThroughShellTransition
1297                                             ? "launcher resumed through a shell transition"
1298                                             : "forceOverviewInputConsumer == true"))
1299                             .append(", trying to use overview input consumer"));
1300         } else if (mDeviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
1301             return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
1302                     .append(launcherChildActivityResumed
1303                             ? "is launcher child-task, trying to use default input consumer"
1304                             : "is gesture-blocked task, trying to use default input consumer"));
1305         } else {
1306             reasonString.append(SUBSTRING_PREFIX)
1307                     .append("using OtherActivityInputConsumer");
1308             return createOtherActivityInputConsumer(gestureState, event);
1309         }
1310     }
1311 
1312     public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
1313         return !mOverviewComponentObserver.isHomeAndOverviewSame()
1314                 ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
1315     }
1316 
1317     private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
1318             MotionEvent event) {
1319 
1320         final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory();
1321         final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
1322                 || gestureState.getContainerInterface().deferStartingActivity(mDeviceState, event);
1323         final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
1324         return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
1325                 gestureState, shouldDefer, this::onConsumerInactive,
1326                 mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory);
1327     }
1328 
1329     private InputConsumer createDeviceLockedInputConsumer(
1330             GestureState gestureState, CompoundString reasonString) {
1331         if ((mDeviceState.isFullyGesturalNavMode() || gestureState.isTrackpadGesture())
1332                 && gestureState.getRunningTask() != null) {
1333             reasonString.append(SUBSTRING_PREFIX)
1334                     .append("device is in gesture nav mode or 3-button mode with a trackpad")
1335                     .append(" gesture and running task != null")
1336                     .append(", using DeviceLockedInputConsumer");
1337             return new DeviceLockedInputConsumer(
1338                     this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat);
1339         } else {
1340             return getDefaultInputConsumer(reasonString
1341                     .append(SUBSTRING_PREFIX)
1342                     .append((mDeviceState.isFullyGesturalNavMode()
1343                                     || gestureState.isTrackpadGesture())
1344                             ? "running task == null"
1345                             : "device is not in gesture nav mode and it's not a trackpad gesture")
1346                     .append(", trying to use default input consumer"));
1347         }
1348     }
1349 
1350     public InputConsumer createOverviewInputConsumer(
1351             GestureState previousGestureState,
1352             GestureState gestureState,
1353             MotionEvent event,
1354             boolean forceOverviewInputConsumer,
1355             CompoundString reasonString) {
1356         RecentsViewContainer container = gestureState.getContainerInterface().getCreatedContainer();
1357         if (container == null) {
1358             return getDefaultInputConsumer(
1359                     reasonString.append(SUBSTRING_PREFIX)
1360                             .append("activity == null, trying to use default input consumer"));
1361         }
1362 
1363         boolean hasWindowFocus = container.getRootView().hasWindowFocus();
1364         boolean isPreviousGestureAnimatingToLauncher =
1365                 previousGestureState.isRunningAnimationToLauncher()
1366                         || mDeviceState.isPredictiveBackToHomeInProgress();
1367         boolean isInLiveTileMode = gestureState.getContainerInterface().isInLiveTileMode();
1368 
1369         reasonString.append(SUBSTRING_PREFIX)
1370                 .append(hasWindowFocus
1371                         ? "activity has window focus"
1372                         : (isPreviousGestureAnimatingToLauncher
1373                                 ? "previous gesture is still animating to launcher"
1374                                 : isInLiveTileMode
1375                                         ? "device is in live mode"
1376                                         : "all overview focus conditions failed"));
1377         if (hasWindowFocus
1378                 || isPreviousGestureAnimatingToLauncher
1379                 || isInLiveTileMode) {
1380             reasonString.append(SUBSTRING_PREFIX)
1381                     .append("overview should have focus, using OverviewInputConsumer");
1382             return new OverviewInputConsumer(gestureState, container, mInputMonitorCompat,
1383                     false /* startingInActivityBounds */);
1384         } else {
1385             reasonString.append(SUBSTRING_PREFIX).append(
1386                     "overview shouldn't have focus, using OverviewWithoutFocusInputConsumer");
1387             final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
1388             return new OverviewWithoutFocusInputConsumer(container.asContext(), mDeviceState,
1389                     gestureState, mInputMonitorCompat, disableHorizontalSwipe);
1390         }
1391     }
1392 
1393     /**
1394      * To be called by the consumer when it's no longer active. This can be called by any consumer
1395      * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts
1396      * intercepting touches, the base consumer can try to call this).
1397      */
1398     private void onConsumerInactive(InputConsumer caller) {
1399         if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
1400             reset();
1401         }
1402     }
1403 
1404     private void reset() {
1405         mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
1406         mGestureState = DEFAULT_STATE;
1407         // By default, use batching of the input events, but check receiver before using in the rare
1408         // case that the monitor was disposed before the swipe settled
1409         if (mInputEventReceiver != null) {
1410             mInputEventReceiver.setBatchingEnabled(true);
1411         }
1412     }
1413 
1414     private @NonNull InputConsumer getDefaultInputConsumer() {
1415         return getDefaultInputConsumer(CompoundString.NO_OP);
1416     }
1417 
1418     /**
1419      * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
1420      */
1421     private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) {
1422         if (mResetGestureInputConsumer != null) {
1423             reasonString.append(SUBSTRING_PREFIX).append(
1424                     "mResetGestureInputConsumer initialized, using ResetGestureInputConsumer");
1425             return mResetGestureInputConsumer;
1426         } else {
1427             reasonString.append(SUBSTRING_PREFIX).append(
1428                     "mResetGestureInputConsumer not initialized, using no-op input consumer");
1429             // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
1430             // NO_OP until then (we never want these to be null).
1431             return InputConsumer.NO_OP;
1432         }
1433     }
1434 
1435     private void preloadOverview(boolean fromInit) {
1436         Trace.beginSection("preloadOverview(fromInit=" + fromInit + ")");
1437         preloadOverview(fromInit, false);
1438         Trace.endSection();
1439     }
1440 
1441     private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
1442         if (!LockedUserState.get(this).isUserUnlocked()) {
1443             return;
1444         }
1445 
1446         if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
1447             // Prevent the overview from being started before the real home on first boot.
1448             return;
1449         }
1450 
1451         if ((RestoreDbTask.isPending(this) && !forSUWAllSet)
1452                 || !mDeviceState.isUserSetupComplete()) {
1453             // Preloading while a restore is pending may cause launcher to start the restore
1454             // too early.
1455             return;
1456         }
1457 
1458         final BaseActivityInterface activityInterface =
1459                 mOverviewComponentObserver.getActivityInterface();
1460         final Intent overviewIntent = new Intent(
1461                 mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
1462         if (activityInterface.getCreatedContainer() != null && fromInit) {
1463             // The activity has been created before the initialization of overview service. It is
1464             // usually happens when booting or launcher is the top activity, so we should already
1465             // have the latest state.
1466             return;
1467         }
1468 
1469         // TODO(b/258022658): Remove temporary logging.
1470         Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet
1471                 + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame());
1472 
1473         ActiveGestureLog.INSTANCE.addLog("preloadRecentsAnimation");
1474         mTaskAnimationManager.preloadRecentsAnimation(overviewIntent);
1475     }
1476 
1477     @Override
1478     public void onConfigurationChanged(Configuration newConfig) {
1479         if (!LockedUserState.get(this).isUserUnlocked()) {
1480             return;
1481         }
1482         final BaseActivityInterface activityInterface =
1483                 mOverviewComponentObserver.getActivityInterface();
1484         final BaseDraggingActivity activity = activityInterface.getCreatedContainer();
1485         if (activity == null || activity.isStarted()) {
1486             // We only care about the existing background activity.
1487             return;
1488         }
1489         Configuration oldConfig = activity.getResources().getConfiguration();
1490         boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig);
1491         if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges(
1492                 activity.getComponentName(),
1493                 activity.getResources().getConfiguration().diff(newConfig))) {
1494             // Since navBar gestural height are different between portrait and landscape,
1495             // can handle orientation changes and refresh navigation gestural region through
1496             // onOneHandedModeChanged()
1497             int newGesturalHeight = ResourceUtils.getNavbarSize(
1498                     ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
1499                     getApplicationContext().getResources());
1500             mDeviceState.onOneHandedModeChanged(newGesturalHeight);
1501             return;
1502         }
1503 
1504         preloadOverview(false /* fromInit */);
1505     }
1506 
1507     private static boolean isTablet(Configuration config) {
1508         return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH;
1509     }
1510 
1511     @Override
1512     protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
1513         // Dump everything
1514         if (LockedUserState.get(this).isUserUnlocked()) {
1515             PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
1516         }
1517         mDeviceState.dump(pw);
1518         if (mOverviewComponentObserver != null) {
1519             mOverviewComponentObserver.dump(pw);
1520         }
1521         if (mOverviewCommandHelper != null) {
1522             mOverviewCommandHelper.dump(pw);
1523         }
1524         if (mGestureState != null) {
1525             mGestureState.dump("", pw);
1526         }
1527         pw.println("Input state:");
1528         pw.println("\tmInputMonitorCompat=" + mInputMonitorCompat);
1529         pw.println("\tmInputEventReceiver=" + mInputEventReceiver);
1530         DisplayController.INSTANCE.get(this).dump(pw);
1531         pw.println("TouchState:");
1532         BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
1533                 : mOverviewComponentObserver.getActivityInterface().getCreatedContainer();
1534         boolean resumed = mOverviewComponentObserver != null
1535                 && mOverviewComponentObserver.getActivityInterface().isResumed();
1536         pw.println("\tcreatedOverviewActivity=" + createdOverviewActivity);
1537         pw.println("\tresumed=" + resumed);
1538         pw.println("\tmConsumer=" + mConsumer.getName());
1539         ActiveGestureLog.INSTANCE.dump("", pw);
1540         RecentsModel.INSTANCE.get(this).dump("", pw);
1541         TopTaskTracker.INSTANCE.get(this).dump("", pw);
1542         if (mTaskAnimationManager != null) {
1543             mTaskAnimationManager.dump("", pw);
1544         }
1545         if (createdOverviewActivity != null) {
1546             createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
1547         }
1548         mTaskbarManager.dumpLogs("", pw);
1549         pw.println("AssistStateManager:");
1550         AssistStateManager.INSTANCE.get(this).dump("\t", pw);
1551         SystemUiProxy.INSTANCE.get(this).dump(pw);
1552         DeviceConfigWrapper.get().dump("   ", pw);
1553     }
1554 
1555     private AbsSwipeUpHandler createLauncherSwipeHandler(
1556             GestureState gestureState, long touchTimeMs) {
1557         return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
1558                 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
1559                 mInputConsumer);
1560     }
1561 
1562     private AbsSwipeUpHandler createFallbackSwipeHandler(
1563             GestureState gestureState, long touchTimeMs) {
1564         return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
1565                 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
1566                 mInputConsumer);
1567     }
1568 }
1569