• 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.quickstep;
17 
18 import static android.view.Display.DEFAULT_DISPLAY;
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.LauncherPrefs.backedUpItem;
29 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
30 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
31 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
32 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
33 import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
34 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
35 import static com.android.quickstep.GestureState.DEFAULT_STATE;
36 import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
37 import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
38 import static com.android.quickstep.InputConsumer.createNoOpInputConsumer;
39 import static com.android.quickstep.InputConsumerUtils.newConsumer;
40 import static com.android.quickstep.InputConsumerUtils.tryCreateAssistantInputConsumer;
41 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
42 
43 import android.app.PendingIntent;
44 import android.app.Service;
45 import android.content.Context;
46 import android.content.IIntentReceiver;
47 import android.content.IIntentSender;
48 import android.content.Intent;
49 import android.content.res.Configuration;
50 import android.graphics.Region;
51 import android.os.Bundle;
52 import android.os.IBinder;
53 import android.os.IRemoteCallback;
54 import android.os.Looper;
55 import android.os.RemoteException;
56 import android.os.SystemClock;
57 import android.util.Log;
58 import android.view.Choreographer;
59 import android.view.Display;
60 import android.view.InputDevice;
61 import android.view.InputEvent;
62 import android.view.MotionEvent;
63 import android.window.DesktopModeFlags;
64 
65 import androidx.annotation.BinderThread;
66 import androidx.annotation.NonNull;
67 import androidx.annotation.Nullable;
68 import androidx.annotation.UiThread;
69 import androidx.annotation.VisibleForTesting;
70 
71 import com.android.launcher3.ConstantItem;
72 import com.android.launcher3.EncryptionType;
73 import com.android.launcher3.Flags;
74 import com.android.launcher3.LauncherPrefs;
75 import com.android.launcher3.anim.AnimatedFloat;
76 import com.android.launcher3.desktop.DesktopAppLaunchTransitionManager;
77 import com.android.launcher3.statehandlers.DesktopVisibilityController;
78 import com.android.launcher3.statemanager.StatefulActivity;
79 import com.android.launcher3.taskbar.TaskbarActivityContext;
80 import com.android.launcher3.taskbar.TaskbarManager;
81 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
82 import com.android.launcher3.taskbar.bubbles.BubbleControllers;
83 import com.android.launcher3.testing.TestLogging;
84 import com.android.launcher3.testing.shared.ResourceUtils;
85 import com.android.launcher3.testing.shared.TestProtocol;
86 import com.android.launcher3.util.DisplayController;
87 import com.android.launcher3.util.LockedUserState;
88 import com.android.launcher3.util.MSDLPlayerWrapper;
89 import com.android.launcher3.util.NavigationMode;
90 import com.android.launcher3.util.PluginManagerWrapper;
91 import com.android.launcher3.util.SafeCloseable;
92 import com.android.launcher3.util.ScreenOnTracker;
93 import com.android.launcher3.util.TraceHelper;
94 import com.android.quickstep.OverviewCommandHelper.CommandType;
95 import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
96 import com.android.quickstep.fallback.window.RecentsDisplayModel;
97 import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
98 import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
99 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
100 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
101 import com.android.quickstep.util.ActiveGestureLog;
102 import com.android.quickstep.util.ActiveGestureLog.CompoundString;
103 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
104 import com.android.quickstep.util.ActiveTrackpadList;
105 import com.android.quickstep.util.ActivityPreloadUtil;
106 import com.android.quickstep.util.ContextualSearchInvoker;
107 import com.android.quickstep.util.ContextualSearchStateManager;
108 import com.android.quickstep.views.RecentsViewContainer;
109 import com.android.systemui.shared.recents.ILauncherProxy;
110 import com.android.systemui.shared.recents.ISystemUiProxy;
111 import com.android.systemui.shared.statusbar.phone.BarTransitions;
112 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
113 import com.android.systemui.shared.system.InputConsumerController;
114 import com.android.systemui.shared.system.InputMonitorCompat;
115 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
116 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
117 import com.android.systemui.unfold.progress.IUnfoldAnimation;
118 import com.android.wm.shell.back.IBackAnimation;
119 import com.android.wm.shell.bubbles.IBubbles;
120 import com.android.wm.shell.common.pip.IPip;
121 import com.android.wm.shell.desktopmode.IDesktopMode;
122 import com.android.wm.shell.draganddrop.IDragAndDrop;
123 import com.android.wm.shell.onehanded.IOneHanded;
124 import com.android.wm.shell.recents.IRecentTasks;
125 import com.android.wm.shell.shared.IShellTransitions;
126 import com.android.wm.shell.splitscreen.ISplitScreen;
127 import com.android.wm.shell.startingsurface.IStartingWindow;
128 
129 import java.io.FileDescriptor;
130 import java.io.PrintWriter;
131 import java.lang.ref.WeakReference;
132 import java.util.Locale;
133 import java.util.function.Consumer;
134 import java.util.function.Function;
135 
136 /**
137  * Service connected by system-UI for handling touch interaction.
138  */
139 public class TouchInteractionService extends Service {
140 
141     private static final String SUBSTRING_PREFIX = "; ";
142 
143     private static final String TAG = "TouchInteractionService";
144 
145     private static final ConstantItem<Boolean> HAS_ENABLED_QUICKSTEP_ONCE = backedUpItem(
146             "launcher.has_enabled_quickstep_once", false, EncryptionType.ENCRYPTED);
147 
148     private static final DesktopModeFlags.DesktopModeFlag ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS =
149             new DesktopModeFlags.DesktopModeFlag(Flags::enableGestureNavOnConnectedDisplays, false);
150 
151     private final TISBinder mTISBinder = new TISBinder(this);
152 
153     /**
154      * Local ILauncherProxy implementation with some methods for local components
155      */
156     public static class TISBinder extends ILauncherProxy.Stub {
157 
158         private final WeakReference<TouchInteractionService> mTis;
159 
TISBinder(TouchInteractionService tis)160         private TISBinder(TouchInteractionService tis) {
161             mTis = new WeakReference<>(tis);
162         }
163 
164         @BinderThread
onInitialize(Bundle bundle)165         public void onInitialize(Bundle bundle) {
166             ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
167                     bundle.getBinder(ISystemUiProxy.DESCRIPTOR));
168             IPip pip = IPip.Stub.asInterface(bundle.getBinder(IPip.DESCRIPTOR));
169             IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(IBubbles.DESCRIPTOR));
170             ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
171                     ISplitScreen.DESCRIPTOR));
172             IOneHanded onehanded = IOneHanded.Stub.asInterface(
173                     bundle.getBinder(IOneHanded.DESCRIPTOR));
174             IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
175                     bundle.getBinder(IShellTransitions.DESCRIPTOR));
176             IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
177                     bundle.getBinder(IStartingWindow.DESCRIPTOR));
178             ISysuiUnlockAnimationController launcherUnlockAnimationController =
179                     ISysuiUnlockAnimationController.Stub.asInterface(
180                             bundle.getBinder(ISysuiUnlockAnimationController.DESCRIPTOR));
181             IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
182                     bundle.getBinder(IRecentTasks.DESCRIPTOR));
183             IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
184                     bundle.getBinder(IBackAnimation.DESCRIPTOR));
185             IDesktopMode desktopMode = IDesktopMode.Stub.asInterface(
186                     bundle.getBinder(IDesktopMode.DESCRIPTOR));
187             IUnfoldAnimation unfoldTransition = IUnfoldAnimation.Stub.asInterface(
188                     bundle.getBinder(IUnfoldAnimation.DESCRIPTOR));
189             IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface(
190                     bundle.getBinder(IDragAndDrop.DESCRIPTOR));
191             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
192                 SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip,
193                         bubbles, splitscreen, onehanded, shellTransitions, startingWindow,
194                         recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode,
195                         unfoldTransition, dragAndDrop);
196                 tis.initInputMonitor("TISBinder#onInitialize()");
197                 ActivityPreloadUtil.preloadOverviewForTIS(tis, true /* fromInit */);
198             }));
199         }
200 
201         @BinderThread
202         @Override
onTaskbarToggled()203         public void onTaskbarToggled() {
204             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
205                 TaskbarActivityContext activityContext =
206                         tis.mTaskbarManager.getCurrentActivityContext();
207 
208                 if (activityContext != null) {
209                     activityContext.toggleTaskbarStash();
210                 }
211             }));
212         }
213 
214         @BinderThread
onOverviewToggle()215         public void onOverviewToggle() {
216             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
217             executeForTouchInteractionService(tis -> {
218                 // If currently screen pinning, do not enter overview
219                 if (tis.mDeviceState.isScreenPinningActive()) {
220                     return;
221                 }
222                 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
223                 tis.mOverviewCommandHelper.addCommand(CommandType.TOGGLE);
224             });
225         }
226 
227         @BinderThread
228         @Override
onOverviewShown(boolean triggeredFromAltTab)229         public void onOverviewShown(boolean triggeredFromAltTab) {
230             executeForTouchInteractionService(tis -> {
231                 if (triggeredFromAltTab) {
232                     TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
233                     tis.mOverviewCommandHelper.addCommand(CommandType.KEYBOARD_INPUT);
234                 } else {
235                     tis.mOverviewCommandHelper.addCommand(CommandType.SHOW);
236                 }
237             });
238         }
239 
240         @BinderThread
241         @Override
onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)242         public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
243             executeForTouchInteractionService(tis -> {
244                 if (triggeredFromAltTab && !triggeredFromHomeKey) {
245                     // onOverviewShownFromAltTab hides the overview and ends at the target app
246                     tis.mOverviewCommandHelper.addCommand(CommandType.HIDE);
247                 }
248             });
249         }
250 
251         @BinderThread
252         @Override
onAssistantAvailable(boolean available, boolean longPressHomeEnabled)253         public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) {
254             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
255                 tis.mDeviceState.setAssistantAvailable(available);
256                 tis.onAssistantVisibilityChanged();
257                 executeForTaskbarManager(taskbarManager -> taskbarManager
258                         .onLongPressHomeEnabled(longPressHomeEnabled));
259             }));
260         }
261 
262         @BinderThread
263         @Override
onAssistantVisibilityChanged(float visibility)264         public void onAssistantVisibilityChanged(float visibility) {
265             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
266                 tis.mDeviceState.setAssistantVisibility(visibility);
267                 tis.onAssistantVisibilityChanged();
268             }));
269         }
270 
271         /**
272          * Sent when the assistant has been invoked with the given type (defined in AssistManager)
273          * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested
274          * was previously called including this invocation type.
275          */
276         @Override
onAssistantOverrideInvoked(int invocationType)277         public void onAssistantOverrideInvoked(int invocationType) {
278             executeForTouchInteractionService(tis -> {
279                 if (!new ContextualSearchInvoker(tis).tryStartAssistOverride(invocationType)) {
280                     Log.w(TAG, "Failed to invoke Assist override");
281                 }
282             });
283         }
284 
285         @BinderThread
onSystemUiStateChanged(@ystemUiStateFlags long stateFlags, int displayId)286         public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags, int displayId) {
287             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
288                 // Last flags is only used for the default display case.
289                 long lastFlags = tis.mDeviceState.getSysuiStateFlag();
290                 tis.mDeviceState.setSysUIStateFlagsForDisplay(stateFlags, displayId);
291                 tis.onSystemUiFlagsChanged(lastFlags, displayId);
292             }));
293         }
294 
295         @BinderThread
onActiveNavBarRegionChanges(Region region)296         public void onActiveNavBarRegionChanges(Region region) {
297             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
298                     tis -> tis.mDeviceState.setDeferredGestureRegion(region)));
299         }
300 
301         @BinderThread
302         @Override
enterStageSplitFromRunningApp(boolean leftOrTop)303         public void enterStageSplitFromRunningApp(boolean leftOrTop) {
304             executeForTouchInteractionService(tis -> {
305                 // TODO (b/397942185): support external displays
306                 RecentsViewContainer container = tis.mOverviewComponentObserver
307                         .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer();
308                 if (container != null) {
309                     container.enterStageSplitFromRunningApp(leftOrTop);
310                 }
311             });
312         }
313 
314         @BinderThread
315         @Override
onDisplayAddSystemDecorations(int displayId)316         public void onDisplayAddSystemDecorations(int displayId) {
317             executeForTouchInteractionService(tis ->
318                     tis.mSystemDecorationChangeObserver.notifyAddSystemDecorations(displayId));
319         }
320 
321         @BinderThread
322         @Override
onDisplayRemoved(int displayId)323         public void onDisplayRemoved(int displayId) {
324             executeForTouchInteractionService(tis -> {
325                 tis.mSystemDecorationChangeObserver.notifyOnDisplayRemoved(displayId);
326                 tis.mDeviceState.clearSysUIStateFlagsForDisplay(displayId);
327             });
328         }
329 
330         @BinderThread
331         @Override
onDisplayRemoveSystemDecorations(int displayId)332         public void onDisplayRemoveSystemDecorations(int displayId) {
333             executeForTouchInteractionService(tis -> {
334                 tis.mSystemDecorationChangeObserver.notifyDisplayRemoveSystemDecorations(displayId);
335             });
336         }
337 
338         @BinderThread
339         @Override
updateWallpaperVisibility(int displayId, boolean visible)340         public void updateWallpaperVisibility(int displayId, boolean visible) {
341             executeForTaskbarManager(taskbarManager ->
342                     taskbarManager.setWallpaperVisible(displayId, visible));
343         }
344 
345         @BinderThread
346         @Override
checkNavBarModes(int displayId)347         public void checkNavBarModes(int displayId) {
348             executeForTaskbarManager(taskbarManager ->
349                     taskbarManager.checkNavBarModes(displayId));
350         }
351 
352         @BinderThread
353         @Override
finishBarAnimations(int displayId)354         public void finishBarAnimations(int displayId) {
355             executeForTaskbarManager(taskbarManager ->
356                     taskbarManager.finishBarAnimations(displayId));
357         }
358 
359         @BinderThread
360         @Override
touchAutoDim(int displayId, boolean reset)361         public void touchAutoDim(int displayId, boolean reset) {
362             executeForTaskbarManager(taskbarManager ->
363                     taskbarManager.touchAutoDim(displayId, reset));
364         }
365 
366         @BinderThread
367         @Override
transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate)368         public void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode,
369                 boolean animate) {
370             executeForTaskbarManager(taskbarManager ->
371                     taskbarManager.transitionTo(displayId, barMode, animate));
372         }
373 
374         @BinderThread
375         @Override
appTransitionPending(boolean pending)376         public void appTransitionPending(boolean pending) {
377             executeForTaskbarManager(taskbarManager ->
378                     taskbarManager.appTransitionPending(pending));
379         }
380 
381         @Override
onRotationProposal(int rotation, boolean isValid)382         public void onRotationProposal(int rotation, boolean isValid) {
383             executeForTaskbarManager(taskbarManager ->
384                     taskbarManager.onRotationProposal(rotation, isValid));
385         }
386 
387         @Override
disable(int displayId, int state1, int state2, boolean animate)388         public void disable(int displayId, int state1, int state2, boolean animate) {
389             executeForTaskbarManager(taskbarManager ->
390                     taskbarManager.disableNavBarElements(displayId, state1, state2, animate));
391         }
392 
393         @Override
onSystemBarAttributesChanged(int displayId, int behavior)394         public void onSystemBarAttributesChanged(int displayId, int behavior) {
395             executeForTaskbarManager(taskbarManager ->
396                     taskbarManager.onSystemBarAttributesChanged(displayId, behavior));
397         }
398 
399         @Override
onTransitionModeUpdated(int barMode, boolean checkBarModes)400         public void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
401             executeForTaskbarManager(taskbarManager ->
402                     taskbarManager.onTransitionModeUpdated(barMode, checkBarModes));
403         }
404 
405         @Override
onNavButtonsDarkIntensityChanged(float darkIntensity)406         public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
407             executeForTaskbarManager(taskbarManager ->
408                     taskbarManager.onNavButtonsDarkIntensityChanged(darkIntensity));
409         }
410 
411         @Override
onNavigationBarLumaSamplingEnabled(int displayId, boolean enable)412         public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
413             executeForTaskbarManager(taskbarManager ->
414                     taskbarManager.onNavigationBarLumaSamplingEnabled(displayId, enable));
415         }
416 
417         @Override
onUnbind(IRemoteCallback reply)418         public void onUnbind(IRemoteCallback reply) {
419             // Run everything in the same main thread block to ensure the cleanup happens before
420             // sending the reply.
421             MAIN_EXECUTOR.execute(() -> {
422                 executeForTaskbarManager(TaskbarManager::destroy);
423                 try {
424                     reply.sendResult(null);
425                 } catch (RemoteException e) {
426                     Log.w(TAG, "onUnbind: Failed to reply to LauncherProxyService", e);
427                 }
428             });
429         }
430 
executeForTouchInteractionService( @onNull Consumer<TouchInteractionService> tisConsumer)431         private void executeForTouchInteractionService(
432                 @NonNull Consumer<TouchInteractionService> tisConsumer) {
433             TouchInteractionService tis = mTis.get();
434             if (tis == null) return;
435             tisConsumer.accept(tis);
436         }
437 
executeForTaskbarManager( @onNull Consumer<TaskbarManager> taskbarManagerConsumer)438         private void executeForTaskbarManager(
439                 @NonNull Consumer<TaskbarManager> taskbarManagerConsumer) {
440             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
441                 TaskbarManager taskbarManager = tis.mTaskbarManager;
442                 if (taskbarManager == null) return;
443                 taskbarManagerConsumer.accept(taskbarManager);
444             }));
445         }
446 
447         /**
448          * Returns the {@link TaskbarManager}.
449          * <p>
450          * Returns {@code null} if TouchInteractionService is not connected
451          */
452         @Nullable
getTaskbarManager()453         public TaskbarManager getTaskbarManager() {
454             TouchInteractionService tis = mTis.get();
455             if (tis == null) return null;
456             return tis.mTaskbarManager;
457         }
458 
459         @VisibleForTesting
injectFakeTrackpadForTesting()460         public void injectFakeTrackpadForTesting() {
461             TouchInteractionService tis = mTis.get();
462             if (tis == null) return;
463             tis.mTrackpadsConnected.add(1000);
464             tis.initInputMonitor("tapl testing");
465         }
466 
467         @VisibleForTesting
ejectFakeTrackpadForTesting()468         public void ejectFakeTrackpadForTesting() {
469             TouchInteractionService tis = mTis.get();
470             if (tis == null) return;
471             tis.mTrackpadsConnected.clear();
472             // This method destroys the current input monitor if set up, and only init a new one
473             // in 3-button mode if {@code mTrackpadsConnected} is not empty. So in other words,
474             // it will destroy the input monitor.
475             tis.initInputMonitor("tapl testing");
476         }
477 
478         /**
479          * Sets whether a predictive back-to-home animation is in progress in the device state
480          */
setPredictiveBackToHomeInProgress(boolean isInProgress)481         public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
482             executeForTouchInteractionService(tis ->
483                     tis.mDeviceState.setPredictiveBackToHomeInProgress(isInProgress));
484         }
485 
486         /**
487          * Returns the {@link OverviewCommandHelper}.
488          * <p>
489          * Returns {@code null} if TouchInteractionService is not connected
490          */
491         @Nullable
getOverviewCommandHelper()492         public OverviewCommandHelper getOverviewCommandHelper() {
493             TouchInteractionService tis = mTis.get();
494             if (tis == null) return null;
495             return tis.mOverviewCommandHelper;
496         }
497 
498         /**
499          * Sets a proxy to bypass swipe up behavior
500          */
setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy)501         public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) {
502             executeForTouchInteractionService(
503                     tis -> tis.mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null));
504         }
505 
506         /**
507          * Sets the task id where gestures should be blocked
508          */
setGestureBlockedTaskId(int taskId)509         public void setGestureBlockedTaskId(int taskId) {
510             executeForTouchInteractionService(
511                     tis -> tis.mDeviceState.setGestureBlockingTaskId(taskId));
512         }
513 
514         /** Refreshes the current overview target. */
refreshOverviewTarget()515         public void refreshOverviewTarget() {
516             executeForTouchInteractionService(tis -> {
517                 tis.mAllAppsActionManager.onDestroy();
518                 tis.onOverviewTargetChanged(tis.mOverviewComponentObserver.isHomeAndOverviewSame());
519             });
520         }
521     }
522 
523     private RotationTouchHelper mRotationTouchHelper;
524 
525     private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
526             this::createLauncherSwipeHandler;
527     private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
528             this::createFallbackSwipeHandler;
529     private final AbsSwipeUpHandler.Factory mRecentsWindowSwipeHandlerFactory =
530             this::createRecentsWindowSwipeHandler;
531     // This needs to be a member to be queued and potentially removed later if the service is
532     // destroyed before the user is unlocked
533     private final Runnable mUserUnlockedRunnable = this::onUserUnlocked;
534 
535     private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
536     private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
537 
538     private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() {
539         @Override
540         public void onNavigateHome() {
541             mOverviewCommandHelper.addCommand(CommandType.HOME);
542         }
543 
544         @Override
545         public void onToggleOverview() {
546             mOverviewCommandHelper.addCommand(CommandType.TOGGLE);
547         }
548 
549         @Override
550         public void onHideOverview() {
551             mOverviewCommandHelper.addCommand(CommandType.HIDE);
552         }
553     };
554 
555     private OverviewCommandHelper mOverviewCommandHelper;
556     private OverviewComponentObserver mOverviewComponentObserver;
557     private InputConsumerController mInputConsumer;
558     private RecentsAnimationDeviceState mDeviceState;
559 
560     private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.DEFAULT_NO_OP;
561     private @NonNull InputConsumer mConsumer = InputConsumer.DEFAULT_NO_OP;
562     private Choreographer mMainChoreographer;
563     private boolean mUserUnlocked = false;
564     private GestureState mGestureState = DEFAULT_STATE;
565 
566     private InputMonitorDisplayModel mInputMonitorDisplayModel;
567     private InputMonitorCompat mInputMonitorCompat;
568     private InputEventReceiver mInputEventReceiver;
569 
570     private TaskbarManager mTaskbarManager;
571     private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
572     private AllAppsActionManager mAllAppsActionManager;
573     private ActiveTrackpadList mTrackpadsConnected;
574 
575     private NavigationMode mGestureStartNavMode = null;
576 
577     private DesktopAppLaunchTransitionManager mDesktopAppLaunchTransitionManager;
578 
579     private DisplayController.DisplayInfoChangeListener mDisplayInfoChangeListener;
580 
581     private RecentsDisplayModel mRecentsDisplayModel;
582 
583     private SystemDecorationChangeObserver mSystemDecorationChangeObserver;
584 
585     @Override
onCreate()586     public void onCreate() {
587         super.onCreate();
588         Log.d(TAG, "onCreate: user=" + getUserId()
589                 + " instance=" + System.identityHashCode(this));
590         // Initialize anything here that is needed in direct boot mode.
591         // Everything else should be initialized in onUserUnlocked() below.
592         mMainChoreographer = Choreographer.getInstance();
593         mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this);
594         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
595         mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this);
596         mSystemDecorationChangeObserver = SystemDecorationChangeObserver.getINSTANCE().get(this);
597         mAllAppsActionManager = new AllAppsActionManager(
598                 this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
599         mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
600             if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
601                 // Don't destroy and reinitialize input monitor due to trackpad
602                 // connecting when it's already set up.
603                 return;
604             }
605             initInputMonitor("onTrackpadConnected()");
606         });
607 
608         mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks,
609                 mRecentsDisplayModel);
610         mDesktopAppLaunchTransitionManager =
611                 new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this));
612         mDesktopAppLaunchTransitionManager.registerTransitions();
613         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
614 
615         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
616         LockedUserState.get(this).runOnUserUnlocked(mUserUnlockedRunnable);
617         mDisplayInfoChangeListener =
618                 mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
619         ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
620     }
621 
622     @Nullable
getInputEventReceiver(int displayId)623     private InputEventReceiver getInputEventReceiver(int displayId) {
624         if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
625             InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null
626                     ? null : mInputMonitorDisplayModel.getDisplayResource(displayId);
627             return inputMonitorResource == null ? null : inputMonitorResource.inputEventReceiver;
628         }
629         return mInputEventReceiver;
630     }
631 
632     @Nullable
getInputMonitorCompat(int displayId)633     private InputMonitorCompat getInputMonitorCompat(int displayId) {
634         if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
635             InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null
636                     ? null : mInputMonitorDisplayModel.getDisplayResource(displayId);
637             return inputMonitorResource == null ? null : inputMonitorResource.inputMonitorCompat;
638         }
639         return mInputMonitorCompat;
640     }
641 
disposeEventHandlers(String reason)642     private void disposeEventHandlers(String reason) {
643         Log.d(TAG, "disposeEventHandlers: Reason: " + reason
644                 + " instance=" + System.identityHashCode(this));
645         if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
646             if (mInputMonitorDisplayModel == null) return;
647             mInputMonitorDisplayModel.destroy();
648             return;
649         }
650         if (mInputEventReceiver != null) {
651             mInputEventReceiver.dispose();
652             mInputEventReceiver = null;
653         }
654         if (mInputMonitorCompat != null) {
655             mInputMonitorCompat.dispose();
656             mInputMonitorCompat = null;
657         }
658     }
659 
initInputMonitor(String reason)660     private void initInputMonitor(String reason) {
661         disposeEventHandlers("Initializing input monitor due to: " + reason);
662 
663         if (mDeviceState.isButtonNavMode()
664                 && !mDeviceState.supportsAssistantGestureInButtonNav()
665                 && (mTrackpadsConnected.isEmpty())) {
666             return;
667         }
668         if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
669             mInputMonitorDisplayModel = new InputMonitorDisplayModel(this);
670         } else {
671             mInputMonitorCompat = new InputMonitorCompat("swipe-up", DEFAULT_DISPLAY);
672             mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
673                     mMainChoreographer, this::onInputEvent);
674         }
675 
676         mRotationTouchHelper.updateGestureTouchRegions();
677     }
678 
679     /**
680      * Called when the navigation mode changes, guaranteed to be after the device state has updated.
681      */
onNavigationModeChanged()682     private void onNavigationModeChanged() {
683         initInputMonitor("onNavigationModeChanged()");
684         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
685     }
686 
687     @UiThread
onUserUnlocked()688     public void onUserUnlocked() {
689         Log.d(TAG, "onUserUnlocked: userId=" + getUserId()
690                 + " instance=" + System.identityHashCode(this));
691         mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
692         mOverviewCommandHelper = new OverviewCommandHelper(this,
693                 mOverviewComponentObserver, mRecentsDisplayModel,
694                 SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager);
695         mUserUnlocked = true;
696         mInputConsumer.registerInputConsumer();
697         for (int displayId : mDeviceState.getDisplaysWithSysUIState()) {
698             onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags(displayId), displayId);
699         }
700         onAssistantVisibilityChanged();
701 
702         // Initialize the task tracker
703         TopTaskTracker.INSTANCE.get(this);
704 
705         // Temporarily disable model preload
706         // new ModelPreload().start(this);
707         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
708 
709         mOverviewComponentObserver.addOverviewChangeListener(mOverviewChangeListener);
710         onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame());
711 
712         mTaskbarManager.onUserUnlocked();
713     }
714 
getOverviewCommandHelper()715     public OverviewCommandHelper getOverviewCommandHelper() {
716         return mOverviewCommandHelper;
717     }
718 
resetHomeBounceSeenOnQuickstepEnabledFirstTime()719     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
720         if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
721             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
722             // mode doesn't have gestures
723             return;
724         }
725 
726         // Reset home bounce seen on quick step enabled for first time
727         LauncherPrefs prefs = LauncherPrefs.get(this);
728         if (!prefs.get(HAS_ENABLED_QUICKSTEP_ONCE)) {
729             prefs.put(
730                     HAS_ENABLED_QUICKSTEP_ONCE.to(true),
731                     HOME_BOUNCE_SEEN.to(false));
732         }
733     }
734 
onOverviewTargetChanged(boolean isHomeAndOverviewSame)735     private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) {
736         mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
737         // TODO (b/399089118): how will this work with per-display Taskbars? Is using the
738         //  default-display container ok?
739         RecentsViewContainer newOverviewContainer =
740                 mOverviewComponentObserver.getContainerInterface(
741                         DEFAULT_DISPLAY).getCreatedContainer();
742         if (newOverviewContainer != null) {
743             if (newOverviewContainer instanceof StatefulActivity activity) {
744                 // This will also call setRecentsViewContainer() internally.
745                 mTaskbarManager.setActivity(activity);
746             } else {
747                 mTaskbarManager.setRecentsViewContainer(newOverviewContainer);
748             }
749         }
750     }
751 
createAllAppsPendingIntent()752     private PendingIntent createAllAppsPendingIntent() {
753         return new PendingIntent(new IIntentSender.Stub() {
754             @Override
755             public void send(int code, Intent intent, String resolvedType,
756                     IBinder allowlistToken, IIntentReceiver finishedReceiver,
757                     String requiredPermission, Bundle options) {
758                 MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllAppsSearch());
759             }
760         });
761     }
762 
763     @UiThread
764     private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags, int displayId) {
765         if (LockedUserState.get(this).isUserUnlocked()) {
766             long systemUiStateFlags = mDeviceState.getSystemUiStateFlags(displayId);
767             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags, displayId);
768             if (displayId == DEFAULT_DISPLAY) {
769                 // The following don't care about non-default displays, at least for now. If they
770                 // ever will, they should be taken care of.
771                 SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
772                 mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled());
773                 // TODO b/399371607 - Propagate to taskAnimationManager once overview is multi
774                 //  display.
775                 TaskAnimationManager taskAnimationManager =
776                         mRecentsDisplayModel.getTaskAnimationManager(displayId);
777                 if (taskAnimationManager != null) {
778                     taskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
779                 }
780             }
781         }
782     }
783 
784     @UiThread
785     private void onAssistantVisibilityChanged() {
786         if (LockedUserState.get(this).isUserUnlocked()) {
787             mOverviewComponentObserver.getContainerInterface(
788                     DEFAULT_DISPLAY).onAssistantVisibilityChanged(
789                     mDeviceState.getAssistantVisibility());
790         }
791     }
792 
793     @Override
794     public void onDestroy() {
795         Log.d(TAG, "onDestroy: user=" + getUserId()
796                 + " instance=" + System.identityHashCode(this));
797         if (LockedUserState.get(this).isUserUnlocked()) {
798             mInputConsumer.unregisterInputConsumer();
799             mOverviewComponentObserver.setHomeDisabled(false);
800             mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener);
801         }
802         disposeEventHandlers("TouchInteractionService onDestroy()");
803         SystemUiProxy.INSTANCE.get(this).clearProxy();
804 
805         mAllAppsActionManager.onDestroy();
806 
807         mTrackpadsConnected.destroy();
808         mTaskbarManager.destroy();
809         if (mDesktopAppLaunchTransitionManager != null) {
810             mDesktopAppLaunchTransitionManager.unregisterTransitions();
811         }
812         mDesktopAppLaunchTransitionManager = null;
813         mDeviceState.removeDisplayInfoChangeListener(mDisplayInfoChangeListener);
814         LockedUserState.get(this).removeOnUserUnlockedRunnable(mUserUnlockedRunnable);
815         ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
816         super.onDestroy();
817     }
818 
819     @Override
820     public IBinder onBind(Intent intent) {
821         Log.d(TAG, "onBind: user=" + getUserId()
822                 + " instance=" + System.identityHashCode(this));
823         return mTISBinder;
824     }
825 
826     protected void onScreenOnChanged(boolean isOn) {
827         if (isOn) {
828             return;
829         }
830         long currentTime = SystemClock.uptimeMillis();
831         MotionEvent cancelEvent = MotionEvent.obtain(
832                 currentTime, currentTime, ACTION_CANCEL, 0f, 0f, 0);
833         onInputEvent(cancelEvent);
834         cancelEvent.recycle();
835     }
836 
837     private void onInputEvent(InputEvent ev) {
838         int displayId = ev.getDisplayId();
839         if (!(ev instanceof MotionEvent)) {
840             ActiveGestureProtoLogProxy.logUnknownInputEvent(displayId, ev.toString());
841             return;
842         }
843         MotionEvent event = (MotionEvent) ev;
844 
845         TestLogging.recordMotionEvent(
846                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
847 
848         if (!LockedUserState.get(this).isUserUnlocked()) {
849             ActiveGestureProtoLogProxy.logOnInputEventUserLocked(displayId);
850             return;
851         }
852 
853         NavigationMode currentNavMode = mDeviceState.getMode();
854         if (mGestureStartNavMode != null && mGestureStartNavMode != currentNavMode) {
855             ActiveGestureProtoLogProxy.logOnInputEventNavModeSwitched(
856                     displayId, mGestureStartNavMode.name(), currentNavMode.name());
857             event.setAction(ACTION_CANCEL);
858         } else if (mDeviceState.isButtonNavMode()
859                 && !mDeviceState.supportsAssistantGestureInButtonNav()
860                 && !isTrackpadMotionEvent(event)) {
861             ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav(displayId);
862             return;
863         }
864 
865         final int action = event.getActionMasked();
866         // Note this will create a new consumer every mouse click, as after ACTION_UP from the click
867         // an ACTION_HOVER_ENTER will fire as well.
868         boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
869                 && isHoverActionWithoutConsumer(event);
870 
871         TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
872                 displayId);
873         if (taskAnimationManager == null) {
874             Log.e(TAG, "TaskAnimationManager not available for displayId " + displayId);
875             ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(displayId);
876             return;
877         }
878         if (enableHandleDelayedGestureCallbacks()) {
879             if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
880                 taskAnimationManager.notifyNewGestureStart();
881             }
882             if (taskAnimationManager.shouldIgnoreMotionEvents()) {
883                 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
884                     ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents(displayId);
885                 }
886                 return;
887             }
888         }
889 
890         InputMonitorCompat inputMonitorCompat = getInputMonitorCompat(displayId);
891         InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId);
892 
893         if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
894             mGestureStartNavMode = currentNavMode;
895         } else if (action == ACTION_UP || action == ACTION_CANCEL) {
896             mGestureStartNavMode = null;
897         }
898 
899         SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
900 
901         CompoundString reasonString = action == ACTION_DOWN
902                 ? CompoundString.newEmptyString() : CompoundString.NO_OP;
903         if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
904             mRotationTouchHelper.setOrientationTransformIfNeeded(event);
905 
906             boolean isOneHandedModeActive = mDeviceState.isOneHandedModeActive();
907             boolean isInSwipeUpTouchRegion = mRotationTouchHelper.isInSwipeUpTouchRegion(event);
908             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
909             BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null;
910             boolean isOnBubbles = bubbleControllers != null
911                     && BubbleBarInputConsumer.isEventOnBubbles(tac, event);
912             if (mDeviceState.isButtonNavMode()
913                     && mDeviceState.supportsAssistantGestureInButtonNav()) {
914                 reasonString.append("in three button mode which supports Assistant gesture");
915                 // Consume gesture event for Assistant (all other gestures should do nothing).
916                 if (mDeviceState.canTriggerAssistantAction(event)) {
917                     reasonString.append(" and event can trigger assistant action, "
918                             + "consuming gesture for assistant action");
919                     mGestureState = createGestureState(
920                             displayId, mGestureState, getTrackpadGestureType(event));
921                     mUncheckedConsumer = tryCreateAssistantInputConsumer(
922                             this,
923                             mDeviceState,
924                             inputMonitorCompat,
925                             mGestureState,
926                             event);
927                 } else {
928                     reasonString.append(" but event cannot trigger Assistant, "
929                             + "consuming gesture as no-op");
930                     mUncheckedConsumer = createNoOpInputConsumer(displayId);
931                 }
932             } else if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
933                     || isHoverActionWithoutConsumer || isOnBubbles) {
934                 reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion
935                         ? "one handed mode is not active and event is in swipe up region, "
936                                 + "creating new input consumer"
937                         : "isHoverActionWithoutConsumer == true, creating new input consumer");
938                 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
939                 // onConsumerInactive and wipe the previous gesture state
940                 GestureState prevGestureState = new GestureState(mGestureState);
941                 GestureState newGestureState = createGestureState(
942                         displayId, mGestureState, getTrackpadGestureType(event));
943                 mConsumer.onConsumerAboutToBeSwitched();
944                 mGestureState = newGestureState;
945                 mConsumer = newConsumer(
946                         this,
947                         mUserUnlocked,
948                         mOverviewComponentObserver,
949                         mDeviceState,
950                         prevGestureState,
951                         mGestureState,
952                         taskAnimationManager,
953                         inputMonitorCompat,
954                         getSwipeUpHandlerFactory(displayId),
955                         this::onConsumerInactive,
956                         inputEventReceiver,
957                         mTaskbarManager,
958                         mSwipeUpProxyProvider,
959                         mOverviewCommandHelper,
960                         event);
961                 mUncheckedConsumer = mConsumer;
962             } else if ((mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event))
963                     && mDeviceState.canTriggerAssistantAction(event)) {
964                 reasonString.append(mDeviceState.isFullyGesturalNavMode()
965                         ? "using fully gestural nav and event can trigger assistant action, "
966                                 + "consuming gesture for assistant action"
967                         : "event is a trackpad multi-finger swipe and event can trigger assistant "
968                                 + "action, consuming gesture for assistant action");
969                 mGestureState = createGestureState(
970                         displayId, mGestureState, getTrackpadGestureType(event));
971                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
972                 // should not interrupt it. QuickSwitch assumes that interruption can only
973                 // happen if the next gesture is also quick switch.
974                 mUncheckedConsumer = tryCreateAssistantInputConsumer(
975                         this, mDeviceState, inputMonitorCompat, mGestureState, event);
976             } else if (mDeviceState.canTriggerOneHandedAction(event)) {
977                 reasonString.append("event can trigger one-handed action, "
978                         + "consuming gesture for one-handed action");
979                 // Consume gesture event for triggering one handed feature.
980                 mUncheckedConsumer = new OneHandedModeInputConsumer(
981                         this,
982                         displayId,
983                         mDeviceState,
984                         InputConsumer.createNoOpInputConsumer(displayId), inputMonitorCompat);
985             } else {
986                 mUncheckedConsumer = InputConsumer.createNoOpInputConsumer(displayId);
987             }
988         } else {
989             // Other events
990             if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) {
991                 // Only transform the event if we are handling it in a proper consumer
992                 mRotationTouchHelper.setOrientationTransformIfNeeded(event);
993             }
994         }
995 
996         if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) {
997             switch (action) {
998                 case ACTION_DOWN:
999                     ActiveGestureProtoLogProxy.logOnInputEventActionDown(displayId, reasonString);
1000                     // fall through
1001                 case ACTION_UP:
1002                     ActiveGestureProtoLogProxy.logOnInputEventActionUp(
1003                             (int) event.getRawX(),
1004                             (int) event.getRawY(),
1005                             action,
1006                             MotionEvent.classificationToString(event.getClassification()),
1007                             displayId);
1008                     break;
1009                 case ACTION_MOVE:
1010                     ActiveGestureProtoLogProxy.logOnInputEventActionMove(
1011                             MotionEvent.actionToString(action),
1012                             MotionEvent.classificationToString(event.getClassification()),
1013                             event.getPointerCount(),
1014                             displayId);
1015                     break;
1016                 default: {
1017                     ActiveGestureProtoLogProxy.logOnInputEventGenericAction(
1018                             MotionEvent.actionToString(action),
1019                             MotionEvent.classificationToString(event.getClassification()),
1020                             displayId);
1021                 }
1022             }
1023         }
1024 
1025         boolean cancelGesture = mGestureState.getContainerInterface() != null
1026                 && mGestureState.getContainerInterface().shouldCancelCurrentGesture();
1027         boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
1028                 && mConsumer != null
1029                 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
1030         if (cancelGesture) {
1031             event.setAction(ACTION_CANCEL);
1032         }
1033 
1034         if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN
1035                 || action == ACTION_POINTER_UP)) {
1036             // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad.
1037         } else if (isCursorHoverEvent(event)) {
1038             mUncheckedConsumer.onHoverEvent(event);
1039         } else {
1040             mUncheckedConsumer.onMotionEvent(event);
1041         }
1042 
1043         if (cleanUpConsumer) {
1044             reset(displayId);
1045         }
1046         traceToken.close();
1047     }
1048 
1049     private boolean isHoverActionWithoutConsumer(MotionEvent event) {
1050         // Only process these events when taskbar is present.
1051         int displayId = event.getDisplayId();
1052         TaskbarActivityContext tac = mTaskbarManager.getTaskbarForDisplay(displayId);
1053         boolean isTaskbarPresent = tac != null && tac.getDeviceProfile().isTaskbarPresent
1054                 && !tac.isPhoneMode();
1055         return event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0
1056                 && isTaskbarPresent;
1057     }
1058 
1059     // Talkback generates hover events on touch, which we do not want to consume.
1060     private boolean isCursorHoverEvent(MotionEvent event) {
1061         return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE;
1062     }
1063 
1064     public GestureState createGestureState(
1065             int displayId,
1066             GestureState previousGestureState,
1067             GestureState.TrackpadGestureType trackpadGestureType) {
1068         final GestureState gestureState;
1069         TopTaskTracker.CachedTaskInfo taskInfo;
1070         TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
1071                 displayId);
1072         if (taskAnimationManager != null && taskAnimationManager.isRecentsAnimationRunning()) {
1073             gestureState = new GestureState(
1074                     mOverviewComponentObserver, displayId, ActiveGestureLog.INSTANCE.getLogId());
1075             TopTaskTracker.CachedTaskInfo previousTaskInfo = previousGestureState.getRunningTask();
1076             // previousTaskInfo can be null iff previousGestureState == GestureState.DEFAULT_STATE
1077             taskInfo = previousTaskInfo != null
1078                     ? previousTaskInfo
1079                     : TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false, displayId);
1080             gestureState.updateRunningTask(taskInfo);
1081             gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds());
1082             gestureState.updatePreviouslyAppearedTaskIds(
1083                     previousGestureState.getPreviouslyAppearedTaskIds());
1084         } else {
1085             gestureState = new GestureState(
1086                     mOverviewComponentObserver,
1087                     displayId,
1088                     ActiveGestureLog.INSTANCE.incrementLogId());
1089             taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false, displayId);
1090             gestureState.updateRunningTask(taskInfo);
1091         }
1092         gestureState.setTrackpadGestureType(trackpadGestureType);
1093 
1094         // Log initial state for the gesture.
1095         ActiveGestureProtoLogProxy.logRunningTaskPackage(taskInfo.getPackageName());
1096         ActiveGestureProtoLogProxy.logSysuiStateFlags(mDeviceState.getSystemUiStateString());
1097         return gestureState;
1098     }
1099 
1100     /**
1101      * Returns a AbsSwipeUpHandler.Factory, used to instantiate AbsSwipeUpHandler later.
1102      * @param displayId The displayId of the display this handler will be used on.
1103      */
1104     public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory(int displayId) {
1105         BaseContainerInterface<?, ?> containerInterface =
1106                 mOverviewComponentObserver.getContainerInterface(displayId);
1107         if (containerInterface instanceof FallbackWindowInterface) {
1108             return mRecentsWindowSwipeHandlerFactory;
1109         } else if (containerInterface instanceof LauncherActivityInterface) {
1110             return mLauncherSwipeHandlerFactory;
1111         } else {
1112             return mFallbackSwipeHandlerFactory;
1113         }
1114     }
1115 
1116     /**
1117      * To be called by the consumer when it's no longer active. This can be called by any consumer
1118      * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts
1119      * intercepting touches, the base consumer can try to call this).
1120      */
1121     private void onConsumerInactive(InputConsumer caller) {
1122         if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
1123             reset(caller.getDisplayId());
1124         }
1125     }
1126 
1127     private void reset(int displayId) {
1128         mConsumer = mUncheckedConsumer = InputConsumerUtils.getDefaultInputConsumer(
1129                 displayId,
1130                 mUserUnlocked,
1131                 mRecentsDisplayModel.getTaskAnimationManager(displayId),
1132                 mTaskbarManager,
1133                 CompoundString.NO_OP);
1134         mGestureState = DEFAULT_STATE;
1135         // By default, use batching of the input events, but check receiver before using in the rare
1136         // case that the monitor was disposed before the swipe settled
1137         InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId);
1138         if (inputEventReceiver != null) {
1139             inputEventReceiver.setBatchingEnabled(true);
1140         }
1141     }
1142 
1143     @Override
1144     public void onConfigurationChanged(Configuration newConfig) {
1145         if (!LockedUserState.get(this).isUserUnlocked()) {
1146             return;
1147         }
1148         // TODO (b/399094853): handle config updates for all connected displays (relevant only for
1149         // gestures on external displays)
1150         final BaseContainerInterface containerInterface =
1151                 mOverviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY);
1152         final RecentsViewContainer container = containerInterface.getCreatedContainer();
1153         if (container == null || container.isStarted()) {
1154             // We only care about the existing background activity.
1155             return;
1156         }
1157         Configuration oldConfig = container.asContext().getResources().getConfiguration();
1158         boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig);
1159         if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges(
1160                 container.getComponentName(),
1161                 container.asContext().getResources().getConfiguration().diff(newConfig))) {
1162             // Since navBar gestural height are different between portrait and landscape,
1163             // can handle orientation changes and refresh navigation gestural region through
1164             // onOneHandedModeChanged()
1165             int newGesturalHeight = ResourceUtils.getNavbarSize(
1166                     ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
1167                     getApplicationContext().getResources());
1168             mDeviceState.onOneHandedModeChanged(newGesturalHeight);
1169             return;
1170         }
1171 
1172         ActivityPreloadUtil.preloadOverviewForTIS(this, false /* fromInit */);
1173     }
1174 
1175     private static boolean isTablet(Configuration config) {
1176         return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH;
1177     }
1178 
1179     @Override
1180     protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
1181         // Dump everything
1182         if (LockedUserState.get(this).isUserUnlocked()) {
1183             PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
1184         }
1185         mDeviceState.dump(pw);
1186         if (mOverviewComponentObserver != null) {
1187             mOverviewComponentObserver.dump(pw);
1188         }
1189         if (mOverviewCommandHelper != null) {
1190             mOverviewCommandHelper.dump(pw);
1191         }
1192         if (mGestureState != null) {
1193             mGestureState.dump("", pw);
1194         }
1195         pw.println("Input state:");
1196         pw.println("\tmInputMonitorCompat=" + mInputMonitorCompat);
1197         pw.println("\tmInputEventReceiver=" + mInputEventReceiver);
1198         if (mInputMonitorDisplayModel == null) {
1199             pw.println("\tmInputMonitorDisplayModel=null");
1200         } else {
1201             mInputMonitorDisplayModel.dump("\t", pw);
1202         }
1203         DisplayController.INSTANCE.get(this).dump(pw);
1204         for (RecentsDisplayResource resource : mRecentsDisplayModel.getActiveDisplayResources()) {
1205             int displayId = resource.getDisplayId();
1206             pw.println(String.format(Locale.ENGLISH, "TouchState (displayId %d):", displayId));
1207             RecentsViewContainer createdOverviewContainer =
1208                     mOverviewComponentObserver == null ? null
1209                             : mOverviewComponentObserver.getContainerInterface(
1210                                     displayId).getCreatedContainer();
1211             boolean resumed = mOverviewComponentObserver != null
1212                     && mOverviewComponentObserver.getContainerInterface(displayId).isResumed();
1213             pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer);
1214             pw.println("\tresumed=" + resumed);
1215             if (createdOverviewContainer != null) {
1216                 createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
1217             }
1218             resource.getTaskAnimationManager().dump("\t", pw);
1219         }
1220         pw.println("\tmConsumer=" + mConsumer.getName());
1221         ActiveGestureLog.INSTANCE.dump("", pw);
1222         RecentsModel.INSTANCE.get(this).dump("", pw);
1223         mTaskbarManager.dumpLogs("", pw);
1224         DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw);
1225         pw.println("ContextualSearchStateManager:");
1226         ContextualSearchStateManager.INSTANCE.get(this).dump("\t", pw);
1227         SystemUiProxy.INSTANCE.get(this).dump(pw);
1228         DeviceConfigWrapper.get().dump("   ", pw);
1229         TopTaskTracker.INSTANCE.get(this).dump(pw);
1230     }
1231 
1232     private AbsSwipeUpHandler createLauncherSwipeHandler(
1233             GestureState gestureState, long touchTimeMs) {
1234         TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
1235                 gestureState.getDisplayId());
1236         return new LauncherSwipeHandlerV2(this, taskAnimationManager,
1237                 gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(),
1238                 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
1239     }
1240 
1241     private AbsSwipeUpHandler createFallbackSwipeHandler(
1242             GestureState gestureState, long touchTimeMs) {
1243         TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
1244                 gestureState.getDisplayId());
1245         return new FallbackSwipeHandler(this, taskAnimationManager,
1246                 gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(),
1247                 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
1248     }
1249 
1250     private AbsSwipeUpHandler createRecentsWindowSwipeHandler(
1251             GestureState gestureState, long touchTimeMs) {
1252         TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
1253                 gestureState.getDisplayId());
1254         return new RecentsWindowSwipeHandler(this, taskAnimationManager,
1255                 gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(),
1256                 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
1257     }
1258 
1259     /**
1260      * Helper class that keeps track of external displays and prepares input monitors for each.
1261      */
1262     private class InputMonitorDisplayModel extends DisplayModel<InputMonitorResource> {
1263 
1264         private InputMonitorDisplayModel(Context context) {
1265             super(context);
1266             initializeDisplays();
1267         }
1268 
1269         @NonNull
1270         @Override
1271         public InputMonitorResource createDisplayResource(@NonNull Display display) {
1272             return new InputMonitorResource(display.getDisplayId());
1273         }
1274     }
1275 
1276     private class InputMonitorResource extends DisplayModel.DisplayResource {
1277 
1278         private final int displayId;
1279 
1280         private final InputMonitorCompat inputMonitorCompat;
1281         private final InputEventReceiver inputEventReceiver;
1282 
1283         private InputMonitorResource(int displayId) {
1284             this.displayId = displayId;
1285             inputMonitorCompat = new InputMonitorCompat("swipe-up", displayId);
1286             inputEventReceiver = inputMonitorCompat.getInputReceiver(
1287                     Looper.getMainLooper(),
1288                     TouchInteractionService.this.mMainChoreographer,
1289                     TouchInteractionService.this::onInputEvent);
1290         }
1291 
1292         @Override
1293         public void cleanup() {
1294             inputEventReceiver.dispose();
1295             inputMonitorCompat.dispose();
1296         }
1297 
1298         @Override
1299         public void dump(String prefix , PrintWriter writer) {
1300             writer.println(prefix + "InputMonitorResource:");
1301 
1302             writer.println(prefix + "\tdisplayId=" + displayId);
1303             writer.println(prefix + "\tinputMonitorCompat=" + inputMonitorCompat);
1304             writer.println(prefix + "\tinputEventReceiver=" + inputEventReceiver);
1305         }
1306     }
1307 }
1308