1 /*
2  * Copyright (C) 2019 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.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
19 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
20 import static android.view.Display.DEFAULT_DISPLAY;
21 
22 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
23 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
24 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
25 import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
26 import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
27 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
28 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
29 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
30 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
31 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
33 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
34 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
35 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
41 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
42 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
45 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
46 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
47 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
48 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
49 
50 import android.app.ActivityTaskManager;
51 import android.content.Context;
52 import android.graphics.Region;
53 import android.inputmethodservice.InputMethodService;
54 import android.net.Uri;
55 import android.os.RemoteException;
56 import android.os.SystemProperties;
57 import android.provider.Settings;
58 import android.view.MotionEvent;
59 import android.view.ViewConfiguration;
60 
61 import androidx.annotation.NonNull;
62 import androidx.annotation.Nullable;
63 import androidx.annotation.VisibleForTesting;
64 
65 import com.android.launcher3.util.DisplayController;
66 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
67 import com.android.launcher3.util.DisplayController.Info;
68 import com.android.launcher3.util.NavigationMode;
69 import com.android.launcher3.util.SettingsCache;
70 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
71 import com.android.quickstep.util.ActiveGestureLog;
72 import com.android.quickstep.util.AssistStateManager;
73 import com.android.quickstep.util.GestureExclusionManager;
74 import com.android.quickstep.util.GestureExclusionManager.ExclusionListener;
75 import com.android.quickstep.util.NavBarPosition;
76 import com.android.systemui.shared.system.ActivityManagerWrapper;
77 import com.android.systemui.shared.system.QuickStepContract;
78 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
79 import com.android.systemui.shared.system.TaskStackChangeListener;
80 import com.android.systemui.shared.system.TaskStackChangeListeners;
81 
82 import java.io.PrintWriter;
83 import java.util.ArrayList;
84 
85 /**
86  * Manages the state of the system during a swipe up gesture.
87  */
88 public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener {
89 
90     private static final String TAG = "RecentsAnimationDeviceState";
91 
92     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
93 
94     // TODO: Move to quickstep contract
95     private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
96     private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
97 
98     private final Context mContext;
99     private final DisplayController mDisplayController;
100 
101     private final GestureExclusionManager mExclusionManager;
102     private final AssistStateManager mAssistStateManager;
103 
104     private final RotationTouchHelper mRotationTouchHelper;
105     private final TaskStackChangeListener mPipListener;
106     // Cache for better performance since it doesn't change at runtime.
107     private final boolean mCanImeRenderGesturalNavButtons =
108             InputMethodService.canImeRenderGesturalNavButtons();
109 
110     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
111 
112     private @SystemUiStateFlags long mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE;
113     private NavigationMode mMode = THREE_BUTTONS;
114     private NavBarPosition mNavBarPosition;
115 
116     private final Region mDeferredGestureRegion = new Region();
117     private boolean mAssistantAvailable;
118     private float mAssistantVisibility;
119     private boolean mIsUserSetupComplete;
120     private boolean mIsOneHandedModeEnabled;
121     private boolean mIsSwipeToNotificationEnabled;
122     private final boolean mIsOneHandedModeSupported;
123     private boolean mPipIsActive;
124     private boolean mIsPredictiveBackToHomeInProgress;
125 
126     private int mGestureBlockingTaskId = -1;
127     private @NonNull Region mExclusionRegion = GestureExclusionManager.EMPTY_REGION;
128     private boolean mExclusionListenerRegistered;
129 
RecentsAnimationDeviceState(Context context)130     public RecentsAnimationDeviceState(Context context) {
131         this(context, false, GestureExclusionManager.INSTANCE);
132     }
133 
RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches)134     public RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches) {
135         this(context, isInstanceForTouches, GestureExclusionManager.INSTANCE);
136     }
137 
138     @VisibleForTesting
RecentsAnimationDeviceState(Context context, GestureExclusionManager exclusionManager)139     RecentsAnimationDeviceState(Context context, GestureExclusionManager exclusionManager) {
140         this(context, false, exclusionManager);
141     }
142 
143     /**
144      * @param isInstanceForTouches {@code true} if this is the persistent instance being used for
145      *                                   gesture touch handling
146      */
RecentsAnimationDeviceState( Context context, boolean isInstanceForTouches, GestureExclusionManager exclusionManager)147     RecentsAnimationDeviceState(
148             Context context, boolean isInstanceForTouches,
149             GestureExclusionManager exclusionManager) {
150         mContext = context;
151         mDisplayController = DisplayController.INSTANCE.get(context);
152         mExclusionManager = exclusionManager;
153         mAssistStateManager = AssistStateManager.INSTANCE.get(context);
154         mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
155         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
156         if (isInstanceForTouches) {
157             // rotationTouchHelper doesn't get initialized after being destroyed, so only destroy
158             // if primary TouchInteractionService instance needs to be destroyed.
159             mRotationTouchHelper.init();
160             runOnDestroy(mRotationTouchHelper::destroy);
161         }
162 
163         // Register for exclusion updates
164         runOnDestroy(() -> unregisterExclusionListener());
165 
166         // Register for display changes changes
167         mDisplayController.addChangeListener(this);
168         onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL);
169         runOnDestroy(() -> mDisplayController.removeChangeListener(this));
170 
171         SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
172         if (mIsOneHandedModeSupported) {
173             Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED);
174             SettingsCache.OnChangeListener onChangeListener =
175                     enabled -> mIsOneHandedModeEnabled = enabled;
176             settingsCache.register(oneHandedUri, onChangeListener);
177             mIsOneHandedModeEnabled = settingsCache.getValue(oneHandedUri);
178             runOnDestroy(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
179         } else {
180             mIsOneHandedModeEnabled = false;
181         }
182 
183         Uri swipeBottomNotificationUri =
184                 Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
185         SettingsCache.OnChangeListener onChangeListener =
186                 enabled -> mIsSwipeToNotificationEnabled = enabled;
187         settingsCache.register(swipeBottomNotificationUri, onChangeListener);
188         mIsSwipeToNotificationEnabled = settingsCache.getValue(swipeBottomNotificationUri);
189         runOnDestroy(() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
190 
191         Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
192         mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0);
193         if (!mIsUserSetupComplete) {
194             SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e;
195             settingsCache.register(setupCompleteUri, userSetupChangeListener);
196             runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
197         }
198 
199         try {
200             mPipIsActive = ActivityTaskManager.getService().getRootTaskInfo(
201                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED) != null;
202         } catch (RemoteException e) {
203             // Do nothing
204         }
205         mPipListener = new TaskStackChangeListener() {
206             @Override
207             public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
208                 mPipIsActive = true;
209             }
210 
211             @Override
212             public void onActivityUnpinned() {
213                 mPipIsActive = false;
214             }
215         };
216         TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
217         runOnDestroy(() ->
218                 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
219     }
220 
runOnDestroy(Runnable action)221     private void runOnDestroy(Runnable action) {
222         mOnDestroyActions.add(action);
223     }
224 
225     /**
226      * Cleans up all the registered listeners and receivers.
227      */
destroy()228     public void destroy() {
229         for (Runnable r : mOnDestroyActions) {
230             r.run();
231         }
232     }
233 
234     /**
235      * Adds a listener for the nav mode change, guaranteed to be called after the device state's
236      * mode has changed.
237      */
addNavigationModeChangedCallback(Runnable callback)238     public void addNavigationModeChangedCallback(Runnable callback) {
239         DisplayController.DisplayInfoChangeListener listener = (context, info, flags) -> {
240             if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
241                 callback.run();
242             }
243         };
244         mDisplayController.addChangeListener(listener);
245         callback.run();
246         runOnDestroy(() -> mDisplayController.removeChangeListener(listener));
247     }
248 
249     @Override
onDisplayInfoChanged(Context context, Info info, int flags)250     public void onDisplayInfoChanged(Context context, Info info, int flags) {
251         if ((flags & (CHANGE_ROTATION | CHANGE_NAVIGATION_MODE)) != 0) {
252             mMode = info.getNavigationMode();
253             ActiveGestureLog.INSTANCE.setIsFullyGesturalNavMode(isFullyGesturalNavMode());
254             mNavBarPosition = new NavBarPosition(mMode, info);
255 
256             if (mMode == NO_BUTTON) {
257                 registerExclusionListener();
258             } else {
259                 unregisterExclusionListener();
260             }
261         }
262     }
263 
264     @Override
onGestureExclusionChanged(@ullable Region exclusionRegion, @Nullable Region unrestrictedOrNull)265     public void onGestureExclusionChanged(@Nullable Region exclusionRegion,
266             @Nullable Region unrestrictedOrNull) {
267         mExclusionRegion = exclusionRegion != null
268                 ? exclusionRegion : GestureExclusionManager.EMPTY_REGION;
269     }
270 
271     /**
272      * Registers itself for getting exclusion rect changes.
273      */
registerExclusionListener()274     public void registerExclusionListener() {
275         if (mExclusionListenerRegistered) {
276             return;
277         }
278         mExclusionManager.addListener(this);
279         mExclusionListenerRegistered = true;
280     }
281 
282     /**
283      * Unregisters itself as gesture exclusion listener if previously registered.
284      */
unregisterExclusionListener()285     public void unregisterExclusionListener() {
286         if (!mExclusionListenerRegistered) {
287             return;
288         }
289         mExclusionManager.removeListener(this);
290         mExclusionListenerRegistered = false;
291     }
292 
onOneHandedModeChanged(int newGesturalHeight)293     public void onOneHandedModeChanged(int newGesturalHeight) {
294         mRotationTouchHelper.setGesturalHeight(newGesturalHeight);
295     }
296 
297     /**
298      * @return the nav bar position for the current nav bar mode and display rotation.
299      */
getNavBarPosition()300     public NavBarPosition getNavBarPosition() {
301         return mNavBarPosition;
302     }
303 
304     /**
305      * @return whether the current nav mode is fully gestural.
306      */
isFullyGesturalNavMode()307     public boolean isFullyGesturalNavMode() {
308         return mMode == NO_BUTTON;
309     }
310 
311     /**
312      * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
313      */
isGesturalNavMode()314     public boolean isGesturalNavMode() {
315         return mMode.hasGestures;
316     }
317 
318     /**
319      * @return whether the current nav mode is button-based.
320      */
isButtonNavMode()321     public boolean isButtonNavMode() {
322         return mMode == THREE_BUTTONS;
323     }
324 
325     /**
326      * @return the display id for the display that Launcher is running on.
327      */
getDisplayId()328     public int getDisplayId() {
329         return DEFAULT_DISPLAY;
330     }
331 
332     /**
333      * @return whether the user has completed setup wizard
334      */
isUserSetupComplete()335     public boolean isUserSetupComplete() {
336         return mIsUserSetupComplete;
337     }
338 
339     /**
340      * Sets the task id where gestures should be blocked
341      */
setGestureBlockingTaskId(int taskId)342     public void setGestureBlockingTaskId(int taskId) {
343         mGestureBlockingTaskId = taskId;
344     }
345 
346     /**
347      * @return whether the given running task info matches the gesture-blocked task.
348      */
isGestureBlockedTask(CachedTaskInfo taskInfo)349     public boolean isGestureBlockedTask(CachedTaskInfo taskInfo) {
350         return taskInfo != null && taskInfo.getTaskId() == mGestureBlockingTaskId;
351     }
352 
353     /**
354      * Updates the system ui state flags from SystemUI.
355      */
setSystemUiFlags(@ystemUiStateFlags long stateFlags)356     public void setSystemUiFlags(@SystemUiStateFlags long stateFlags) {
357         mSystemUiStateFlags = stateFlags;
358     }
359 
360     /**
361      * @return the system ui state flags.
362      */
363     // TODO(141886704): See if we can remove this
364     @SystemUiStateFlags
getSystemUiStateFlags()365     public long getSystemUiStateFlags() {
366         return mSystemUiStateFlags;
367     }
368 
369     /**
370      * Sets the flag that indicates whether a predictive back-to-home animation is in progress
371      */
setPredictiveBackToHomeInProgress(boolean isInProgress)372     public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
373         mIsPredictiveBackToHomeInProgress = isInProgress;
374     }
375 
376     /**
377      * @return whether a predictive back-to-home animation is currently in progress
378      */
isPredictiveBackToHomeInProgress()379     public boolean isPredictiveBackToHomeInProgress() {
380         return mIsPredictiveBackToHomeInProgress;
381     }
382 
383     /**
384      * @return whether SystemUI is in a state where we can start a system gesture.
385      */
canStartSystemGesture()386     public boolean canStartSystemGesture() {
387         boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
388                 || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
389                 || mRotationTouchHelper.isTaskListFrozen();
390         return canStartWithNavHidden && canStartAnyGesture();
391     }
392 
393     /**
394      * @return whether SystemUI is in a state where we can start a system gesture from the trackpad.
395      * Trackpad gestures can start even when the nav bar / task bar is hidden in sticky immersive
396      * mode.
397      */
canStartTrackpadGesture()398     public boolean canStartTrackpadGesture() {
399         boolean trackpadGesturesEnabled =
400                 (mSystemUiStateFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
401         return trackpadGesturesEnabled && canStartAnyGesture();
402     }
403 
404     /**
405      * Common logic to determine if either trackpad or finger gesture can be started
406      */
canStartAnyGesture()407     private boolean canStartAnyGesture() {
408         boolean homeOrOverviewEnabled = (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
409                 || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
410         long gestureDisablingStates = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
411                         | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
412                         | SYSUI_STATE_QUICK_SETTINGS_EXPANDED
413                         | SYSUI_STATE_MAGNIFICATION_OVERLAP
414                         | SYSUI_STATE_DEVICE_DREAMING
415                         | SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
416         return (gestureDisablingStates & mSystemUiStateFlags) == 0 && homeOrOverviewEnabled;
417     }
418 
419     /**
420      * @return whether the keyguard is showing and is occluded by an app showing above the keyguard
421      *         (like camera or maps)
422      */
isKeyguardShowingOccluded()423     public boolean isKeyguardShowingOccluded() {
424         return (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
425     }
426 
427     /**
428      * @return whether screen pinning is enabled and active
429      */
isScreenPinningActive()430     public boolean isScreenPinningActive() {
431         return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
432     }
433 
434     /**
435      * @return whether assistant gesture is constraint
436      */
isAssistantGestureIsConstrained()437     public boolean isAssistantGestureIsConstrained() {
438         return (mSystemUiStateFlags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
439     }
440 
441     /**
442      * @return whether the bubble stack is expanded
443      */
isBubblesExpanded()444     public boolean isBubblesExpanded() {
445         return (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
446     }
447 
448     /**
449      * @return whether the global actions dialog is showing
450      */
isSystemUiDialogShowing()451     public boolean isSystemUiDialogShowing() {
452         return (mSystemUiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0;
453     }
454 
455     /**
456      * @return whether lock-task mode is active
457      */
isLockToAppActive()458     public boolean isLockToAppActive() {
459         return ActivityManagerWrapper.getInstance().isLockToAppActive();
460     }
461 
462     /**
463      * @return whether the accessibility menu is available.
464      */
isAccessibilityMenuAvailable()465     public boolean isAccessibilityMenuAvailable() {
466         return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
467     }
468 
469     /**
470      * @return whether the accessibility menu shortcut is available.
471      */
isAccessibilityMenuShortcutAvailable()472     public boolean isAccessibilityMenuShortcutAvailable() {
473         return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
474     }
475 
476     /**
477      * @return whether home is disabled (either by SUW/SysUI/device policy)
478      */
isHomeDisabled()479     public boolean isHomeDisabled() {
480         return (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
481     }
482 
483     /**
484      * @return whether overview is disabled (either by SUW/SysUI/device policy)
485      */
isOverviewDisabled()486     public boolean isOverviewDisabled() {
487         return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
488     }
489 
490     /**
491      * @return whether one-handed mode is enabled and active
492      */
isOneHandedModeActive()493     public boolean isOneHandedModeActive() {
494         return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
495     }
496 
497     /**
498      * Sets the region in screen space where the gestures should be deferred (ie. due to specific
499      * nav bar ui).
500      */
setDeferredGestureRegion(Region deferredGestureRegion)501     public void setDeferredGestureRegion(Region deferredGestureRegion) {
502         mDeferredGestureRegion.set(deferredGestureRegion);
503     }
504 
505     /**
506      * @return whether the given {@param event} is in the deferred gesture region indicating that
507      *         the Launcher should not immediately start the recents animation until the gesture
508      *         passes a certain threshold.
509      */
isInDeferredGestureRegion(MotionEvent event)510     public boolean isInDeferredGestureRegion(MotionEvent event) {
511         return mDeferredGestureRegion.contains((int) event.getX(), (int) event.getY());
512     }
513 
514     /**
515      * @return whether the given {@param event} is in the app-requested gesture-exclusion region.
516      *         This is only used for quickswitch, and not swipe up.
517      */
isInExclusionRegion(MotionEvent event)518     public boolean isInExclusionRegion(MotionEvent event) {
519         return mMode == NO_BUTTON
520                 && mExclusionRegion.contains((int) event.getX(), (int) event.getY());
521     }
522 
523     /**
524      * Sets whether the assistant is available.
525      */
setAssistantAvailable(boolean assistantAvailable)526     public void setAssistantAvailable(boolean assistantAvailable) {
527         mAssistantAvailable = assistantAvailable;
528     }
529 
530     /**
531      * Sets the visibility fraction of the assistant.
532      */
setAssistantVisibility(float visibility)533     public void setAssistantVisibility(float visibility) {
534         mAssistantVisibility = visibility;
535     }
536 
537     /**
538      * @return the visibility fraction of the assistant.
539      */
getAssistantVisibility()540     public float getAssistantVisibility() {
541         return mAssistantVisibility;
542     }
543 
544     /**
545      * @param ev An ACTION_DOWN motion event
546      * @return whether the given motion event can trigger the assistant over the current task.
547      */
canTriggerAssistantAction(MotionEvent ev)548     public boolean canTriggerAssistantAction(MotionEvent ev) {
549         return mAssistantAvailable
550                 && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
551                 && mRotationTouchHelper.touchInAssistantRegion(ev)
552                 && !isLockToAppActive();
553     }
554 
555     /**
556      * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
557      *
558      * @param ev The touch screen motion event.
559      * @return whether the given motion event can trigger the one handed mode.
560      */
canTriggerOneHandedAction(MotionEvent ev)561     public boolean canTriggerOneHandedAction(MotionEvent ev) {
562         if (!mIsOneHandedModeSupported) {
563             return false;
564         }
565 
566         if (mIsOneHandedModeEnabled) {
567             final Info displayInfo = mDisplayController.getInfo();
568             return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
569                     && (displayInfo.currentSize.x < displayInfo.currentSize.y));
570         }
571         return false;
572     }
573 
isOneHandedModeEnabled()574     public boolean isOneHandedModeEnabled() {
575         return mIsOneHandedModeEnabled;
576     }
577 
isSwipeToNotificationEnabled()578     public boolean isSwipeToNotificationEnabled() {
579         return mIsSwipeToNotificationEnabled;
580     }
581 
isPipActive()582     public boolean isPipActive() {
583         return mPipIsActive;
584     }
585 
getRotationTouchHelper()586     public RotationTouchHelper getRotationTouchHelper() {
587         return mRotationTouchHelper;
588     }
589 
590     /** Returns whether IME is rendering nav buttons, and IME is currently showing. */
isImeRenderingNavButtons()591     public boolean isImeRenderingNavButtons() {
592         return mCanImeRenderGesturalNavButtons && mMode == NO_BUTTON
593                 && ((mSystemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0);
594     }
595 
596     /**
597      * Returns the touch slop for {@link InputConsumer}s to compare against before pilfering
598      * pointers.
599      */
getTouchSlop()600     public float getTouchSlop() {
601         float slopMultiplier = isFullyGesturalNavMode()
602                 ? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL
603                 : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
604         float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
605 
606         if (mAssistStateManager.getLPNHCustomSlopMultiplier().isPresent()) {
607             float customSlopMultiplier = mAssistStateManager.getLPNHCustomSlopMultiplier().get();
608             return customSlopMultiplier * slopMultiplier * touchSlop;
609         } else {
610             return slopMultiplier * touchSlop;
611         }
612     }
613 
614     /**
615      * Returns the squared touch slop for {@link InputConsumer}s to compare against before pilfering
616      * pointers. Note that this is squared because it expects to be compared against
617      * {@link com.android.launcher3.Utilities#squaredHypot} (to avoid square root on each event).
618      */
getSquaredTouchSlop()619     public float getSquaredTouchSlop() {
620         float touchSlop = getTouchSlop();
621         return touchSlop * touchSlop;
622     }
623 
getSystemUiStateString()624     public String getSystemUiStateString() {
625         return  QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
626     }
627 
dump(PrintWriter pw)628     public void dump(PrintWriter pw) {
629         pw.println("DeviceState:");
630         pw.println("  canStartSystemGesture=" + canStartSystemGesture());
631         pw.println("  systemUiFlags=" + mSystemUiStateFlags);
632         pw.println("  systemUiFlagsDesc=" + getSystemUiStateString());
633         pw.println("  assistantAvailable=" + mAssistantAvailable);
634         pw.println("  assistantDisabled="
635                 + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
636         pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
637         pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
638         pw.println("  deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
639         pw.println("  exclusionRegion=" + mExclusionRegion.getBounds());
640         pw.println("  pipIsActive=" + mPipIsActive);
641         pw.println("  predictiveBackToHomeInProgress=" + mIsPredictiveBackToHomeInProgress);
642         mRotationTouchHelper.dump(pw);
643     }
644 }
645