• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 android.content.Context
19 import android.view.MotionEvent
20 import androidx.annotation.VisibleForTesting
21 import com.android.launcher3.anim.AnimatedFloat
22 import com.android.launcher3.statemanager.BaseState
23 import com.android.launcher3.statemanager.StatefulContainer
24 import com.android.launcher3.taskbar.TaskbarManager
25 import com.android.launcher3.util.LockedUserState.Companion.get
26 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer
27 import com.android.quickstep.inputconsumers.AssistantInputConsumer
28 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer
29 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer
30 import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer
31 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer
32 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer
33 import com.android.quickstep.inputconsumers.OverviewInputConsumer
34 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer
35 import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer
36 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer
37 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer
38 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer
39 import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer
40 import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer
41 import com.android.quickstep.util.ActiveGestureErrorDetector
42 import com.android.quickstep.util.ActiveGestureLog
43 import com.android.quickstep.util.ActiveGestureLog.CompoundString
44 import com.android.quickstep.util.ActiveGestureProtoLogProxy
45 import com.android.quickstep.views.RecentsViewContainer
46 import com.android.systemui.shared.system.InputChannelCompat
47 import com.android.systemui.shared.system.InputMonitorCompat
48 import com.android.wm.shell.Flags
49 import java.util.function.Consumer
50 import java.util.function.Function
51 
52 /** Utility class for creating input consumers. */
53 object InputConsumerUtils {
54     private const val SUBSTRING_PREFIX = "; "
55     private const val NEWLINE_PREFIX = "\n\t\t\t-> "
56 
57     @JvmStatic
newConsumernull58     fun <S : BaseState<S>, T> newConsumer(
59         context: Context,
60         userUnlocked: Boolean,
61         overviewComponentObserver: OverviewComponentObserver,
62         deviceState: RecentsAnimationDeviceState,
63         previousGestureState: GestureState,
64         gestureState: GestureState,
65         taskAnimationManager: TaskAnimationManager,
66         inputMonitorCompat: InputMonitorCompat,
67         swipeUpHandlerFactory: AbsSwipeUpHandler.Factory,
68         onCompleteCallback: Consumer<OtherActivityInputConsumer>,
69         inputEventReceiver: InputChannelCompat.InputEventReceiver,
70         taskbarManager: TaskbarManager,
71         swipeUpProxyProvider: Function<GestureState?, AnimatedFloat?>,
72         overviewCommandHelper: OverviewCommandHelper,
73         event: MotionEvent,
74     ): InputConsumer where T : RecentsViewContainer, T : StatefulContainer<S> {
75         val tac = taskbarManager.currentActivityContext
76         val bubbleControllers = tac?.bubbleControllers
77         if (bubbleControllers != null && BubbleBarInputConsumer.isEventOnBubbles(tac, event)) {
78             val consumer: InputConsumer =
79                 BubbleBarInputConsumer(
80                     context,
81                     gestureState.displayId,
82                     bubbleControllers,
83                     inputMonitorCompat,
84                 )
85             logInputConsumerSelectionReason(
86                 consumer,
87                 newCompoundString("event is on bubbles, creating new input consumer"),
88             )
89             return consumer
90         }
91         val progressProxy = swipeUpProxyProvider.apply(gestureState)
92         if (progressProxy != null) {
93             val consumer: InputConsumer =
94                 ProgressDelegateInputConsumer(
95                     context,
96                     taskAnimationManager,
97                     gestureState,
98                     inputMonitorCompat,
99                     progressProxy,
100                 )
101 
102             logInputConsumerSelectionReason(
103                 consumer,
104                 newCompoundString(
105                     "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer"
106                 ),
107             )
108 
109             return consumer
110         }
111 
112         val canStartSystemGesture =
113             if (gestureState.isTrackpadGesture) deviceState.canStartTrackpadGesture()
114             else deviceState.canStartSystemGesture()
115 
116         if (!get(context).isUserUnlocked) {
117             val reasonString = newCompoundString("device locked")
118             val consumer =
119                 if (canStartSystemGesture) {
120                     // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
121                     // launched while device is locked even after exiting direct boot mode (e.g.
122                     // camera).
123                     createDeviceLockedInputConsumer(
124                         context,
125                         userUnlocked,
126                         taskbarManager,
127                         deviceState,
128                         gestureState,
129                         taskAnimationManager,
130                         inputMonitorCompat,
131                         reasonString.append("%scan start system gesture", SUBSTRING_PREFIX),
132                     )
133                 } else {
134                     getDefaultInputConsumer(
135                         gestureState.displayId,
136                         userUnlocked,
137                         taskAnimationManager,
138                         taskbarManager,
139                         reasonString.append("%scannot start system gesture", SUBSTRING_PREFIX),
140                     )
141                 }
142             logInputConsumerSelectionReason(consumer, reasonString)
143             return consumer
144         }
145 
146         var reasonString: CompoundString
147         var base: InputConsumer
148         // When there is an existing recents animation running, bypass systemState check as this is
149         // a followup gesture and the first gesture started in a valid system state.
150         if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning) {
151             reasonString =
152                 newCompoundString(
153                     if (canStartSystemGesture)
154                         "can start system gesture, trying to use base consumer"
155                     else "recents animation was running, trying to use base consumer"
156                 )
157             base =
158                 newBaseConsumer<S, T>(
159                     context,
160                     userUnlocked,
161                     taskbarManager,
162                     overviewComponentObserver,
163                     deviceState,
164                     previousGestureState,
165                     gestureState,
166                     taskAnimationManager,
167                     inputMonitorCompat,
168                     swipeUpHandlerFactory,
169                     onCompleteCallback,
170                     inputEventReceiver,
171                     event,
172                     reasonString,
173                 )
174         } else {
175             reasonString =
176                 newCompoundString(
177                     "cannot start system gesture and recents " +
178                         "animation was not running, trying to use default input consumer"
179                 )
180             base =
181                 getDefaultInputConsumer(
182                     gestureState.displayId,
183                     userUnlocked,
184                     taskAnimationManager,
185                     taskbarManager,
186                     reasonString,
187                 )
188         }
189         if (deviceState.isGesturalNavMode || gestureState.isTrackpadGesture) {
190             handleOrientationSetup(base)
191         }
192         if (deviceState.isFullyGesturalNavMode || gestureState.isTrackpadGesture) {
193             val reasonPrefix =
194                 "device is in gesture navigation mode or 3-button mode with a trackpad gesture"
195             if (deviceState.canTriggerAssistantAction(event)) {
196                 reasonString.append(
197                     "%s%s%sgesture can trigger the assistant, " +
198                         "trying to use assistant input consumer",
199                     NEWLINE_PREFIX,
200                     reasonPrefix,
201                     SUBSTRING_PREFIX,
202                 )
203                 base =
204                     tryCreateAssistantInputConsumer(
205                         context,
206                         deviceState,
207                         inputMonitorCompat,
208                         base,
209                         gestureState,
210                         event,
211                         reasonString,
212                     )
213             }
214 
215             // If Taskbar is present, we listen for swipe or cursor hover events to unstash it.
216             if (tac != null && base !is AssistantInputConsumer) {
217                 // Present always on large screen or on small screen w/ flag
218                 val useTaskbarConsumer =
219                     (tac.deviceProfile.isTaskbarPresent &&
220                         !tac.isPhoneMode &&
221                         !tac.isInStashedLauncherState)
222                 if (canStartSystemGesture && useTaskbarConsumer) {
223                     reasonString.append(
224                         "%s%s%sTaskbarActivityContext != null, " +
225                             "using TaskbarUnstashInputConsumer",
226                         NEWLINE_PREFIX,
227                         reasonPrefix,
228                         SUBSTRING_PREFIX,
229                     )
230                     base =
231                         TaskbarUnstashInputConsumer(
232                             context,
233                             base,
234                             inputMonitorCompat,
235                             tac,
236                             overviewCommandHelper,
237                             gestureState,
238                         )
239                 }
240             }
241             if (Flags.enableBubblesLongPressNavHandle()) {
242                 // Create bubbles input consumer before NavHandleLongPressInputConsumer.
243                 // This allows for nav handle to fall back to bubbles.
244                 if (deviceState.isBubblesExpanded) {
245                     reasonString =
246                         newCompoundString(reasonPrefix)
247                             .append(
248                                 "%sbubbles expanded, trying to use default input consumer",
249                                 SUBSTRING_PREFIX,
250                             )
251                     // Bubbles can handle home gesture itself.
252                     base =
253                         getDefaultInputConsumer(
254                             gestureState.displayId,
255                             userUnlocked,
256                             taskAnimationManager,
257                             taskbarManager,
258                             reasonString,
259                         )
260                 }
261             }
262 
263             val navHandle = tac?.navHandle ?: SystemUiProxy.INSTANCE[context]
264             if (
265                 canStartSystemGesture &&
266                     !previousGestureState.isRecentsAnimationRunning &&
267                     navHandle.canNavHandleBeLongPressed() &&
268                     !ignoreThreeFingerTrackpadForNavHandleLongPress(gestureState)
269             ) {
270                 reasonString.append(
271                     "%s%s%sNot running recents animation, ",
272                     NEWLINE_PREFIX,
273                     reasonPrefix,
274                     SUBSTRING_PREFIX,
275                 )
276                 if (tac != null && tac.navHandle.canNavHandleBeLongPressed()) {
277                     reasonString.append("stashed handle is long-pressable, ")
278                 }
279                 reasonString.append("using NavHandleLongPressInputConsumer")
280                 base =
281                     NavHandleLongPressInputConsumer(
282                         context,
283                         base,
284                         inputMonitorCompat,
285                         deviceState,
286                         navHandle,
287                         gestureState,
288                     )
289             }
290 
291             if (!Flags.enableBubblesLongPressNavHandle()) {
292                 // Continue overriding nav handle input consumer with bubbles
293                 if (deviceState.isBubblesExpanded) {
294                     reasonString =
295                         newCompoundString(reasonPrefix)
296                             .append(
297                                 "%sbubbles expanded, trying to use default input consumer",
298                                 SUBSTRING_PREFIX,
299                             )
300                     // Bubbles can handle home gesture itself.
301                     base =
302                         getDefaultInputConsumer(
303                             gestureState.displayId,
304                             userUnlocked,
305                             taskAnimationManager,
306                             taskbarManager,
307                             reasonString,
308                         )
309                 }
310             }
311 
312             if (deviceState.isSystemUiDialogShowing) {
313                 reasonString =
314                     newCompoundString(reasonPrefix)
315                         .append(
316                             "%ssystem dialog is showing, using SysUiOverlayInputConsumer",
317                             SUBSTRING_PREFIX,
318                         )
319                 base =
320                     SysUiOverlayInputConsumer(
321                         context,
322                         gestureState.displayId,
323                         deviceState,
324                         inputMonitorCompat,
325                     )
326             }
327 
328             if (
329                 gestureState.isTrackpadGesture &&
330                     canStartSystemGesture &&
331                     !previousGestureState.isRecentsAnimationRunning
332             ) {
333                 reasonString =
334                     newCompoundString(reasonPrefix)
335                         .append(
336                             "%sTrackpad 3-finger gesture, using TrackpadStatusBarInputConsumer",
337                             SUBSTRING_PREFIX,
338                         )
339                 base =
340                     TrackpadStatusBarInputConsumer(
341                         context,
342                         gestureState.displayId,
343                         base,
344                         inputMonitorCompat,
345                     )
346             }
347 
348             if (deviceState.isScreenPinningActive) {
349                 reasonString =
350                     newCompoundString(reasonPrefix)
351                         .append(
352                             "%sscreen pinning is active, using ScreenPinnedInputConsumer",
353                             SUBSTRING_PREFIX,
354                         )
355                 // Note: we only allow accessibility to wrap this, and it replaces the previous
356                 // base input consumer (which should be NO_OP anyway since topTaskLocked == true).
357                 base = ScreenPinnedInputConsumer(context, gestureState)
358             }
359 
360             if (deviceState.canTriggerOneHandedAction(event)) {
361                 reasonString.append(
362                     "%s%s%sgesture can trigger one handed mode, " +
363                         "using OneHandedModeInputConsumer",
364                     NEWLINE_PREFIX,
365                     reasonPrefix,
366                     SUBSTRING_PREFIX,
367                 )
368                 base =
369                     OneHandedModeInputConsumer(
370                         context,
371                         gestureState.displayId,
372                         deviceState,
373                         base,
374                         inputMonitorCompat,
375                     )
376             }
377 
378             if (deviceState.isAccessibilityMenuAvailable) {
379                 reasonString.append(
380                     "%s%s%saccessibility menu is available, using AccessibilityInputConsumer",
381                     NEWLINE_PREFIX,
382                     reasonPrefix,
383                     SUBSTRING_PREFIX,
384                 )
385                 base =
386                     AccessibilityInputConsumer(
387                         context,
388                         gestureState.displayId,
389                         deviceState,
390                         base,
391                         inputMonitorCompat,
392                     )
393             }
394         } else {
395             val reasonPrefix = "device is not in gesture navigation mode"
396             if (deviceState.isScreenPinningActive) {
397                 reasonString =
398                     newCompoundString(reasonPrefix)
399                         .append(
400                             "%sscreen pinning is active, trying to use default input consumer",
401                             SUBSTRING_PREFIX,
402                         )
403                 base =
404                     getDefaultInputConsumer(
405                         gestureState.displayId,
406                         userUnlocked,
407                         taskAnimationManager,
408                         taskbarManager,
409                         reasonString,
410                     )
411             }
412 
413             if (deviceState.canTriggerOneHandedAction(event)) {
414                 reasonString.append(
415                     "%s%s%sgesture can trigger one handed mode, " +
416                         "using OneHandedModeInputConsumer",
417                     NEWLINE_PREFIX,
418                     reasonPrefix,
419                     SUBSTRING_PREFIX,
420                 )
421                 base =
422                     OneHandedModeInputConsumer(
423                         context,
424                         gestureState.displayId,
425                         deviceState,
426                         base,
427                         inputMonitorCompat,
428                     )
429             }
430         }
431         logInputConsumerSelectionReason(base, reasonString)
432         return base
433     }
434 
435     @JvmStatic
tryCreateAssistantInputConsumernull436     fun tryCreateAssistantInputConsumer(
437         context: Context,
438         deviceState: RecentsAnimationDeviceState,
439         inputMonitorCompat: InputMonitorCompat,
440         gestureState: GestureState,
441         motionEvent: MotionEvent,
442     ): InputConsumer {
443         return tryCreateAssistantInputConsumer(
444             context,
445             deviceState,
446             inputMonitorCompat,
447             InputConsumer.createNoOpInputConsumer(gestureState.displayId),
448             gestureState,
449             motionEvent,
450             CompoundString.NO_OP,
451         )
452     }
453 
tryCreateAssistantInputConsumernull454     private fun tryCreateAssistantInputConsumer(
455         context: Context,
456         deviceState: RecentsAnimationDeviceState,
457         inputMonitorCompat: InputMonitorCompat,
458         base: InputConsumer,
459         gestureState: GestureState,
460         motionEvent: MotionEvent,
461         reasonString: CompoundString,
462     ): InputConsumer {
463         return if (deviceState.isGestureBlockedTask(gestureState.runningTask)) {
464             reasonString.append(
465                 "%sis gesture-blocked task, using base input consumer",
466                 SUBSTRING_PREFIX,
467             )
468             base
469         } else {
470             reasonString.append("%susing AssistantInputConsumer", SUBSTRING_PREFIX)
471             AssistantInputConsumer(
472                 context,
473                 gestureState,
474                 base,
475                 inputMonitorCompat,
476                 deviceState,
477                 motionEvent,
478             )
479         }
480     }
481 
482     @VisibleForTesting
483     @JvmStatic
newBaseConsumernull484     fun <S : BaseState<S>, T> newBaseConsumer(
485         context: Context,
486         userUnlocked: Boolean,
487         taskbarManager: TaskbarManager,
488         overviewComponentObserver: OverviewComponentObserver,
489         deviceState: RecentsAnimationDeviceState,
490         previousGestureState: GestureState,
491         gestureState: GestureState,
492         taskAnimationManager: TaskAnimationManager,
493         inputMonitorCompat: InputMonitorCompat,
494         swipeUpHandlerFactory: AbsSwipeUpHandler.Factory,
495         onCompleteCallback: Consumer<OtherActivityInputConsumer>,
496         inputEventReceiver: InputChannelCompat.InputEventReceiver,
497         event: MotionEvent,
498         reasonString: CompoundString,
499     ): InputConsumer where T : RecentsViewContainer, T : StatefulContainer<S> {
500         if (deviceState.isKeyguardShowingOccluded) {
501             // This handles apps showing over the lockscreen (e.g. camera)
502             return createDeviceLockedInputConsumer(
503                 context,
504                 userUnlocked,
505                 taskbarManager,
506                 deviceState,
507                 gestureState,
508                 taskAnimationManager,
509                 inputMonitorCompat,
510                 reasonString.append(
511                     "%skeyguard is showing occluded, " +
512                         "trying to use device locked input consumer",
513                     SUBSTRING_PREFIX,
514                 ),
515             )
516         }
517 
518         reasonString.append("%skeyguard is not showing occluded", SUBSTRING_PREFIX)
519 
520         val runningTask = gestureState.runningTask
521         // Use overview input consumer for sharesheets on top of home.
522         val forceOverviewInputConsumer =
523             gestureState.getContainerInterface<S, T>().isStarted() &&
524                 runningTask != null &&
525                 runningTask.isRootChooseActivity
526 
527         if (!Flags.enableShellTopTaskTracking()) {
528             // In the case where we are in an excluded, translucent overlay, ignore it and treat the
529             // running activity as the task behind the overlay.
530             val otherVisibleTask = runningTask?.visibleNonExcludedTask
531             if (otherVisibleTask != null) {
532                 ActiveGestureProtoLogProxy.logUpdateGestureStateRunningTask(
533                     otherVisibleTask.packageName ?: "MISSING",
534                     runningTask.packageName ?: "MISSING",
535                 )
536                 gestureState.updateRunningTask(otherVisibleTask)
537             }
538         }
539 
540         val previousGestureAnimatedToLauncher =
541             (previousGestureState.isRunningAnimationToLauncher ||
542                 deviceState.isPredictiveBackToHomeInProgress)
543         // with shell-transitions, home is resumed during recents animation, so
544         // explicitly check against recents animation too.
545         val launcherResumedThroughShellTransition =
546             (gestureState.getContainerInterface<S, T>().isResumed() &&
547                 !previousGestureState.isRecentsAnimationRunning)
548         // If a task fragment within Launcher is resumed
549         val launcherChildActivityResumed =
550             (com.android.launcher3.Flags.useActivityOverlay() &&
551                 runningTask != null &&
552                 runningTask.isHomeTask &&
553                 overviewComponentObserver.isHomeAndOverviewSameActivity &&
554                 !launcherResumedThroughShellTransition &&
555                 !previousGestureState.isRecentsAnimationRunning)
556 
557         return if (gestureState.getContainerInterface<S, T>().isInLiveTileMode()) {
558             createOverviewInputConsumer<S, T>(
559                 userUnlocked,
560                 taskAnimationManager,
561                 taskbarManager,
562                 deviceState,
563                 inputMonitorCompat,
564                 previousGestureState,
565                 gestureState,
566                 event,
567                 reasonString.append(
568                     "%sis in live tile mode, trying to use overview input consumer",
569                     SUBSTRING_PREFIX,
570                 ),
571             )
572         } else if (runningTask == null) {
573             getDefaultInputConsumer(
574                 gestureState.displayId,
575                 userUnlocked,
576                 taskAnimationManager,
577                 taskbarManager,
578                 reasonString.append("%srunning task == null", SUBSTRING_PREFIX),
579             )
580         } else if (
581             previousGestureAnimatedToLauncher ||
582                 launcherResumedThroughShellTransition ||
583                 forceOverviewInputConsumer
584         ) {
585             createOverviewInputConsumer<S, T>(
586                 userUnlocked,
587                 taskAnimationManager,
588                 taskbarManager,
589                 deviceState,
590                 inputMonitorCompat,
591                 previousGestureState,
592                 gestureState,
593                 event,
594                 reasonString.append(
595                     if (previousGestureAnimatedToLauncher)
596                         ("%sprevious gesture animated to launcher, " +
597                             "trying to use overview input consumer")
598                     else
599                         (if (launcherResumedThroughShellTransition)
600                             ("%slauncher resumed through a shell transition, " +
601                                 "trying to use overview input consumer")
602                         else
603                             ("%sforceOverviewInputConsumer == true, " +
604                                 "trying to use overview input consumer")),
605                     SUBSTRING_PREFIX,
606                 ),
607             )
608         } else if (deviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
609             getDefaultInputConsumer(
610                 gestureState.displayId,
611                 userUnlocked,
612                 taskAnimationManager,
613                 taskbarManager,
614                 reasonString.append(
615                     if (launcherChildActivityResumed)
616                         "%sis launcher child-task, trying to use default input consumer"
617                     else "%sis gesture-blocked task, trying to use default input consumer",
618                     SUBSTRING_PREFIX,
619                 ),
620             )
621         } else {
622             reasonString.append("%susing OtherActivityInputConsumer", SUBSTRING_PREFIX)
623             createOtherActivityInputConsumer<S, T>(
624                 context,
625                 swipeUpHandlerFactory,
626                 overviewComponentObserver,
627                 deviceState,
628                 taskAnimationManager,
629                 inputMonitorCompat,
630                 onCompleteCallback,
631                 inputEventReceiver,
632                 gestureState,
633                 event,
634             )
635         }
636     }
637 
createDeviceLockedInputConsumernull638     private fun createDeviceLockedInputConsumer(
639         context: Context,
640         userUnlocked: Boolean,
641         taskbarManager: TaskbarManager,
642         deviceState: RecentsAnimationDeviceState,
643         gestureState: GestureState,
644         taskAnimationManager: TaskAnimationManager,
645         inputMonitorCompat: InputMonitorCompat,
646         reasonString: CompoundString,
647     ): InputConsumer {
648         return if (
649             (deviceState.isFullyGesturalNavMode || gestureState.isTrackpadGesture) &&
650                 gestureState.runningTask != null
651         ) {
652             reasonString.append(
653                 "%sdevice is in gesture nav mode or 3-button mode with a trackpad " +
654                     "gesture and running task != null, using DeviceLockedInputConsumer",
655                 SUBSTRING_PREFIX,
656             )
657             DeviceLockedInputConsumer(
658                 context,
659                 deviceState,
660                 taskAnimationManager,
661                 gestureState,
662                 inputMonitorCompat,
663             )
664         } else {
665             getDefaultInputConsumer(
666                 gestureState.displayId,
667                 userUnlocked,
668                 taskAnimationManager,
669                 taskbarManager,
670                 reasonString.append(
671                     if (deviceState.isFullyGesturalNavMode || gestureState.isTrackpadGesture)
672                         "%srunning task == null, trying to use default input consumer"
673                     else
674                         ("%sdevice is not in gesture nav mode and it's not a trackpad gesture," +
675                             " trying to use default input consumer"),
676                     SUBSTRING_PREFIX,
677                 ),
678             )
679         }
680     }
681 
createOverviewInputConsumernull682     private fun <S : BaseState<S>, T> createOverviewInputConsumer(
683         userUnlocked: Boolean,
684         taskAnimationManager: TaskAnimationManager,
685         taskbarManager: TaskbarManager,
686         deviceState: RecentsAnimationDeviceState,
687         inputMonitorCompat: InputMonitorCompat,
688         previousGestureState: GestureState,
689         gestureState: GestureState,
690         event: MotionEvent,
691         reasonString: CompoundString,
692     ): InputConsumer where T : RecentsViewContainer, T : StatefulContainer<S> {
693         val container: T =
694             gestureState.getContainerInterface<S, T>().getCreatedContainer()
695                 ?: return getDefaultInputConsumer(
696                     gestureState.displayId,
697                     userUnlocked,
698                     taskAnimationManager,
699                     taskbarManager,
700                     reasonString.append(
701                         "%sactivity == null, trying to use default input consumer",
702                         SUBSTRING_PREFIX,
703                     ),
704                 )
705 
706         val rootView = container.rootView
707         val hasWindowFocus = rootView?.hasWindowFocus() ?: false
708         val isPreviousGestureAnimatingToLauncher =
709             (previousGestureState.isRunningAnimationToLauncher ||
710                 deviceState.isPredictiveBackToHomeInProgress)
711         val isInLiveTileMode: Boolean =
712             gestureState.getContainerInterface<S, T>().isInLiveTileMode()
713 
714         reasonString.append(
715             if (hasWindowFocus) "%sactivity has window focus"
716             else
717                 (if (isPreviousGestureAnimatingToLauncher)
718                     "%sprevious gesture is still animating to launcher"
719                 else if (isInLiveTileMode) "%sdevice is in live mode"
720                 else "%sall overview focus conditions failed"),
721             SUBSTRING_PREFIX,
722         )
723         return if (hasWindowFocus || isPreviousGestureAnimatingToLauncher || isInLiveTileMode) {
724             reasonString.append(
725                 "%soverview should have focus, using OverviewInputConsumer",
726                 SUBSTRING_PREFIX,
727             )
728             OverviewInputConsumer(
729                 gestureState,
730                 container,
731                 inputMonitorCompat,
732                 /* startingInActivityBounds= */ false,
733             )
734         } else {
735             reasonString.append(
736                 "%soverview shouldn't have focus, using OverviewWithoutFocusInputConsumer",
737                 SUBSTRING_PREFIX,
738             )
739             val disableHorizontalSwipe = deviceState.isInExclusionRegion(event)
740             OverviewWithoutFocusInputConsumer(
741                 container.asContext(),
742                 deviceState,
743                 gestureState,
744                 inputMonitorCompat,
745                 disableHorizontalSwipe,
746             )
747         }
748     }
749 
750     /** Returns the [ResetGestureInputConsumer] if user is unlocked, else NO_OP. */
751     @JvmStatic
getDefaultInputConsumernull752     fun getDefaultInputConsumer(
753         displayId: Int,
754         userUnlocked: Boolean,
755         taskAnimationManager: TaskAnimationManager?,
756         taskbarManager: TaskbarManager?,
757         reasonString: CompoundString,
758     ): InputConsumer {
759         return if (userUnlocked && taskAnimationManager != null && taskbarManager != null) {
760             reasonString.append(
761                 "%sResetGestureInputConsumer available, using ResetGestureInputConsumer",
762                 SUBSTRING_PREFIX,
763             )
764             ResetGestureInputConsumer(displayId, taskAnimationManager) {
765                 taskbarManager.getTaskbarForDisplay(displayId)
766             }
767         } else {
768             reasonString.append(
769                 "%s${
770                     if (userUnlocked) "user is locked"
771                     else if (taskAnimationManager == null) "taskAnimationManager is null"
772                     else "taskbarManager is null"
773                 }, using no-op input consumer",
774                 SUBSTRING_PREFIX,
775             )
776             // ResetGestureInputConsumer isn't available until onUserUnlocked(), so reset to
777             // NO_OP until then (we never want these to be null).
778             InputConsumer.createNoOpInputConsumer(displayId)
779         }
780     }
781 
createOtherActivityInputConsumernull782     private fun <S : BaseState<S>, T> createOtherActivityInputConsumer(
783         context: Context,
784         swipeUpHandlerFactory: AbsSwipeUpHandler.Factory,
785         overviewComponentObserver: OverviewComponentObserver,
786         deviceState: RecentsAnimationDeviceState,
787         taskAnimationManager: TaskAnimationManager,
788         inputMonitorCompat: InputMonitorCompat,
789         onCompleteCallback: Consumer<OtherActivityInputConsumer>,
790         inputEventReceiver: InputChannelCompat.InputEventReceiver,
791         gestureState: GestureState,
792         event: MotionEvent,
793     ): InputConsumer where T : RecentsViewContainer, T : StatefulContainer<S> {
794         val shouldDefer =
795             (!overviewComponentObserver.isHomeAndOverviewSame ||
796                 gestureState
797                     .getContainerInterface<S, T>()
798                     .deferStartingActivity(deviceState, event))
799         val disableHorizontalSwipe = deviceState.isInExclusionRegion(event)
800         return OtherActivityInputConsumer(
801             /* base= */ context,
802             deviceState,
803             taskAnimationManager,
804             gestureState,
805             /* isDeferredDownTarget= */ shouldDefer,
806             onCompleteCallback,
807             inputMonitorCompat,
808             inputEventReceiver,
809             disableHorizontalSwipe,
810             swipeUpHandlerFactory,
811         )
812     }
813 
newCompoundStringnull814     private fun newCompoundString(substring: String): CompoundString {
815         return CompoundString("%s%s", NEWLINE_PREFIX, substring)
816     }
817 
logInputConsumerSelectionReasonnull818     private fun logInputConsumerSelectionReason(
819         consumer: InputConsumer,
820         reasonString: CompoundString,
821     ) {
822         ActiveGestureProtoLogProxy.logSetInputConsumer(consumer.name, reasonString.toString())
823         if ((consumer.type and InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
824             ActiveGestureLog.INSTANCE.trackEvent(
825                 ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER
826             )
827         }
828     }
829 
ignoreThreeFingerTrackpadForNavHandleLongPressnull830     private fun ignoreThreeFingerTrackpadForNavHandleLongPress(
831         gestureState: GestureState
832     ): Boolean {
833         return (com.android.launcher3.Flags.ignoreThreeFingerTrackpadForNavHandleLongPress() &&
834             gestureState.isThreeFingerTrackpadGesture)
835     }
836 
handleOrientationSetupnull837     private fun handleOrientationSetup(baseInputConsumer: InputConsumer) {
838         baseInputConsumer.notifyOrientationSetup()
839     }
840 }
841