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.content.Intent.ACTION_USER_UNLOCKED;
21 import static android.view.Display.DEFAULT_DISPLAY;
22 
23 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
24 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
25 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
26 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
27 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
28 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
29 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
30 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
31 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
33 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
34 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
35 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
41 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
42 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
45 
46 import android.app.ActivityManager;
47 import android.app.ActivityTaskManager;
48 import android.content.BroadcastReceiver;
49 import android.content.ComponentName;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.IntentFilter;
53 import android.content.res.Resources;
54 import android.graphics.Region;
55 import android.net.Uri;
56 import android.os.Process;
57 import android.os.RemoteException;
58 import android.os.SystemProperties;
59 import android.os.UserManager;
60 import android.provider.Settings;
61 import android.text.TextUtils;
62 import android.view.MotionEvent;
63 import android.view.Surface;
64 
65 import androidx.annotation.BinderThread;
66 
67 import com.android.launcher3.R;
68 import com.android.launcher3.Utilities;
69 import com.android.launcher3.util.DisplayController;
70 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
71 import com.android.launcher3.util.DisplayController.Info;
72 import com.android.launcher3.util.SettingsCache;
73 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
74 import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener;
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.SystemGestureExclusionListenerCompat;
80 import com.android.systemui.shared.system.TaskStackChangeListener;
81 import com.android.systemui.shared.system.TaskStackChangeListeners;
82 
83 import java.io.PrintWriter;
84 import java.util.ArrayList;
85 import java.util.List;
86 
87 /**
88  * Manages the state of the system during a swipe up gesture.
89  */
90 public class RecentsAnimationDeviceState implements
91         NavigationModeChangeListener,
92         DisplayInfoChangeListener,
93         OneHandedModeChangeListener {
94 
95     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
96 
97     private final Context mContext;
98     private final SysUINavigationMode mSysUiNavMode;
99     private final DisplayController mDisplayController;
100     private final int mDisplayId;
101     private final RotationTouchHelper mRotationTouchHelper;
102     private final TaskStackChangeListener mPipListener;
103     private final List<ComponentName> mGestureBlockedActivities;
104 
105     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
106 
107     private @SystemUiStateFlags int mSystemUiStateFlags;
108     private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
109     private NavBarPosition mNavBarPosition;
110 
111     private final Region mDeferredGestureRegion = new Region();
112     private boolean mAssistantAvailable;
113     private float mAssistantVisibility;
114     private boolean mIsUserSetupComplete;
115     private boolean mIsOneHandedModeEnabled;
116     private boolean mIsSwipeToNotificationEnabled;
117     private final boolean mIsOneHandedModeSupported;
118     private boolean mPipIsActive;
119 
120     private boolean mIsUserUnlocked;
121     private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
122     private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
123         @Override
124         public void onReceive(Context context, Intent intent) {
125             if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
126                 mIsUserUnlocked = true;
127                 notifyUserUnlocked();
128             }
129         }
130     };
131 
132     private Region mExclusionRegion;
133     private SystemGestureExclusionListenerCompat mExclusionListener;
134 
RecentsAnimationDeviceState(Context context)135     public RecentsAnimationDeviceState(Context context) {
136         this(context, false);
137     }
138 
139     /**
140      * @param isInstanceForTouches {@code true} if this is the persistent instance being used for
141      *                                   gesture touch handling
142      */
RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches)143     public RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches) {
144         mContext = context;
145         mDisplayController = DisplayController.INSTANCE.get(context);
146         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
147         mDisplayId = DEFAULT_DISPLAY;
148         mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
149         runOnDestroy(() -> mDisplayController.removeChangeListener(this));
150         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
151         if (isInstanceForTouches) {
152             // rotationTouchHelper doesn't get initialized after being destroyed, so only destroy
153             // if primary TouchInteractionService instance needs to be destroyed.
154             mRotationTouchHelper.init();
155             runOnDestroy(mRotationTouchHelper::destroy);
156         }
157 
158         // Register for user unlocked if necessary
159         mIsUserUnlocked = context.getSystemService(UserManager.class)
160                 .isUserUnlocked(Process.myUserHandle());
161         if (!mIsUserUnlocked) {
162             mContext.registerReceiver(mUserUnlockedReceiver,
163                     new IntentFilter(ACTION_USER_UNLOCKED));
164         }
165         runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
166 
167         // Register for exclusion updates
168         mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
169             @Override
170             @BinderThread
171             public void onExclusionChanged(Region region) {
172                 // Assignments are atomic, it should be safe on binder thread
173                 mExclusionRegion = region;
174             }
175         };
176         runOnDestroy(mExclusionListener::unregister);
177 
178         // Register for navigation mode changes
179         onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
180         runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
181 
182         // Add any blocked activities
183         String[] blockingActivities;
184         try {
185             blockingActivities =
186                     context.getResources().getStringArray(R.array.gesture_blocking_activities);
187         } catch (Resources.NotFoundException e) {
188             blockingActivities = new String[0];
189         }
190         mGestureBlockedActivities = new ArrayList<>(blockingActivities.length);
191         for (String blockingActivity : blockingActivities) {
192             if (!TextUtils.isEmpty(blockingActivity)) {
193                 mGestureBlockedActivities.add(
194                         ComponentName.unflattenFromString(blockingActivity));
195             }
196         }
197 
198         SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
199         if (mIsOneHandedModeSupported) {
200             Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED);
201             SettingsCache.OnChangeListener onChangeListener =
202                     enabled -> mIsOneHandedModeEnabled = enabled;
203             settingsCache.register(oneHandedUri, onChangeListener);
204             mIsOneHandedModeEnabled = settingsCache.getValue(oneHandedUri);
205             runOnDestroy(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
206         } else {
207             mIsOneHandedModeEnabled = false;
208         }
209 
210         Uri swipeBottomNotificationUri =
211                 Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
212         SettingsCache.OnChangeListener onChangeListener =
213                 enabled -> mIsSwipeToNotificationEnabled = enabled;
214         settingsCache.register(swipeBottomNotificationUri, onChangeListener);
215         mIsSwipeToNotificationEnabled = settingsCache.getValue(swipeBottomNotificationUri);
216         runOnDestroy(() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
217 
218         Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
219         mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0);
220         if (!mIsUserSetupComplete) {
221             SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e;
222             settingsCache.register(setupCompleteUri, userSetupChangeListener);
223             runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
224         }
225 
226         try {
227             mPipIsActive = ActivityTaskManager.getService().getRootTaskInfo(
228                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED) != null;
229         } catch (RemoteException e) {
230             // Do nothing
231         }
232         mPipListener = new TaskStackChangeListener() {
233             @Override
234             public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
235                 mPipIsActive = true;
236             }
237 
238             @Override
239             public void onActivityUnpinned() {
240                 mPipIsActive = false;
241             }
242         };
243         TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
244         runOnDestroy(() ->
245                 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
246     }
247 
runOnDestroy(Runnable action)248     private void runOnDestroy(Runnable action) {
249         mOnDestroyActions.add(action);
250     }
251 
252     /**
253      * Cleans up all the registered listeners and receivers.
254      */
destroy()255     public void destroy() {
256         for (Runnable r : mOnDestroyActions) {
257             r.run();
258         }
259     }
260 
261     /**
262      * Adds a listener for the nav mode change, guaranteed to be called after the device state's
263      * mode has changed.
264      */
addNavigationModeChangedCallback(NavigationModeChangeListener listener)265     public void addNavigationModeChangedCallback(NavigationModeChangeListener listener) {
266         listener.onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(listener));
267         runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
268     }
269 
270     /**
271      * Adds a listener for the one handed mode change,
272      * guaranteed to be called after the device state's mode has changed.
273      */
addOneHandedModeChangedCallback(OneHandedModeChangeListener listener)274     public void addOneHandedModeChangedCallback(OneHandedModeChangeListener listener) {
275         listener.onOneHandedModeChanged(mSysUiNavMode.addOneHandedOverlayChangeListener(listener));
276         runOnDestroy(() -> mSysUiNavMode.removeOneHandedOverlayChangeListener(listener));
277     }
278 
279     @Override
onNavigationModeChanged(SysUINavigationMode.Mode newMode)280     public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
281         mDisplayController.removeChangeListener(this);
282         mDisplayController.addChangeListener(this);
283         onDisplayInfoChanged(mContext, mDisplayController.getInfo(), CHANGE_ALL);
284 
285         if (newMode == NO_BUTTON) {
286             mExclusionListener.register();
287         } else {
288             mExclusionListener.unregister();
289         }
290 
291         mNavBarPosition = new NavBarPosition(newMode, mDisplayController.getInfo());
292         mMode = newMode;
293     }
294 
295     @Override
onDisplayInfoChanged(Context context, Info info, int flags)296     public void onDisplayInfoChanged(Context context, Info info, int flags) {
297         if ((flags & CHANGE_ROTATION) != 0) {
298             mNavBarPosition = new NavBarPosition(mMode, info);
299         }
300     }
301 
302     @Override
onOneHandedModeChanged(int newGesturalHeight)303     public void onOneHandedModeChanged(int newGesturalHeight) {
304         mRotationTouchHelper.setGesturalHeight(newGesturalHeight);
305     }
306 
307     /**
308      * @return the current navigation mode for the device.
309      */
getNavMode()310     public SysUINavigationMode.Mode getNavMode() {
311         return mMode;
312     }
313 
314     /**
315      * @return the nav bar position for the current nav bar mode and display rotation.
316      */
getNavBarPosition()317     public NavBarPosition getNavBarPosition() {
318         return mNavBarPosition;
319     }
320 
321     /**
322      * @return whether the current nav mode is fully gestural.
323      */
isFullyGesturalNavMode()324     public boolean isFullyGesturalNavMode() {
325         return mMode == NO_BUTTON;
326     }
327 
328     /**
329      * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
330      */
isGesturalNavMode()331     public boolean isGesturalNavMode() {
332         return mMode.hasGestures;
333     }
334 
335     /**
336      * @return whether the current nav mode is 2-button-based.
337      */
isTwoButtonNavMode()338     public boolean isTwoButtonNavMode() {
339         return mMode == TWO_BUTTONS;
340     }
341 
342     /**
343      * @return whether the current nav mode is button-based.
344      */
isButtonNavMode()345     public boolean isButtonNavMode() {
346         return mMode == THREE_BUTTONS;
347     }
348 
349     /**
350      * @return the display id for the display that Launcher is running on.
351      */
getDisplayId()352     public int getDisplayId() {
353         return mDisplayId;
354     }
355 
356     /**
357      * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
358      * will be called back immediately.
359      */
runOnUserUnlocked(Runnable action)360     public void runOnUserUnlocked(Runnable action) {
361         if (mIsUserUnlocked) {
362             action.run();
363         } else {
364             mUserUnlockedActions.add(action);
365         }
366     }
367 
368     /**
369      * @return whether the user is unlocked.
370      */
isUserUnlocked()371     public boolean isUserUnlocked() {
372         return mIsUserUnlocked;
373     }
374 
375     /**
376      * @return whether the user has completed setup wizard
377      */
isUserSetupComplete()378     public boolean isUserSetupComplete() {
379         return mIsUserSetupComplete;
380     }
381 
notifyUserUnlocked()382     private void notifyUserUnlocked() {
383         for (Runnable action : mUserUnlockedActions) {
384             action.run();
385         }
386         mUserUnlockedActions.clear();
387         Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
388     }
389 
390     /**
391      * @return whether the given running task info matches the gesture-blocked activity.
392      */
isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo)393     public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) {
394         return runningTaskInfo != null
395                 && mGestureBlockedActivities.contains(runningTaskInfo.topActivity);
396     }
397 
398     /**
399      * Updates the system ui state flags from SystemUI.
400      */
setSystemUiFlags(int stateFlags)401     public void setSystemUiFlags(int stateFlags) {
402         mSystemUiStateFlags = stateFlags;
403     }
404 
405     /**
406      * @return the system ui state flags.
407      */
408     // TODO(141886704): See if we can remove this
getSystemUiStateFlags()409     public int getSystemUiStateFlags() {
410         return mSystemUiStateFlags;
411     }
412 
413     /**
414      * @return whether SystemUI is in a state where we can start a system gesture.
415      */
canStartSystemGesture()416     public boolean canStartSystemGesture() {
417         boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
418                 || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
419                 || mRotationTouchHelper.isTaskListFrozen();
420         return canStartWithNavHidden
421                 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
422                 && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
423                 && (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0
424                 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
425                         || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
426     }
427 
428     /**
429      * @return whether the keyguard is showing and is occluded by an app showing above the keyguard
430      *         (like camera or maps)
431      */
isKeyguardShowingOccluded()432     public boolean isKeyguardShowingOccluded() {
433         return (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
434     }
435 
436     /**
437      * @return whether screen pinning is enabled and active
438      */
isScreenPinningActive()439     public boolean isScreenPinningActive() {
440         return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
441     }
442 
443     /**
444      * @return whether assistant gesture is constraint
445      */
isAssistantGestureIsConstrained()446     public boolean isAssistantGestureIsConstrained() {
447         return (mSystemUiStateFlags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
448     }
449 
450     /**
451      * @return whether the bubble stack is expanded
452      */
isBubblesExpanded()453     public boolean isBubblesExpanded() {
454         return (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
455     }
456 
457     /**
458      * @return whether the global actions dialog is showing
459      */
isGlobalActionsShowing()460     public boolean isGlobalActionsShowing() {
461         return (mSystemUiStateFlags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0;
462     }
463 
464     /**
465      * @return whether lock-task mode is active
466      */
isLockToAppActive()467     public boolean isLockToAppActive() {
468         return ActivityManagerWrapper.getInstance().isLockToAppActive();
469     }
470 
471     /**
472      * @return whether the accessibility menu is available.
473      */
isAccessibilityMenuAvailable()474     public boolean isAccessibilityMenuAvailable() {
475         return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
476     }
477 
478     /**
479      * @return whether the accessibility menu shortcut is available.
480      */
isAccessibilityMenuShortcutAvailable()481     public boolean isAccessibilityMenuShortcutAvailable() {
482         return (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
483     }
484 
485     /**
486      * @return whether home is disabled (either by SUW/SysUI/device policy)
487      */
isHomeDisabled()488     public boolean isHomeDisabled() {
489         return (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
490     }
491 
492     /**
493      * @return whether overview is disabled (either by SUW/SysUI/device policy)
494      */
isOverviewDisabled()495     public boolean isOverviewDisabled() {
496         return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
497     }
498 
499     /**
500      * @return whether one-handed mode is enabled and active
501      */
isOneHandedModeActive()502     public boolean isOneHandedModeActive() {
503         return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
504     }
505 
506     /**
507      * Sets the region in screen space where the gestures should be deferred (ie. due to specific
508      * nav bar ui).
509      */
setDeferredGestureRegion(Region deferredGestureRegion)510     public void setDeferredGestureRegion(Region deferredGestureRegion) {
511         mDeferredGestureRegion.set(deferredGestureRegion);
512     }
513 
514     /**
515      * @return whether the given {@param event} is in the deferred gesture region indicating that
516      *         the Launcher should not immediately start the recents animation until the gesture
517      *         passes a certain threshold.
518      */
isInDeferredGestureRegion(MotionEvent event)519     public boolean isInDeferredGestureRegion(MotionEvent event) {
520         return mDeferredGestureRegion.contains((int) event.getX(), (int) event.getY());
521     }
522 
523     /**
524      * @return whether the given {@param event} is in the app-requested gesture-exclusion region.
525      *         This is only used for quickswitch, and not swipe up.
526      */
isInExclusionRegion(MotionEvent event)527     public boolean isInExclusionRegion(MotionEvent event) {
528         // mExclusionRegion can change on binder thread, use a local instance here.
529         Region exclusionRegion = mExclusionRegion;
530         return mMode == NO_BUTTON && exclusionRegion != null
531                 && exclusionRegion.contains((int) event.getX(), (int) event.getY());
532     }
533 
534     /**
535      * Sets whether the assistant is available.
536      */
setAssistantAvailable(boolean assistantAvailable)537     public void setAssistantAvailable(boolean assistantAvailable) {
538         mAssistantAvailable = assistantAvailable;
539     }
540 
541     /**
542      * Sets the visibility fraction of the assistant.
543      */
setAssistantVisibility(float visibility)544     public void setAssistantVisibility(float visibility) {
545         mAssistantVisibility = visibility;
546     }
547 
548     /**
549      * @return the visibility fraction of the assistant.
550      */
getAssistantVisibility()551     public float getAssistantVisibility() {
552         return mAssistantVisibility;
553     }
554 
555     /**
556      * @param ev An ACTION_DOWN motion event
557      * @param task Info for the currently running task
558      * @return whether the given motion event can trigger the assistant over the current task.
559      */
canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task)560     public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) {
561         return mAssistantAvailable
562                 && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
563                 && mRotationTouchHelper.touchInAssistantRegion(ev)
564                 && !isLockToAppActive()
565                 && !isGestureBlockedActivity(task);
566     }
567 
568     /**
569      * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
570      *
571      * @param ev The touch screen motion event.
572      * @return whether the given motion event can trigger the one handed mode.
573      */
canTriggerOneHandedAction(MotionEvent ev)574     public boolean canTriggerOneHandedAction(MotionEvent ev) {
575         if (!mIsOneHandedModeSupported) {
576             return false;
577         }
578 
579         if (mIsOneHandedModeEnabled) {
580             final Info displayInfo = mDisplayController.getInfo();
581             return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
582                 && displayInfo.rotation != Surface.ROTATION_90
583                 && displayInfo.rotation != Surface.ROTATION_270);
584         }
585         return false;
586     }
587 
isOneHandedModeEnabled()588     public boolean isOneHandedModeEnabled() {
589         return mIsOneHandedModeEnabled;
590     }
591 
isSwipeToNotificationEnabled()592     public boolean isSwipeToNotificationEnabled() {
593         return mIsSwipeToNotificationEnabled;
594     }
595 
isPipActive()596     public boolean isPipActive() {
597         return mPipIsActive;
598     }
599 
getRotationTouchHelper()600     public RotationTouchHelper getRotationTouchHelper() {
601         return mRotationTouchHelper;
602     }
603 
dump(PrintWriter pw)604     public void dump(PrintWriter pw) {
605         pw.println("DeviceState:");
606         pw.println("  canStartSystemGesture=" + canStartSystemGesture());
607         pw.println("  systemUiFlags=" + mSystemUiStateFlags);
608         pw.println("  systemUiFlagsDesc="
609                 + QuickStepContract.getSystemUiStateString(mSystemUiStateFlags));
610         pw.println("  assistantAvailable=" + mAssistantAvailable);
611         pw.println("  assistantDisabled="
612                 + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
613         pw.println("  isUserUnlocked=" + mIsUserUnlocked);
614         pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
615         pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
616         pw.println("  deferredGestureRegion=" + mDeferredGestureRegion);
617         pw.println("  pipIsActive=" + mPipIsActive);
618         mRotationTouchHelper.dump(pw);
619     }
620 }
621