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