• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.systemui.navigationbar;
18 
19 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
20 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
21 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
22 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
23 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
24 import static android.app.StatusBarManager.WindowType;
25 import static android.app.StatusBarManager.WindowVisibleState;
26 import static android.app.StatusBarManager.windowStateToString;
27 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
28 import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
29 import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
30 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
31 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
32 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
33 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
34 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
35 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
36 
37 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
38 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
39 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
40 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
41 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
42 import static com.android.systemui.shared.rotation.RotationButtonController.DEBUG_ROTATION;
43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
45 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
46 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
47 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
48 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
49 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
50 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
51 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
52 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
53 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE;
54 import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions;
55 import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
56 
57 import android.annotation.IdRes;
58 import android.annotation.NonNull;
59 import android.app.ActivityTaskManager;
60 import android.app.IActivityTaskManager;
61 import android.app.StatusBarManager;
62 import android.content.Context;
63 import android.content.Intent;
64 import android.content.res.Configuration;
65 import android.graphics.Insets;
66 import android.graphics.PixelFormat;
67 import android.graphics.Point;
68 import android.graphics.Rect;
69 import android.graphics.RectF;
70 import android.graphics.Region;
71 import android.os.Binder;
72 import android.os.Bundle;
73 import android.os.Handler;
74 import android.os.IBinder;
75 import android.os.RemoteException;
76 import android.os.Trace;
77 import android.provider.DeviceConfig;
78 import android.telecom.TelecomManager;
79 import android.text.TextUtils;
80 import android.util.Log;
81 import android.view.Display;
82 import android.view.Gravity;
83 import android.view.HapticFeedbackConstants;
84 import android.view.InsetsFrameProvider;
85 import android.view.KeyEvent;
86 import android.view.MotionEvent;
87 import android.view.Surface;
88 import android.view.View;
89 import android.view.ViewConfiguration;
90 import android.view.ViewTreeObserver;
91 import android.view.ViewTreeObserver.InternalInsetsInfo;
92 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
93 import android.view.WindowInsets;
94 import android.view.WindowInsets.Type.InsetsType;
95 import android.view.WindowInsetsController.Appearance;
96 import android.view.WindowInsetsController.Behavior;
97 import android.view.WindowManager;
98 import android.view.accessibility.AccessibilityEvent;
99 import android.view.accessibility.AccessibilityManager;
100 import android.view.inputmethod.InputMethodManager;
101 
102 import androidx.annotation.Nullable;
103 import androidx.annotation.VisibleForTesting;
104 
105 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
106 import com.android.internal.logging.MetricsLogger;
107 import com.android.internal.logging.UiEvent;
108 import com.android.internal.logging.UiEventLogger;
109 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
110 import com.android.internal.statusbar.LetterboxDetails;
111 import com.android.internal.util.LatencyTracker;
112 import com.android.internal.view.AppearanceRegion;
113 import com.android.systemui.Gefingerpoken;
114 import com.android.systemui.assist.AssistManager;
115 import com.android.systemui.dagger.qualifiers.Background;
116 import com.android.systemui.dagger.qualifiers.DisplayId;
117 import com.android.systemui.dagger.qualifiers.Main;
118 import com.android.systemui.keyguard.WakefulnessLifecycle;
119 import com.android.systemui.model.SysUiState;
120 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
121 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener;
122 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
123 import com.android.systemui.navigationbar.buttons.DeadZone;
124 import com.android.systemui.navigationbar.buttons.KeyButtonView;
125 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
126 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
127 import com.android.systemui.plugins.statusbar.StatusBarStateController;
128 import com.android.systemui.recents.OverviewProxyService;
129 import com.android.systemui.recents.Recents;
130 import com.android.systemui.res.R;
131 import com.android.systemui.settings.DisplayTracker;
132 import com.android.systemui.settings.UserContextProvider;
133 import com.android.systemui.settings.UserTracker;
134 import com.android.systemui.shade.ShadeViewController;
135 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
136 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
137 import com.android.systemui.shared.recents.utilities.Utilities;
138 import com.android.systemui.shared.rotation.RotationButtonController;
139 import com.android.systemui.shared.system.QuickStepContract;
140 import com.android.systemui.shared.system.SysUiStatsLog;
141 import com.android.systemui.shared.system.TaskStackChangeListener;
142 import com.android.systemui.shared.system.TaskStackChangeListeners;
143 import com.android.systemui.statusbar.AutoHideUiElement;
144 import com.android.systemui.statusbar.CommandQueue;
145 import com.android.systemui.statusbar.CommandQueue.Callbacks;
146 import com.android.systemui.statusbar.NotificationRemoteInputManager;
147 import com.android.systemui.statusbar.NotificationShadeDepthController;
148 import com.android.systemui.statusbar.StatusBarState;
149 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
150 import com.android.systemui.statusbar.phone.AutoHideController;
151 import com.android.systemui.statusbar.phone.BarTransitions;
152 import com.android.systemui.statusbar.phone.CentralSurfaces;
153 import com.android.systemui.statusbar.phone.LightBarController;
154 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
155 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
156 import com.android.systemui.statusbar.policy.KeyguardStateController;
157 import com.android.systemui.util.DeviceConfigProxy;
158 import com.android.systemui.util.ViewController;
159 import com.android.wm.shell.back.BackAnimation;
160 import com.android.wm.shell.pip.Pip;
161 
162 import dagger.Lazy;
163 
164 import java.io.PrintWriter;
165 import java.util.Locale;
166 import java.util.Map;
167 import java.util.Optional;
168 import java.util.concurrent.Executor;
169 
170 import javax.inject.Inject;
171 
172 /**
173  * Contains logic for a navigation bar view.
174  */
175 @NavigationBarScope
176 public class NavigationBar extends ViewController<NavigationBarView> implements Callbacks {
177 
178     public static final String TAG = "NavigationBar";
179     private static final boolean DEBUG = false;
180     private static final String EXTRA_DISABLE_STATE = "disabled_state";
181     private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
182     private static final String EXTRA_APPEARANCE = "appearance";
183     private static final String EXTRA_BEHAVIOR = "behavior";
184     private static final String EXTRA_TRANSIENT_STATE = "transient_state";
185 
186     /** Allow some time inbetween the long press for back and recents. */
187     private static final int LOCK_TO_APP_GESTURE_TOLERANCE = 200;
188     private static final long AUTODIM_TIMEOUT_MS = 2250;
189     private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
190 
191     private final Context mContext;
192     private final Bundle mSavedState;
193     private final WindowManager mWindowManager;
194     private final AccessibilityManager mAccessibilityManager;
195     private final DeviceProvisionedController mDeviceProvisionedController;
196     private final StatusBarStateController mStatusBarStateController;
197     private final MetricsLogger mMetricsLogger;
198     private final Lazy<AssistManager> mAssistManagerLazy;
199     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
200     private final SysUiState mSysUiFlagsContainer;
201     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
202     private final KeyguardStateController mKeyguardStateController;
203     private final ShadeViewController mShadeViewController;
204     private final PanelExpansionInteractor mPanelExpansionInteractor;
205     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
206     private final OverviewProxyService mOverviewProxyService;
207     private final NavigationModeController mNavigationModeController;
208     private final UserTracker mUserTracker;
209     private final CommandQueue mCommandQueue;
210     private final Optional<Pip> mPipOptional;
211     private final Optional<Recents> mRecentsOptional;
212     private final DeviceConfigProxy mDeviceConfigProxy;
213     private final NavigationBarTransitions mNavigationBarTransitions;
214     private final Optional<BackAnimation> mBackAnimation;
215     private final Handler mHandler;
216     private final UiEventLogger mUiEventLogger;
217     private final NavBarHelper mNavBarHelper;
218     private final NotificationShadeDepthController mNotificationShadeDepthController;
219     private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
220     private final UserContextProvider mUserContextProvider;
221     private final WakefulnessLifecycle mWakefulnessLifecycle;
222     private final DisplayTracker mDisplayTracker;
223     private final RegionSamplingHelper mRegionSamplingHelper;
224     private final int mNavColorSampleMargin;
225     private EdgeBackGestureHandler mEdgeBackGestureHandler;
226     private NavigationBarFrame mFrame;
227     private MotionEvent mCurrentDownEvent;
228 
229     private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
230 
231     private int mNavigationIconHints = 0;
232     private @TransitionMode int mTransitionMode;
233     private boolean mLongPressHomeEnabled;
234 
235     private int mDisabledFlags1;
236     private int mDisabledFlags2;
237     private long mLastLockToAppLongPress;
238 
239     private Locale mLocale;
240     private int mLayoutDirection;
241 
242     private Optional<Long> mHomeButtonLongPressDurationMs;
243     private Optional<Long> mOverrideHomeButtonLongPressDurationMs = Optional.empty();
244     private Optional<Float> mOverrideHomeButtonLongPressSlopMultiplier = Optional.empty();
245     private boolean mHomeButtonLongPressHapticEnabled = true;
246 
247     /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
248     private @Appearance int mAppearance;
249 
250     /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */
251     private @Behavior int mBehavior;
252 
253     private boolean mTransientShown;
254     private boolean mTransientShownFromGestureOnSystemBar;
255     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
256     private LightBarController mLightBarController;
257     private final LightBarController mMainLightBarController;
258     private final LightBarController.Factory mLightBarControllerFactory;
259     private AutoHideController mAutoHideController;
260     private final AutoHideController mMainAutoHideController;
261     private final AutoHideController.Factory mAutoHideControllerFactory;
262     private final Optional<TelecomManager> mTelecomManagerOptional;
263     private final InputMethodManager mInputMethodManager;
264     private final TaskStackChangeListeners mTaskStackChangeListeners;
265 
266     @VisibleForTesting
267     public int mDisplayId;
268     private boolean mIsOnDefaultDisplay;
269     private boolean mHomeBlockedThisTouch;
270 
271     /**
272      * When user is QuickSwitching between apps of different orientations, we'll draw a fake
273      * home handle on the orientation they originally touched down to start their swipe
274      * gesture to indicate to them that they can continue in that orientation without having to
275      * rotate the phone
276      * The secondary handle will show when we get
277      * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the
278      * original handle hidden and we'll flip the visibilities once the
279      * {@link #mTasksFrozenListener} fires
280      */
281     private QuickswitchOrientedNavHandle mOrientationHandle;
282     private WindowManager.LayoutParams mOrientationParams;
283     private int mStartingQuickSwitchRotation = -1;
284     private int mCurrentRotation;
285     private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener;
286     private boolean mShowOrientedHandleForImmersiveMode;
287     private final DeadZone mDeadZone;
288     private boolean mImeVisible;
289     private final Rect mSamplingBounds = new Rect();
290     private final Binder mInsetsSourceOwner = new Binder();
291     private final NavBarButtonClickLogger mNavBarButtonClickLogger;
292     private final NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger;
293 
294     /**
295      * When quickswitching between apps of different orientations, we draw a secondary home handle
296      * in the position of the first app's orientation. This rect represents the region of that
297      * home handle so we can apply the correct light/dark luma on that.
298      * @see {@link NavigationBar#mOrientationHandle}
299      */
300     @android.annotation.Nullable
301     private Rect mOrientedHandleSamplingRegion;
302 
303     @com.android.internal.annotations.VisibleForTesting
304     public enum NavBarActionEvent implements UiEventLogger.UiEventEnum {
305 
306         @UiEvent(doc = "Assistant invoked via home button long press.")
307         NAVBAR_ASSIST_LONGPRESS(550);
308 
309         private final int mId;
310 
NavBarActionEvent(int id)311         NavBarActionEvent(int id) {
312             mId = id;
313         }
314 
315         @Override
getId()316         public int getId() {
317             return mId;
318         }
319     }
320 
321     private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
322         @Override
323         public void synchronizeState() {
324             checkNavBarModes();
325         }
326 
327         @Override
328         public boolean shouldHideOnTouch() {
329             return !mNotificationRemoteInputManager.isRemoteInputActive();
330         }
331 
332         @Override
333         public boolean isVisible() {
334             return isTransientShown();
335         }
336 
337         @Override
338         public void hide() {
339             clearTransient();
340         }
341     };
342 
343     private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
344             new NavBarHelper.NavbarTaskbarStateUpdater() {
345                 @Override
346                 public void updateAccessibilityServicesState() {
347                     updateAccessibilityStateFlags();
348                 }
349 
350                 @Override
351                 public void updateAssistantAvailable(boolean available,
352                         boolean longPressHomeEnabled) {
353                     // TODO(b/198002034): Content observers currently can still be called back after
354                     //  being unregistered, and in this case we can ignore the change if the nav bar
355                     //  has been destroyed already
356                     if (mView == null) {
357                         return;
358                     }
359                     mLongPressHomeEnabled = longPressHomeEnabled;
360                     updateAssistantEntrypoints(available, longPressHomeEnabled);
361                 }
362 
363                 @Override
364                 public void updateWallpaperVisibility(boolean visible, int displayId) {
365                     mNavigationBarTransitions.setWallpaperVisibility(visible);
366                 }
367 
368                 @Override
369                 public void updateRotationWatcherState(int rotation) {
370                     if (mIsOnDefaultDisplay && mView != null) {
371                         mView.getRotationButtonController().onRotationWatcherChanged(rotation);
372                         if (mView.needsReorient(rotation)) {
373                             repositionNavigationBar(rotation);
374                         }
375                     }
376                 }
377             };
378 
379     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
380         @Override
381         public void onConnectionChanged(boolean isConnected) {
382             mView.onOverviewProxyConnectionChange(
383                     mOverviewProxyService.isEnabled());
384             mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
385             updateScreenPinningGestures();
386         }
387 
388         @Override
389         public void onPrioritizedRotation(@Surface.Rotation int rotation) {
390             mStartingQuickSwitchRotation = rotation;
391             if (rotation == -1) {
392                 mShowOrientedHandleForImmersiveMode = false;
393             }
394             orientSecondaryHomeHandle();
395         }
396 
397         @Override
398         public void startAssistant(Bundle bundle) {
399             mAssistManagerLazy.get().startAssist(bundle);
400         }
401 
402         @Override
403         public void setAssistantOverridesRequested(int[] invocationTypes) {
404             mAssistManagerLazy.get().setAssistantOverridesRequested(invocationTypes);
405         }
406 
407         @Override
408         public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
409             mView.getHomeHandle().animateLongPress(isTouchDown, shrink, durationMs);
410         }
411 
412         @Override
413         public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier,
414                 boolean haptic) {
415             Log.d(TAG, "setOverrideHomeButtonLongPress receives: " + duration + ";"
416                     + slopMultiplier + ";" + haptic);
417             mOverrideHomeButtonLongPressDurationMs = Optional.of(duration)
418                     .filter(value -> value > 0);
419             mOverrideHomeButtonLongPressSlopMultiplier = Optional.of(slopMultiplier)
420                     .filter(value -> value > 0);
421             mHomeButtonLongPressHapticEnabled = haptic;
422             mOverrideHomeButtonLongPressDurationMs.ifPresent(aLong
423                     -> Log.d(TAG, "Use duration override: " + aLong));
424             mOverrideHomeButtonLongPressSlopMultiplier.ifPresent(aFloat
425                     -> Log.d(TAG, "Use slop multiplier override: " + aFloat));
426             if (mView != null) {
427                 reconfigureHomeLongClick();
428             }
429         }
430 
431         @Override
432         public void onHomeRotationEnabled(boolean enabled) {
433             mView.getRotationButtonController().setHomeRotationEnabled(enabled);
434         }
435 
436         @Override
437         public void onOverviewShown(boolean fromHome) {
438             // If the overview has fixed orientation that may change display to natural rotation,
439             // we don't want the user rotation to be reset. So after user returns to application,
440             // it can keep in the original rotation.
441             mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
442         }
443 
444         @Override
445         public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
446             mView.getFloatingRotationButton().onTaskbarStateChanged(visible, stashed);
447         }
448 
449         @Override
450         public void onToggleRecentApps() {
451             // The same case as onOverviewShown but only for 3-button navigation.
452             mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
453         }
454     };
455 
456     private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener =
457             new NavigationBarTransitions.DarkIntensityListener() {
458                 @Override
459                 public void onDarkIntensity(float darkIntensity) {
460                     mOrientationHandle.setDarkIntensity(darkIntensity);
461                 }
462             };
463 
464     private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
465     private final Runnable mEnableLayoutTransitions = () -> mView.setLayoutTransitionsEnabled(true);
466     private final Runnable mOnVariableDurationHomeLongClick = () -> {
467         if (onHomeLongClick(mView.getHomeButton().getCurrentView())) {
468             if (mHomeButtonLongPressHapticEnabled) {
469                 mView.getHomeButton().getCurrentView().performHapticFeedback(
470                         HapticFeedbackConstants.LONG_PRESS,
471                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
472             }
473         }
474     };
475 
476     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
477             new DeviceConfig.OnPropertiesChangedListener() {
478                 @Override
479                 public void onPropertiesChanged(DeviceConfig.Properties properties) {
480                     if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
481                         mHomeButtonLongPressDurationMs = Optional.of(
482                             properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0))
483                                 .filter(duration -> duration != 0);
484                         if (mView != null) {
485                             reconfigureHomeLongClick();
486                         }
487                     }
488                 }
489             };
490 
491     private final NotificationShadeDepthController.DepthListener mDepthListener =
492             new NotificationShadeDepthController.DepthListener() {
493                 boolean mHasBlurs;
494 
495                 @Override
496                 public void onWallpaperZoomOutChanged(float zoomOut) {
497                 }
498 
499                 @Override
500                 public void onBlurRadiusChanged(int radius) {
501                     boolean hasBlurs = radius != 0;
502                     if (hasBlurs == mHasBlurs) {
503                         return;
504                     }
505                     mHasBlurs = hasBlurs;
506                     mRegionSamplingHelper.setWindowHasBlurs(hasBlurs);
507                 }
508             };
509 
510     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
511             new WakefulnessLifecycle.Observer() {
512                 private void notifyScreenStateChanged(boolean isScreenOn) {
513                     notifyNavigationBarScreenOn();
514                     mView.onScreenStateChanged(isScreenOn);
515                 }
516 
517                 @Override
518                 public void onStartedWakingUp() {
519                     notifyScreenStateChanged(true);
520                     if (isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker,
521                             mNavBarMode)) {
522                         mRegionSamplingHelper.start(mSamplingBounds);
523                     }
524                 }
525 
526                 @Override
527                 public void onFinishedGoingToSleep() {
528                     notifyScreenStateChanged(false);
529                     mRegionSamplingHelper.stop();
530                 }
531             };
532 
533     private boolean mScreenPinningActive = false;
534     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
535         @Override
536         public void onLockTaskModeChanged(int mode) {
537             mScreenPinningActive = (mode == LOCK_TASK_MODE_PINNED);
538             mSysUiFlagsContainer.setFlag(SYSUI_STATE_SCREEN_PINNING, mScreenPinningActive)
539                     .commitUpdate(mDisplayId);
540             mView.setInScreenPinning(mScreenPinningActive);
541             updateScreenPinningGestures();
542         }
543     };
544 
545     @Inject
NavigationBar( NavigationBarView navigationBarView, NavigationBarFrame navigationBarFrame, @Nullable Bundle savedState, @DisplayId Context context, @DisplayId WindowManager windowManager, Lazy<AssistManager> assistManagerLazy, AccessibilityManager accessibilityManager, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, SysUiState sysUiFlagsContainer, UserTracker userTracker, CommandQueue commandQueue, Optional<Pip> pipOptional, Optional<Recents> recentsOptional, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, KeyguardStateController keyguardStateController, ShadeViewController shadeViewController, PanelExpansionInteractor panelExpansionInteractor, NotificationRemoteInputManager notificationRemoteInputManager, NotificationShadeDepthController notificationShadeDepthController, @Main Handler mainHandler, @Main Executor mainExecutor, @Background Executor bgExecutor, UiEventLogger uiEventLogger, NavBarHelper navBarHelper, LightBarController mainLightBarController, LightBarController.Factory lightBarControllerFactory, AutoHideController mainAutoHideController, AutoHideController.Factory autoHideControllerFactory, Optional<TelecomManager> telecomManagerOptional, InputMethodManager inputMethodManager, DeadZone deadZone, DeviceConfigProxy deviceConfigProxy, NavigationBarTransitions navigationBarTransitions, Optional<BackAnimation> backAnimation, UserContextProvider userContextProvider, WakefulnessLifecycle wakefulnessLifecycle, TaskStackChangeListeners taskStackChangeListeners, DisplayTracker displayTracker, NavBarButtonClickLogger navBarButtonClickLogger, NavbarOrientationTrackingLogger navbarOrientationTrackingLogger)546     NavigationBar(
547             NavigationBarView navigationBarView,
548             NavigationBarFrame navigationBarFrame,
549             @Nullable Bundle savedState,
550             @DisplayId Context context,
551             @DisplayId WindowManager windowManager,
552             Lazy<AssistManager> assistManagerLazy,
553             AccessibilityManager accessibilityManager,
554             DeviceProvisionedController deviceProvisionedController,
555             MetricsLogger metricsLogger,
556             OverviewProxyService overviewProxyService,
557             NavigationModeController navigationModeController,
558             StatusBarStateController statusBarStateController,
559             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
560             SysUiState sysUiFlagsContainer,
561             UserTracker userTracker,
562             CommandQueue commandQueue,
563             Optional<Pip> pipOptional,
564             Optional<Recents> recentsOptional,
565             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
566             KeyguardStateController keyguardStateController,
567             ShadeViewController shadeViewController,
568             PanelExpansionInteractor panelExpansionInteractor,
569             NotificationRemoteInputManager notificationRemoteInputManager,
570             NotificationShadeDepthController notificationShadeDepthController,
571             @Main Handler mainHandler,
572             @Main Executor mainExecutor,
573             @Background Executor bgExecutor,
574             UiEventLogger uiEventLogger,
575             NavBarHelper navBarHelper,
576             LightBarController mainLightBarController,
577             LightBarController.Factory lightBarControllerFactory,
578             AutoHideController mainAutoHideController,
579             AutoHideController.Factory autoHideControllerFactory,
580             Optional<TelecomManager> telecomManagerOptional,
581             InputMethodManager inputMethodManager,
582             DeadZone deadZone,
583             DeviceConfigProxy deviceConfigProxy,
584             NavigationBarTransitions navigationBarTransitions,
585             Optional<BackAnimation> backAnimation,
586             UserContextProvider userContextProvider,
587             WakefulnessLifecycle wakefulnessLifecycle,
588             TaskStackChangeListeners taskStackChangeListeners,
589             DisplayTracker displayTracker,
590             NavBarButtonClickLogger navBarButtonClickLogger,
591             NavbarOrientationTrackingLogger navbarOrientationTrackingLogger) {
592         super(navigationBarView);
593         mFrame = navigationBarFrame;
594         mContext = context;
595         mSavedState = savedState;
596         mWindowManager = windowManager;
597         mAccessibilityManager = accessibilityManager;
598         mDeviceProvisionedController = deviceProvisionedController;
599         mStatusBarStateController = statusBarStateController;
600         mMetricsLogger = metricsLogger;
601         mAssistManagerLazy = assistManagerLazy;
602         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
603         mSysUiFlagsContainer = sysUiFlagsContainer;
604         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
605         mKeyguardStateController = keyguardStateController;
606         mShadeViewController = shadeViewController;
607         mPanelExpansionInteractor = panelExpansionInteractor;
608         mNotificationRemoteInputManager = notificationRemoteInputManager;
609         mOverviewProxyService = overviewProxyService;
610         mNavigationModeController = navigationModeController;
611         mUserTracker = userTracker;
612         mCommandQueue = commandQueue;
613         mPipOptional = pipOptional;
614         mRecentsOptional = recentsOptional;
615         mDeadZone = deadZone;
616         mDeviceConfigProxy = deviceConfigProxy;
617         mNavigationBarTransitions = navigationBarTransitions;
618         mBackAnimation = backAnimation;
619         mHandler = mainHandler;
620         mUiEventLogger = uiEventLogger;
621         mNavBarHelper = navBarHelper;
622         mNotificationShadeDepthController = notificationShadeDepthController;
623         mMainLightBarController = mainLightBarController;
624         mLightBarControllerFactory = lightBarControllerFactory;
625         mMainAutoHideController = mainAutoHideController;
626         mAutoHideControllerFactory = autoHideControllerFactory;
627         mTelecomManagerOptional = telecomManagerOptional;
628         mInputMethodManager = inputMethodManager;
629         mUserContextProvider = userContextProvider;
630         mWakefulnessLifecycle = wakefulnessLifecycle;
631         mTaskStackChangeListeners = taskStackChangeListeners;
632         mDisplayTracker = displayTracker;
633         mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
634         mNavBarButtonClickLogger = navBarButtonClickLogger;
635         mNavbarOrientationTrackingLogger = navbarOrientationTrackingLogger;
636 
637         mNavColorSampleMargin = getResources()
638                 .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
639 
640         mOnComputeInternalInsetsListener = info -> {
641             // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
642             // gestural mode, the entire nav bar should be touchable.
643             if (!mEdgeBackGestureHandler.isHandlingGestures()) {
644                 // We're in 2/3 button mode OR back button force-shown in SUW
645                 if (!mImeVisible) {
646                     // IME not showing, take all touches
647                     info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
648                     return;
649                 }
650                 if (!mView.isImeRenderingNavButtons()) {
651                     // IME showing but not drawing any buttons, take all touches
652                     info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
653                     return;
654                 }
655             }
656 
657             // When in gestural and the IME is showing, don't use the nearest region since it will
658             // take gesture space away from the IME
659             info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
660             info.touchableRegion.set(
661                     getButtonLocations(false /* inScreen */, false /* useNearestRegion */));
662         };
663 
664         mRegionSamplingHelper = new RegionSamplingHelper(mView,
665                 new RegionSamplingHelper.SamplingCallback() {
666                     @Override
667                     public void onRegionDarknessChanged(boolean isRegionDark) {
668                         getBarTransitions().getLightTransitionsController().setIconsDark(
669                                 !isRegionDark, true /* animate */);
670                     }
671 
672                     @Override
673                     public Rect getSampledRegion(View sampledView) {
674                         if (mOrientedHandleSamplingRegion != null) {
675                             return mOrientedHandleSamplingRegion;
676                         }
677 
678                         return calculateSamplingRect();
679                     }
680 
681                     @Override
682                     public boolean isSamplingEnabled() {
683                         return isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker,
684                                 mNavBarMode);
685                     }
686                 }, mainExecutor, bgExecutor);
687 
688         mView.setBackgroundExecutor(bgExecutor);
689         mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler);
690         mView.setDisplayTracker(mDisplayTracker);
691         mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
692     }
693 
getView()694     public NavigationBarView getView() {
695         return mView;
696     }
697 
698     @Override
onInit()699     public void onInit() {
700         // TODO: A great deal of this code should probably live in onViewAttached.
701         // It should also has corresponding cleanup in onViewDetached.
702         mView.setBarTransitions(mNavigationBarTransitions);
703         mView.setTouchHandler(mTouchHandler);
704         setNavBarMode(mNavBarMode);
705         mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates);
706         mEdgeBackGestureHandler.setButtonForcedVisibleChangeCallback((forceVisible) -> {
707             repositionNavigationBar(mCurrentRotation);
708         });
709         mNavigationBarTransitions.addListener(this::onBarTransition);
710         mView.updateRotationButton();
711 
712         mView.setVisibility(
713                 mStatusBarKeyguardViewManager.isNavBarVisible() ? View.VISIBLE : View.INVISIBLE);
714 
715         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
716 
717         mWindowManager.addView(mFrame,
718                 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
719                         .getRotation()));
720         mDisplayId = mContext.getDisplayId();
721         mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId();
722 
723         // Ensure we try to get currentSysuiState from navBarHelper before command queue callbacks
724         // start firing, since the latter is source of truth
725         parseCurrentSysuiState();
726         mCommandQueue.addCallback(this);
727         mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong(
728                 DeviceConfig.NAMESPACE_SYSTEMUI,
729                 HOME_BUTTON_LONG_PRESS_DURATION_MS,
730                 /* defaultValue = */ 0
731         )).filter(duration -> duration != 0);
732         // This currently MUST be called after mHomeButtonLongPressDurationMs is initialized since
733         // the registration callbacks will trigger code that uses it
734         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
735         mDeviceConfigProxy.addOnPropertiesChangedListener(
736                 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
737 
738         if (mSavedState != null) {
739             mDisabledFlags1 = mSavedState.getInt(EXTRA_DISABLE_STATE, 0);
740             mDisabledFlags2 = mSavedState.getInt(EXTRA_DISABLE2_STATE, 0);
741             mAppearance = mSavedState.getInt(EXTRA_APPEARANCE, 0);
742             mBehavior = mSavedState.getInt(EXTRA_BEHAVIOR, 0);
743             mTransientShown = mSavedState.getBoolean(EXTRA_TRANSIENT_STATE, false);
744         }
745 
746         // Respect the latest disabled-flags.
747         mCommandQueue.recomputeDisableFlags(mDisplayId, false);
748 
749         mNotificationShadeDepthController.addListener(mDepthListener);
750         mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
751     }
752 
destroyView()753     public void destroyView() {
754         Trace.beginSection("NavigationBar#destroyView");
755         try {
756             setAutoHideController(/* autoHideController */ null);
757             mCommandQueue.removeCallback(this);
758             Trace.beginSection("NavigationBar#removeViewImmediate");
759             try {
760                 mWindowManager.removeViewImmediate(mView.getRootView());
761             } finally {
762                 Trace.endSection();
763             }
764             mNavigationModeController.removeListener(mModeChangedListener);
765             mEdgeBackGestureHandler.setStateChangeCallback(null);
766 
767             mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
768             mNotificationShadeDepthController.removeListener(mDepthListener);
769 
770             mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
771             mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
772         } finally {
773             Trace.endSection();
774         }
775     }
776 
777     @Override
onViewAttached()778     public void onViewAttached() {
779         final Display display = mView.getDisplay();
780         mView.setComponents(mRecentsOptional);
781         if (mCentralSurfacesOptionalLazy.get().isPresent()) {
782             mView.setComponents(mShadeViewController, mPanelExpansionInteractor);
783         }
784         mView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer);
785         mView.setOnVerticalChangedListener(this::onVerticalChanged);
786         mView.setOnTouchListener(this::onNavigationTouch);
787         if (mSavedState != null) {
788             getBarTransitions().getLightTransitionsController().restoreState(mSavedState);
789         }
790         setNavigationIconHints(mNavigationIconHints);
791         setWindowVisible(isNavBarWindowVisible());
792         mView.setBehavior(mBehavior);
793         setNavBarMode(mNavBarMode);
794         repositionNavigationBar(mCurrentRotation);
795         mView.setUpdateActiveTouchRegionsCallback(
796                 () -> mOverviewProxyService.onActiveNavBarRegionChanges(
797                         getButtonLocations(true /* inScreen */, true /* useNearestRegion */)));
798 
799         mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
800                 mOnComputeInternalInsetsListener);
801 
802         mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener);
803         mBackAnimation.ifPresent(mView::registerBackAnimation);
804 
805         prepareNavigationBarView();
806         checkNavBarModes();
807 
808         mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
809         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
810         notifyNavigationBarScreenOn();
811 
812         mOverviewProxyService.addCallback(mOverviewProxyListener);
813         updateSystemUiStateFlags();
814 
815         // Currently there is no accelerometer sensor on non-default display.
816         if (mIsOnDefaultDisplay) {
817             final RotationButtonController rotationButtonController =
818                     mView.getRotationButtonController();
819 
820             // Reset user rotation pref to match that of the WindowManager if starting in locked
821             // mode. This will automatically happen when switching from auto-rotate to locked mode.
822             if (display != null && rotationButtonController.isRotationLocked()) {
823                 rotationButtonController.setRotationLockedAtAngle(
824                         display.getRotation(), /* caller= */ "NavigationBar#onViewAttached");
825             }
826         } else {
827             mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
828         }
829         setDisabled2Flags(mDisabledFlags2);
830 
831         initSecondaryHomeHandleForRotation();
832 
833         // Unfortunately, we still need it because status bar needs LightBarController
834         // before notifications creation. We cannot directly use getLightBarController()
835         // from NavigationBarFragment directly.
836         LightBarController lightBarController = mIsOnDefaultDisplay
837                 ? mMainLightBarController : mLightBarControllerFactory.create(mContext);
838         setLightBarController(lightBarController);
839 
840         // TODO(b/118592525): to support multi-display, we start to add something which is
841         //                    per-display, while others may be global. I think it's time to
842         //                    add a new class maybe named DisplayDependency to solve
843         //                    per-display Dependency problem.
844         // Alternative: this is a good case for a Dagger subcomponent. Same with LightBarController.
845         AutoHideController autoHideController = mIsOnDefaultDisplay
846                 ? mMainAutoHideController : mAutoHideControllerFactory.create(mContext);
847         setAutoHideController(autoHideController);
848         restoreAppearanceAndTransientState();
849     }
850 
851     @Override
onViewDetached()852     public void onViewDetached() {
853         mView.setUpdateActiveTouchRegionsCallback(null);
854         getBarTransitions().destroy();
855         mOverviewProxyService.removeCallback(mOverviewProxyListener);
856         mUserTracker.removeCallback(mUserChangedCallback);
857         mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
858         if (mOrientationHandle != null) {
859             resetSecondaryHandle();
860             getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
861             mWindowManager.removeView(mOrientationHandle);
862             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
863                     mOrientationHandleGlobalLayoutListener);
864         }
865         mView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
866                 mOnComputeInternalInsetsListener);
867         mHandler.removeCallbacks(mAutoDim);
868         mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
869         mHandler.removeCallbacks(mEnableLayoutTransitions);
870         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
871         mPipOptional.ifPresent(mView::removePipExclusionBoundsChangeListener);
872         mFrame = null;
873         mOrientationHandle = null;
874     }
875 
876     // TODO: Remove this when we update nav bar recreation
onSaveInstanceState(Bundle outState)877     public void onSaveInstanceState(Bundle outState) {
878         outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
879         outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2);
880         outState.putInt(EXTRA_APPEARANCE, mAppearance);
881         outState.putInt(EXTRA_BEHAVIOR, mBehavior);
882         outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
883         getBarTransitions().getLightTransitionsController().saveState(outState);
884     }
885 
886     /**
887      * Called when a non-reloading configuration change happens and we need to update.
888      */
onConfigurationChanged(Configuration newConfig)889     public void onConfigurationChanged(Configuration newConfig) {
890         final int rotation = newConfig.windowConfiguration.getRotation();
891         final Locale locale = mContext.getResources().getConfiguration().locale;
892         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
893         if (!locale.equals(mLocale) || ld != mLayoutDirection) {
894             if (DEBUG) {
895                 Log.v(TAG, String.format(
896                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
897                         locale, ld));
898             }
899             mLocale = locale;
900             mLayoutDirection = ld;
901             refreshLayout(ld);
902         }
903         repositionNavigationBar(rotation);
904         if (canShowSecondaryHandle()) {
905             if (rotation != mCurrentRotation) {
906                 mCurrentRotation = rotation;
907                 orientSecondaryHomeHandle();
908             }
909         }
910     }
911 
initSecondaryHomeHandleForRotation()912     private void initSecondaryHomeHandleForRotation() {
913         if (mNavBarMode != NAV_BAR_MODE_GESTURAL) {
914             return;
915         }
916 
917         mOrientationHandle = new QuickswitchOrientedNavHandle(mContext);
918         mOrientationHandle.setId(R.id.secondary_home_handle);
919 
920         getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
921         mOrientationParams = new WindowManager.LayoutParams(0, 0,
922                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
923                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
924                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
925                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
926                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
927                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
928                 PixelFormat.TRANSLUCENT);
929         mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
930         mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
931                 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
932         mWindowManager.addView(mOrientationHandle, mOrientationParams);
933         mOrientationHandle.setVisibility(View.GONE);
934 
935         logNavbarOrientation("initSecondaryHomeHandleForRotation");
936         mOrientationParams.setFitInsetsTypes(0 /* types*/);
937         mOrientationHandleGlobalLayoutListener =
938                 () -> {
939                     if (mStartingQuickSwitchRotation == -1) {
940                         return;
941                     }
942 
943                     RectF boundsOnScreen = mOrientationHandle.computeHomeHandleBounds();
944                     mOrientationHandle.mapRectFromViewToScreenCoords(boundsOnScreen, true);
945                     Rect boundsRounded = new Rect();
946                     boundsOnScreen.roundOut(boundsRounded);
947                     setOrientedHandleSamplingRegion(boundsRounded);
948                 };
949         mOrientationHandle.getViewTreeObserver().addOnGlobalLayoutListener(
950                 mOrientationHandleGlobalLayoutListener);
951     }
952 
orientSecondaryHomeHandle()953     private void orientSecondaryHomeHandle() {
954         if (!canShowSecondaryHandle()) {
955             return;
956         }
957 
958         if (mStartingQuickSwitchRotation == -1) {
959             resetSecondaryHandle();
960         } else {
961             int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
962             if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) {
963                 // Curious if starting quickswitch can change between the if check and our delta
964                 Log.d(TAG, "secondary nav delta rotation: " + deltaRotation
965                         + " current: " + mCurrentRotation
966                         + " starting: " + mStartingQuickSwitchRotation);
967             }
968             int height = 0;
969             int width = 0;
970             Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
971             mOrientationHandle.setDeltaRotation(deltaRotation);
972             switch (deltaRotation) {
973                 case Surface.ROTATION_90:
974                 case Surface.ROTATION_270:
975                     height = dispSize.height();
976                     width = mView.getHeight();
977                     break;
978                 case Surface.ROTATION_180:
979                 case Surface.ROTATION_0:
980                     // TODO(b/152683657): Need to determine best UX for this
981                     if (!mShowOrientedHandleForImmersiveMode) {
982                         resetSecondaryHandle();
983                         return;
984                     }
985                     width = dispSize.width();
986                     height = mView.getHeight();
987                     break;
988             }
989 
990             mOrientationParams.gravity =
991                     deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM :
992                             (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT);
993             mOrientationParams.height = height;
994             mOrientationParams.width = width;
995             mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
996             mView.setVisibility(View.GONE);
997             mOrientationHandle.setVisibility(View.VISIBLE);
998             logNavbarOrientation("orientSecondaryHomeHandle");
999         }
1000     }
1001 
resetSecondaryHandle()1002     private void resetSecondaryHandle() {
1003         if (mOrientationHandle != null) {
1004             // Case where nav mode is changed w/o ever invoking a quickstep
1005             // mOrientedHandle is initialized lazily
1006             mOrientationHandle.setVisibility(View.GONE);
1007         }
1008         mView.setVisibility(View.VISIBLE);
1009         logNavbarOrientation("resetSecondaryHandle");
1010         setOrientedHandleSamplingRegion(null);
1011     }
1012 
1013     /**
1014      * Logging method for issues concerning Navbar/secondary handle visibility.
1015      */
logNavbarOrientation(String methodName)1016     private void logNavbarOrientation(String methodName) {
1017         boolean isViewVisible = (mView != null) && (mView.getVisibility() == View.VISIBLE);
1018         boolean isSecondaryHandleVisible =
1019                 (mOrientationHandle != null) && (mOrientationHandle.getVisibility()
1020                         == View.VISIBLE);
1021         mNavbarOrientationTrackingLogger.logPrimaryAndSecondaryVisibility(methodName, isViewVisible,
1022                 mShowOrientedHandleForImmersiveMode, isSecondaryHandleVisible, mCurrentRotation,
1023                 mStartingQuickSwitchRotation);
1024     }
1025 
parseCurrentSysuiState()1026     private void parseCurrentSysuiState() {
1027         NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
1028         if (state.mWindowStateDisplayId == mDisplayId) {
1029             mNavigationBarWindowState = state.mWindowState;
1030         }
1031     }
1032 
reconfigureHomeLongClick()1033     private void reconfigureHomeLongClick() {
1034         if (mView.getHomeButton().getCurrentView() == null) {
1035             return;
1036         }
1037         if (mHomeButtonLongPressDurationMs.isPresent()
1038                 || mOverrideHomeButtonLongPressDurationMs.isPresent()
1039                 || mOverrideHomeButtonLongPressSlopMultiplier.isPresent()
1040                 || !mLongPressHomeEnabled) {
1041             mView.getHomeButton().getCurrentView().setLongClickable(false);
1042             mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
1043             mView.getHomeButton().setOnLongClickListener(null);
1044         } else {
1045             mView.getHomeButton().getCurrentView().setLongClickable(true);
1046             mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(
1047                     mHomeButtonLongPressHapticEnabled);
1048             mView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
1049         }
1050     }
1051 
deltaRotation(int oldRotation, int newRotation)1052     private int deltaRotation(int oldRotation, int newRotation) {
1053         int delta = newRotation - oldRotation;
1054         if (delta < 0) delta += 4;
1055         return delta;
1056     }
1057 
dump(PrintWriter pw)1058     public void dump(PrintWriter pw) {
1059         pw.println("NavigationBar (displayId=" + mDisplayId + "):");
1060         pw.println("  mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
1061         pw.println("  mCurrentRotation=" + mCurrentRotation);
1062         pw.println("  mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
1063         pw.println("  mOverrideHomeButtonLongPressDurationMs="
1064                 + mOverrideHomeButtonLongPressDurationMs);
1065         pw.println("  mOverrideHomeButtonLongPressSlopMultiplier="
1066                 + mOverrideHomeButtonLongPressSlopMultiplier);
1067         pw.println("  mLongPressHomeEnabled=" + mLongPressHomeEnabled);
1068         pw.println("  mNavigationBarWindowState="
1069                 + windowStateToString(mNavigationBarWindowState));
1070         pw.println("  mTransitionMode="
1071                 + BarTransitions.modeToString(mTransitionMode));
1072         pw.println("  mTransientShown=" + mTransientShown);
1073         pw.println("  mTransientShownFromGestureOnSystemBar="
1074                 + mTransientShownFromGestureOnSystemBar);
1075         pw.println("  mScreenPinningActive=" + mScreenPinningActive);
1076         dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions());
1077 
1078         pw.println("  mOrientedHandleSamplingRegion: " + mOrientedHandleSamplingRegion);
1079         mView.dump(pw);
1080         mRegionSamplingHelper.dump(pw);
1081         if (mAutoHideController != null) {
1082             mAutoHideController.dump(pw);
1083         }
1084     }
1085 
1086     // ----- CommandQueue Callbacks -----
1087 
1088     @Override
setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)1089     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
1090             boolean showImeSwitcher) {
1091         if (displayId != mDisplayId) {
1092             return;
1093         }
1094         boolean imeShown = mNavBarHelper.isImeShown(vis);
1095         showImeSwitcher = imeShown && showImeSwitcher;
1096         int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
1097                 imeShown, showImeSwitcher);
1098         if (hints == mNavigationIconHints) return;
1099 
1100         setNavigationIconHints(hints);
1101         checkBarModes();
1102         updateSystemUiStateFlags();
1103     }
1104 
1105     @Override
setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state)1106     public void setWindowState(
1107             int displayId, @WindowType int window, @WindowVisibleState int state) {
1108         if (displayId == mDisplayId
1109                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
1110                 && mNavigationBarWindowState != state) {
1111             mNavigationBarWindowState = state;
1112             updateSystemUiStateFlags();
1113             mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN;
1114             if (mOrientationHandle != null
1115                     && mStartingQuickSwitchRotation != -1) {
1116                 orientSecondaryHomeHandle();
1117             }
1118             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
1119             setWindowVisible(isNavBarWindowVisible());
1120         }
1121     }
1122 
1123     @Override
onRotationProposal(final int rotation, boolean isValid)1124     public void onRotationProposal(final int rotation, boolean isValid) {
1125         // The CommandQueue callbacks are added when the view is created to ensure we track other
1126         // states, but until the view is attached (at the next traversal), the view's display is
1127         // not valid.  Just ignore the rotation in this case.
1128         if (!mView.isAttachedToWindow()) return;
1129 
1130         final boolean rotateSuggestionsDisabled = RotationButtonController
1131                 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
1132         final RotationButtonController rotationButtonController =
1133                 mView.getRotationButtonController();
1134         if (DEBUG_ROTATION) {
1135             Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
1136                     + ", isValid=" + isValid + ", mNavBarWindowState="
1137                     + StatusBarManager.windowStateToString(mNavigationBarWindowState)
1138                     + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
1139                     + ", isRotateButtonVisible="
1140                     + rotationButtonController.getRotationButton().isVisible());
1141         }
1142         // Respect the disabled flag, no need for action as flag change callback will handle hiding
1143         if (rotateSuggestionsDisabled) return;
1144 
1145         rotationButtonController.onRotationProposal(rotation, isValid);
1146     }
1147 
1148     @Override
onRecentsAnimationStateChanged(boolean running)1149     public void onRecentsAnimationStateChanged(boolean running) {
1150         mView.getRotationButtonController().setRecentsAnimationRunning(running);
1151     }
1152 
1153     /** Restores the appearance and the transient saved state to {@link NavigationBar}. */
restoreAppearanceAndTransientState()1154     public void restoreAppearanceAndTransientState() {
1155         final int transitionMode = transitionMode(mTransientShown, mAppearance);
1156         mTransitionMode = transitionMode;
1157         checkNavBarModes();
1158         if (mAutoHideController != null) {
1159             mAutoHideController.touchAutoHide();
1160         }
1161         if (mLightBarController != null) {
1162             mLightBarController.onNavigationBarAppearanceChanged(mAppearance,
1163                     true /* nbModeChanged */, transitionMode, false /* navbarColorManagedByIme */);
1164         }
1165     }
1166 
1167     @Override
onSystemBarAttributesChanged(int displayId, @Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName, LetterboxDetails[] letterboxDetails)1168     public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
1169             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
1170             @Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName,
1171             LetterboxDetails[] letterboxDetails) {
1172         if (displayId != mDisplayId) {
1173             return;
1174         }
1175         boolean nbModeChanged = false;
1176         if (mAppearance != appearance) {
1177             mAppearance = appearance;
1178             nbModeChanged = updateTransitionMode(transitionMode(mTransientShown, appearance));
1179         }
1180         if (mLightBarController != null) {
1181             mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
1182                     mTransitionMode, navbarColorManagedByIme);
1183         }
1184         if (mBehavior != behavior) {
1185             mBehavior = behavior;
1186             mView.setBehavior(behavior);
1187             updateSystemUiStateFlags();
1188         }
1189     }
1190 
1191     @Override
showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar)1192     public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) {
1193         if (displayId != mDisplayId) {
1194             return;
1195         }
1196         if ((types & WindowInsets.Type.navigationBars()) == 0) {
1197             return;
1198         }
1199         if (!mTransientShown) {
1200             mTransientShown = true;
1201             mTransientShownFromGestureOnSystemBar = isGestureOnSystemBar;
1202             handleTransientChanged();
1203         }
1204     }
1205 
1206     @Override
abortTransient(int displayId, @InsetsType int types)1207     public void abortTransient(int displayId, @InsetsType int types) {
1208         if (displayId != mDisplayId) {
1209             return;
1210         }
1211         if ((types & WindowInsets.Type.navigationBars()) == 0) {
1212             return;
1213         }
1214         clearTransient();
1215     }
1216 
clearTransient()1217     private void clearTransient() {
1218         if (mTransientShown) {
1219             mTransientShown = false;
1220             mTransientShownFromGestureOnSystemBar = false;
1221             handleTransientChanged();
1222         }
1223     }
1224 
handleTransientChanged()1225     private void handleTransientChanged() {
1226         mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTransientShown);
1227 
1228         final int transitionMode = transitionMode(mTransientShown, mAppearance);
1229         if (updateTransitionMode(transitionMode) && mLightBarController != null) {
1230             mLightBarController.onNavigationBarModeChanged(transitionMode);
1231         }
1232     }
1233 
1234     // Returns true if the bar mode is changed.
updateTransitionMode(int barMode)1235     private boolean updateTransitionMode(int barMode) {
1236         if (mTransitionMode != barMode) {
1237             mTransitionMode = barMode;
1238             checkNavBarModes();
1239             if (mAutoHideController != null) {
1240                 mAutoHideController.touchAutoHide();
1241             }
1242             return true;
1243         }
1244         return false;
1245     }
1246 
1247     @Override
disable(int displayId, int state1, int state2, boolean animate)1248     public void disable(int displayId, int state1, int state2, boolean animate) {
1249         if (displayId != mDisplayId) {
1250             return;
1251         }
1252         // Navigation bar flags are in both state1 and state2.
1253         final int masked = state1 & (StatusBarManager.DISABLE_HOME
1254                 | StatusBarManager.DISABLE_RECENT
1255                 | StatusBarManager.DISABLE_BACK
1256                 | StatusBarManager.DISABLE_SEARCH);
1257         if (masked != mDisabledFlags1) {
1258             mDisabledFlags1 = masked;
1259             mView.setDisabledFlags(state1, mSysUiFlagsContainer);
1260             updateScreenPinningGestures();
1261         }
1262 
1263         // Only default display supports rotation suggestions.
1264         if (mIsOnDefaultDisplay) {
1265             final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
1266             if (masked2 != mDisabledFlags2) {
1267                 mDisabledFlags2 = masked2;
1268                 setDisabled2Flags(masked2);
1269             }
1270         }
1271     }
1272 
setDisabled2Flags(int state2)1273     private void setDisabled2Flags(int state2) {
1274         // Method only called on change of disable2 flags
1275         mView.getRotationButtonController().onDisable2FlagChanged(state2);
1276     }
1277 
1278     // ----- Internal stuff -----
1279 
refreshLayout(int layoutDirection)1280     private void refreshLayout(int layoutDirection) {
1281         mView.setLayoutDirection(layoutDirection);
1282     }
1283 
shouldDisableNavbarGestures()1284     private boolean shouldDisableNavbarGestures() {
1285         return !mDeviceProvisionedController.isDeviceProvisioned()
1286                 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
1287     }
1288 
repositionNavigationBar(int rotation)1289     private void repositionNavigationBar(int rotation) {
1290         if (mView == null || !mView.isAttachedToWindow()) return;
1291 
1292         prepareNavigationBarView();
1293 
1294         mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation));
1295     }
1296 
updateScreenPinningGestures()1297     private void updateScreenPinningGestures() {
1298         // Change the cancel pin gesture to home and back if recents button is invisible
1299         ButtonDispatcher backButton = mView.getBackButton();
1300         ButtonDispatcher recentsButton = mView.getRecentsButton();
1301         if (mScreenPinningActive) {
1302             boolean recentsVisible = mView.isRecentsButtonVisible();
1303             backButton.setOnLongClickListener(recentsVisible
1304                     ? this::onLongPressBackRecents
1305                     : this::onLongPressBackHome);
1306             recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
1307         } else {
1308             backButton.setOnLongClickListener(null);
1309             recentsButton.setOnLongClickListener(null);
1310         }
1311         // Note, this needs to be set after even if we're setting the listener to null
1312         backButton.setLongClickable(mScreenPinningActive);
1313         recentsButton.setLongClickable(mScreenPinningActive);
1314     }
1315 
notifyNavigationBarScreenOn()1316     private void notifyNavigationBarScreenOn() {
1317         mView.updateNavButtonIcons();
1318     }
1319 
prepareNavigationBarView()1320     private void prepareNavigationBarView() {
1321         mView.reorient();
1322 
1323         ButtonDispatcher recentsButton = mView.getRecentsButton();
1324         recentsButton.setOnClickListener(this::onRecentsClick);
1325         recentsButton.setOnTouchListener(this::onRecentsTouch);
1326 
1327         ButtonDispatcher homeButton = mView.getHomeButton();
1328         homeButton.setOnTouchListener(this::onHomeTouch);
1329         homeButton.setNavBarButtonClickLogger(mNavBarButtonClickLogger);
1330 
1331         ButtonDispatcher backButton = mView.getBackButton();
1332         backButton.setNavBarButtonClickLogger(mNavBarButtonClickLogger);
1333 
1334         reconfigureHomeLongClick();
1335 
1336         ButtonDispatcher accessibilityButton = mView.getAccessibilityButton();
1337         accessibilityButton.setOnClickListener(this::onAccessibilityClick);
1338         accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
1339         updateAccessibilityStateFlags();
1340 
1341         ButtonDispatcher imeSwitcherButton = mView.getImeSwitchButton();
1342         imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
1343 
1344         updateScreenPinningGestures();
1345     }
1346 
1347     @VisibleForTesting
onHomeTouch(View v, MotionEvent event)1348     boolean onHomeTouch(View v, MotionEvent event) {
1349         if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
1350             return true;
1351         }
1352         // If an incoming call is ringing, HOME is totally disabled.
1353         // (The user is already on the InCallUI at this point,
1354         // and their ONLY options are to answer or reject the call.)
1355         final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get();
1356         switch (event.getAction()) {
1357             case MotionEvent.ACTION_DOWN:
1358                 if (mCurrentDownEvent != null) {
1359                     mCurrentDownEvent.recycle();
1360                 }
1361                 mCurrentDownEvent = MotionEvent.obtain(event);
1362                 mHomeBlockedThisTouch = false;
1363                 if (mTelecomManagerOptional.isPresent()
1364                         && mTelecomManagerOptional.get().isRinging()) {
1365                     if (mKeyguardStateController.isShowing()) {
1366                         Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
1367                                 "No heads up");
1368                         mHomeBlockedThisTouch = true;
1369                         return true;
1370                     }
1371                 }
1372                 if (mLongPressHomeEnabled) {
1373                     if (mOverrideHomeButtonLongPressDurationMs.isPresent()) {
1374                         Log.d(TAG, "ACTION_DOWN Launcher override duration: "
1375                                 + mOverrideHomeButtonLongPressDurationMs.get());
1376                         mHandler.postDelayed(mOnVariableDurationHomeLongClick,
1377                                 mOverrideHomeButtonLongPressDurationMs.get());
1378                     } else if (mOverrideHomeButtonLongPressSlopMultiplier.isPresent()) {
1379                         // If override timeout doesn't exist but override touch slop exists, we use
1380                         // system default long press duration
1381                         Log.d(TAG, "ACTION_DOWN default duration: "
1382                                 + ViewConfiguration.getLongPressTimeout());
1383                         mHandler.postDelayed(mOnVariableDurationHomeLongClick,
1384                                 ViewConfiguration.getLongPressTimeout());
1385                     } else {
1386                         mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
1387                             Log.d(TAG, "ACTION_DOWN original duration: " + longPressDuration);
1388                             mHandler.postDelayed(mOnVariableDurationHomeLongClick,
1389                                     longPressDuration);
1390                         });
1391                     }
1392                 }
1393                 break;
1394             case MotionEvent.ACTION_MOVE:
1395                 if (!mHandler.hasCallbacks(mOnVariableDurationHomeLongClick)) {
1396                     Log.v(TAG, "ACTION_MOVE no callback. Don't handle touch slop.");
1397                     break;
1398                 }
1399                 Log.v(TAG, "ACTION_MOVE handle touch slop");
1400                 float customSlopMultiplier = mOverrideHomeButtonLongPressSlopMultiplier.orElse(1f);
1401                 float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
1402                 float calculatedTouchSlop =
1403                         customSlopMultiplier * QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON * touchSlop;
1404                 float touchSlopSquared = calculatedTouchSlop * calculatedTouchSlop;
1405 
1406                 float dx = event.getX() - mCurrentDownEvent.getX();
1407                 float dy = event.getY() - mCurrentDownEvent.getY();
1408                 double distanceSquared = (dx * dx) + (dy * dy);
1409                 if (distanceSquared > touchSlopSquared) {
1410                     Log.i(TAG, "Touch slop passed. Abort.");
1411                     mView.abortCurrentGesture();
1412                     mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
1413                 }
1414                 break;
1415             case MotionEvent.ACTION_UP:
1416             case MotionEvent.ACTION_CANCEL:
1417                 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
1418                 centralSurfacesOptional.ifPresent(CentralSurfaces::awakenDreams);
1419                 break;
1420         }
1421         return false;
1422     }
1423 
onVerticalChanged(boolean isVertical)1424     private void onVerticalChanged(boolean isVertical) {
1425         // This check can probably be safely removed. It only remained to reduce regression
1426         // risk for a broad change that removed the CentralSurfaces reference in the if block
1427         if (mCentralSurfacesOptionalLazy.get().isPresent()) {
1428             mShadeViewController.setQsScrimEnabled(!isVertical);
1429         }
1430     }
1431 
onNavigationTouch(View v, MotionEvent event)1432     private boolean onNavigationTouch(View v, MotionEvent event) {
1433         if (mAutoHideController != null) {
1434             mAutoHideController.checkUserAutoHide(event);
1435         }
1436         return false;
1437     }
1438 
1439     @VisibleForTesting
onHomeLongClick(View v)1440     boolean onHomeLongClick(View v) {
1441         if (!mView.isRecentsButtonVisible() && mScreenPinningActive) {
1442             return onLongPressBackHome(v);
1443         }
1444         if (shouldDisableNavbarGestures()) {
1445             return false;
1446         }
1447         mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);
1448         mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS);
1449         Bundle args = new Bundle();
1450         args.putInt(
1451                 AssistManager.INVOCATION_TYPE_KEY,
1452                 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
1453         // If Launcher has requested to override long press home, add a delay for the ripple.
1454         // TODO(b/304146255): Remove this delay once we can exclude 3-button nav from screenshot.
1455         boolean delayAssistInvocation = mAssistManagerLazy.get().shouldOverrideAssist(
1456                 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
1457         // In practice, I think v should always be a KeyButtonView, but just being safe.
1458         if (delayAssistInvocation && v instanceof KeyButtonView) {
1459             ((KeyButtonView) v).setOnRippleInvisibleRunnable(
1460                     () -> mAssistManagerLazy.get().startAssist(args));
1461         } else {
1462             mAssistManagerLazy.get().startAssist(args);
1463         }
1464         mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
1465         mView.abortCurrentGesture();
1466         return true;
1467     }
1468 
1469     // additional optimization when we have software system buttons - start loading the recent
1470     // tasks on touch down
onRecentsTouch(View v, MotionEvent event)1471     private boolean onRecentsTouch(View v, MotionEvent event) {
1472         int action = event.getAction() & MotionEvent.ACTION_MASK;
1473         if (action == MotionEvent.ACTION_DOWN) {
1474             mCommandQueue.preloadRecentApps();
1475         } else if (action == MotionEvent.ACTION_CANCEL) {
1476             mCommandQueue.cancelPreloadRecentApps();
1477         } else if (action == MotionEvent.ACTION_UP) {
1478             if (!v.isPressed()) {
1479                 mCommandQueue.cancelPreloadRecentApps();
1480             }
1481         }
1482         return false;
1483     }
1484 
onRecentsClick(View v)1485     private void onRecentsClick(View v) {
1486         mNavBarButtonClickLogger.logRecentsButtonClick();
1487 
1488         if (LatencyTracker.isEnabled(mContext)) {
1489             LatencyTracker.getInstance(mContext).onActionStart(
1490                     LatencyTracker.ACTION_TOGGLE_RECENTS);
1491         }
1492         mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
1493         mCommandQueue.toggleRecentApps();
1494     }
1495 
onImeSwitcherClick(View v)1496     private void onImeSwitcherClick(View v) {
1497         mNavBarButtonClickLogger.logImeSwitcherClick();
1498         mInputMethodManager.showInputMethodPickerFromSystem(
1499                 true /* showAuxiliarySubtypes */, mDisplayId);
1500         mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
1501     };
1502 
onLongPressBackHome(View v)1503     private boolean onLongPressBackHome(View v) {
1504         return onLongPressNavigationButtons(v, R.id.back, R.id.home);
1505     }
1506 
onLongPressBackRecents(View v)1507     private boolean onLongPressBackRecents(View v) {
1508         return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
1509     }
1510 
1511     /**
1512      * This handles long-press of both back and recents/home. Back is the common button with
1513      * combination of recents if it is visible or home if recents is invisible.
1514      * They are handled together to capture them both being long-pressed
1515      * at the same time to exit screen pinning (lock task).
1516      *
1517      * When accessibility mode is on, only a long-press from recents/home
1518      * is required to exit.
1519      *
1520      * In all other circumstances we try to pass through long-press events
1521      * for Back, so that apps can still use it.  Which can be from two things.
1522      * 1) Not currently in screen pinning (lock task).
1523      * 2) Back is long-pressed without recents/home.
1524      */
onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2)1525     private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
1526         try {
1527             boolean sendBackLongPress = false;
1528             IActivityTaskManager activityManager = ActivityTaskManager.getService();
1529             boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
1530             boolean inLockTaskMode = activityManager.isInLockTaskMode();
1531             boolean stopLockTaskMode = false;
1532             try {
1533                 if (inLockTaskMode && !touchExplorationEnabled) {
1534                     long time = System.currentTimeMillis();
1535 
1536                     // If we recently long-pressed the other button then they were
1537                     // long-pressed 'together'
1538                     if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERANCE) {
1539                         stopLockTaskMode = true;
1540                         return true;
1541                     } else if (v.getId() == btnId1) {
1542                         ButtonDispatcher button = btnId2 == R.id.recent_apps
1543                                 ? mView.getRecentsButton() : mView.getHomeButton();
1544                         if (!button.getCurrentView().isPressed()) {
1545                             // If we aren't pressing recents/home right now then they presses
1546                             // won't be together, so send the standard long-press action.
1547                             sendBackLongPress = true;
1548                         }
1549                     }
1550                     mLastLockToAppLongPress = time;
1551                 } else {
1552                     // If this is back still need to handle sending the long-press event.
1553                     if (v.getId() == btnId1) {
1554                         sendBackLongPress = true;
1555                     } else if (touchExplorationEnabled && inLockTaskMode) {
1556                         // When in accessibility mode a long press that is recents/home (not back)
1557                         // should stop lock task.
1558                         stopLockTaskMode = true;
1559                         return true;
1560                     } else if (v.getId() == btnId2) {
1561                         return btnId2 == R.id.recent_apps
1562                                 ? false
1563                                 : onHomeLongClick(mView.getHomeButton().getCurrentView());
1564                     }
1565                 }
1566             } finally {
1567                 if (stopLockTaskMode) {
1568                     activityManager.stopSystemLockTaskMode();
1569                     // When exiting refresh disabled flags.
1570                     mView.updateNavButtonIcons();
1571                 }
1572             }
1573 
1574             if (sendBackLongPress) {
1575                 KeyButtonView keyButtonView = (KeyButtonView) v;
1576                 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
1577                 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
1578                 return true;
1579             }
1580         } catch (RemoteException e) {
1581             Log.d(TAG, "Unable to reach activity manager", e);
1582         }
1583         return false;
1584     }
1585 
onAccessibilityClick(View v)1586     private void onAccessibilityClick(View v) {
1587         mNavBarButtonClickLogger.logAccessibilityButtonClick();
1588         final Display display = v.getDisplay();
1589         mAccessibilityManager.notifyAccessibilityButtonClicked(
1590                 display != null ? display.getDisplayId() : mDisplayTracker.getDefaultDisplayId());
1591     }
1592 
onAccessibilityLongClick(View v)1593     private boolean onAccessibilityLongClick(View v) {
1594         final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
1595         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
1596         final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
1597         intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
1598         mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
1599         return true;
1600     }
1601 
updateAccessibilityStateFlags()1602     void updateAccessibilityStateFlags() {
1603         mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
1604         if (mView != null) {
1605             long a11yFlags = mNavBarHelper.getA11yButtonState();
1606             boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
1607             boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
1608             mView.setAccessibilityButtonState(clickable, longClickable);
1609         }
1610         updateSystemUiStateFlags();
1611     }
1612 
updateSystemUiStateFlags()1613     public void updateSystemUiStateFlags() {
1614         long a11yFlags = mNavBarHelper.getA11yButtonState();
1615         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
1616         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
1617 
1618         mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
1619                 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
1620                 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
1621                 .setFlag(SYSUI_STATE_IME_SHOWING,
1622                         (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
1623                 .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
1624                         (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
1625                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
1626                         allowSystemGestureIgnoringBarVisibility())
1627                 .commitUpdate(mDisplayId);
1628     }
1629 
updateAssistantEntrypoints(boolean assistantAvailable, boolean longPressHomeEnabled)1630     private void updateAssistantEntrypoints(boolean assistantAvailable,
1631             boolean longPressHomeEnabled) {
1632         if (mOverviewProxyService.getProxy() != null) {
1633             try {
1634                 mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable,
1635                         longPressHomeEnabled);
1636             } catch (RemoteException e) {
1637                 Log.w(TAG, "Unable to send assistant availability data to launcher");
1638             }
1639         }
1640         reconfigureHomeLongClick();
1641     }
1642 
1643     // ----- Methods that DisplayNavigationBarController talks to -----
1644 
1645     /** Applies auto dimming animation on navigation bar when touched. */
touchAutoDim()1646     public void touchAutoDim() {
1647         getBarTransitions().setAutoDim(false);
1648         mHandler.removeCallbacks(mAutoDim);
1649         int state = mStatusBarStateController.getState();
1650         if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) {
1651             mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS);
1652         }
1653     }
1654 
setLightBarController(LightBarController lightBarController)1655     public void setLightBarController(LightBarController lightBarController) {
1656         mLightBarController = lightBarController;
1657         if (mLightBarController != null) {
1658             mLightBarController.setNavigationBar(
1659                     getBarTransitions().getLightTransitionsController());
1660         }
1661     }
1662 
setWindowVisible(boolean visible)1663     private void setWindowVisible(boolean visible) {
1664         mRegionSamplingHelper.setWindowVisible(visible);
1665         mView.setWindowVisible(visible);
1666     }
1667 
1668     /** Sets {@link AutoHideController} to the navigation bar. */
setAutoHideController(AutoHideController autoHideController)1669     private void setAutoHideController(AutoHideController autoHideController) {
1670         mAutoHideController = autoHideController;
1671         if (mAutoHideController != null) {
1672             mAutoHideController.setNavigationBar(mAutoHideUiElement);
1673         }
1674         mView.setAutoHideController(autoHideController);
1675     }
1676 
isTransientShown()1677     private boolean isTransientShown() {
1678         return mTransientShown;
1679     }
1680 
checkBarModes()1681     private void checkBarModes() {
1682         // We only have status bar on default display now.
1683         if (mIsOnDefaultDisplay) {
1684             mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::checkBarModes);
1685         } else {
1686             checkNavBarModes();
1687         }
1688     }
1689 
isNavBarWindowVisible()1690     public boolean isNavBarWindowVisible() {
1691         return mNavigationBarWindowState == WINDOW_STATE_SHOWING;
1692     }
1693 
allowSystemGestureIgnoringBarVisibility()1694     private boolean allowSystemGestureIgnoringBarVisibility() {
1695         return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
1696     }
1697 
1698     /**
1699      * Checks current navigation bar mode and make transitions.
1700      */
checkNavBarModes()1701     public void checkNavBarModes() {
1702         final boolean anim =
1703                 mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive)
1704                         .orElse(false)
1705                 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
1706         getBarTransitions().transitionTo(mTransitionMode, anim);
1707     }
1708 
disableAnimationsDuringHide(long delay)1709     public void disableAnimationsDuringHide(long delay) {
1710         mView.setLayoutTransitionsEnabled(false);
1711         mHandler.postDelayed(mEnableLayoutTransitions,
1712                 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
1713     }
1714 
1715     /**
1716      * Performs transitions on navigation bar.
1717      *
1718      * @param barMode transition bar mode.
1719      * @param animate shows animations if {@code true}.
1720      */
transitionTo(@ransitionMode int barMode, boolean animate)1721     public void transitionTo(@TransitionMode int barMode, boolean animate) {
1722         getBarTransitions().transitionTo(barMode, animate);
1723     }
1724 
getBarTransitions()1725     public NavigationBarTransitions getBarTransitions() {
1726         return mNavigationBarTransitions;
1727     }
1728 
finishBarAnimations()1729     public void finishBarAnimations() {
1730         getBarTransitions().finishAnimations();
1731     }
1732 
getBarLayoutParams(int rotation)1733     private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
1734         WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
1735         lp.paramsForRotation = new WindowManager.LayoutParams[4];
1736         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
1737             lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
1738         }
1739         return lp;
1740     }
1741 
getBarLayoutParamsForRotation(int rotation)1742     private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
1743         int width = WindowManager.LayoutParams.MATCH_PARENT;
1744         int height = WindowManager.LayoutParams.MATCH_PARENT;
1745         int insetsHeight = -1;
1746         int gravity = Gravity.BOTTOM;
1747         boolean navBarCanMove = true;
1748         final Context userContext = mUserContextProvider.createCurrentUserContext(mContext);
1749         if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) {
1750             Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
1751             navBarCanMove = displaySize.width() != displaySize.height()
1752                     && userContext.getResources().getBoolean(
1753                     com.android.internal.R.bool.config_navBarCanMove);
1754         }
1755         if (!navBarCanMove) {
1756             height = userContext.getResources().getDimensionPixelSize(
1757                     com.android.internal.R.dimen.navigation_bar_frame_height);
1758             insetsHeight = userContext.getResources().getDimensionPixelSize(
1759                     com.android.internal.R.dimen.navigation_bar_height);
1760         } else {
1761             switch (rotation) {
1762                 case ROTATION_UNDEFINED:
1763                 case Surface.ROTATION_0:
1764                 case Surface.ROTATION_180:
1765                     height = userContext.getResources().getDimensionPixelSize(
1766                             com.android.internal.R.dimen.navigation_bar_frame_height);
1767                     insetsHeight = userContext.getResources().getDimensionPixelSize(
1768                             com.android.internal.R.dimen.navigation_bar_height);
1769                     break;
1770                 case Surface.ROTATION_90:
1771                     gravity = Gravity.RIGHT;
1772                     width = userContext.getResources().getDimensionPixelSize(
1773                             com.android.internal.R.dimen.navigation_bar_width);
1774                     break;
1775                 case Surface.ROTATION_270:
1776                     gravity = Gravity.LEFT;
1777                     width = userContext.getResources().getDimensionPixelSize(
1778                             com.android.internal.R.dimen.navigation_bar_width);
1779                     break;
1780             }
1781         }
1782         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1783                 width,
1784                 height,
1785                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
1786                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1787                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1788                         | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1789                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
1790                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
1791                 PixelFormat.TRANSLUCENT);
1792         lp.gravity = gravity;
1793         lp.providedInsets = getInsetsFrameProvider(insetsHeight, userContext);
1794 
1795         lp.token = new Binder();
1796         lp.accessibilityTitle = userContext.getString(R.string.nav_bar);
1797         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC
1798                 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
1799         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
1800         lp.windowAnimations = 0;
1801         lp.setTitle("NavigationBar" + userContext.getDisplayId());
1802         lp.setFitInsetsTypes(0 /* types */);
1803         lp.setTrustedOverlay();
1804         return lp;
1805     }
1806 
getInsetsFrameProvider(int insetsHeight, Context userContext)1807     private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) {
1808         final InsetsFrameProvider navBarProvider =
1809                 new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars());
1810         if (!ENABLE_HIDE_IME_CAPTION_BAR) {
1811             navBarProvider.setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] {
1812                     new InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, null)
1813             });
1814         }
1815         if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
1816             navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight));
1817         }
1818         final boolean needsScrim = userContext.getResources().getBoolean(
1819                 com.android.internal.R.bool.config_navBarNeedsScrim);
1820         navBarProvider.setFlags(needsScrim ? 0 : FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM);
1821 
1822         final InsetsFrameProvider tappableElementProvider = new InsetsFrameProvider(
1823                 mInsetsSourceOwner, 0, WindowInsets.Type.tappableElement());
1824         final boolean tapThrough = userContext.getResources().getBoolean(
1825                 com.android.internal.R.bool.config_navBarTapThrough);
1826         if (tapThrough) {
1827             tappableElementProvider.setInsetsSize(Insets.NONE);
1828         }
1829 
1830         final int gestureHeight = userContext.getResources().getDimensionPixelSize(
1831                 com.android.internal.R.dimen.navigation_bar_gesture_height);
1832         final boolean handlingGesture = mEdgeBackGestureHandler.isHandlingGestures();
1833         final InsetsFrameProvider mandatoryGestureProvider = new InsetsFrameProvider(
1834                 mInsetsSourceOwner, 0, WindowInsets.Type.mandatorySystemGestures());
1835         if (handlingGesture) {
1836             mandatoryGestureProvider.setInsetsSize(Insets.of(0, 0, 0, gestureHeight));
1837         }
1838         final int gestureInsetsLeft = handlingGesture
1839                 ? mEdgeBackGestureHandler.getEdgeWidthLeft() : 0;
1840         final int gestureInsetsRight = handlingGesture
1841                 ? mEdgeBackGestureHandler.getEdgeWidthRight() : 0;
1842         return new InsetsFrameProvider[] {
1843                 navBarProvider,
1844                 tappableElementProvider,
1845                 mandatoryGestureProvider,
1846                 new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.systemGestures())
1847                         .setSource(InsetsFrameProvider.SOURCE_DISPLAY)
1848                         .setInsetsSize(Insets.of(gestureInsetsLeft, 0, 0, 0))
1849                         .setMinimalInsetsSizeInDisplayCutoutSafe(
1850                                 Insets.of(gestureInsetsLeft, 0, 0, 0)),
1851                 new InsetsFrameProvider(mInsetsSourceOwner, 1, WindowInsets.Type.systemGestures())
1852                         .setSource(InsetsFrameProvider.SOURCE_DISPLAY)
1853                         .setInsetsSize(Insets.of(0, 0, gestureInsetsRight, 0))
1854                         .setMinimalInsetsSizeInDisplayCutoutSafe(
1855                                 Insets.of(0, 0, gestureInsetsRight, 0))
1856         };
1857     }
1858 
canShowSecondaryHandle()1859     private boolean canShowSecondaryHandle() {
1860         return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
1861     }
1862 
1863     private final UserTracker.Callback mUserChangedCallback =
1864             new UserTracker.Callback() {
1865                 @Override
1866                 public void onUserChanged(int newUser, @NonNull Context userContext) {
1867                     // The accessibility settings may be different for the new user
1868                     updateAccessibilityStateFlags();
1869                 }
1870             };
1871 
1872     @VisibleForTesting
getNavigationIconHints()1873     int getNavigationIconHints() {
1874         return mNavigationIconHints;
1875     }
1876 
setNavigationIconHints(int hints)1877     private void setNavigationIconHints(int hints) {
1878         if (hints == mNavigationIconHints) return;
1879         if (!isLargeScreen(mContext)) {
1880             // All IME functions handled by launcher via Sysui flags for large screen
1881             final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
1882             final boolean oldBackAlt =
1883                     (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
1884             if (newBackAlt != oldBackAlt) {
1885                 mView.onImeVisibilityChanged(newBackAlt);
1886                 mImeVisible = newBackAlt;
1887             }
1888 
1889             mView.setNavigationIconHints(hints);
1890         }
1891         if (DEBUG) {
1892             android.widget.Toast.makeText(mContext,
1893                     "Navigation icon hints = " + hints,
1894                     500).show();
1895         }
1896         mNavigationIconHints = hints;
1897     }
1898 
1899     /**
1900      * @param inScreenSpace Whether to return values in screen space or window space
1901      * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
1902      * @return
1903      */
getButtonLocations(boolean inScreenSpace, boolean useNearestRegion)1904     Region getButtonLocations(boolean inScreenSpace, boolean useNearestRegion) {
1905         if (useNearestRegion && !inScreenSpace) {
1906             // We currently don't support getting the nearest region in anything but screen space
1907             useNearestRegion = false;
1908         }
1909         Region region = new Region();
1910         Map<View, Rect> touchRegionCache = mView.getButtonTouchRegionCache();
1911         updateButtonLocation(
1912                 region, touchRegionCache, mView.getBackButton(), inScreenSpace, useNearestRegion);
1913         updateButtonLocation(
1914                 region, touchRegionCache, mView.getHomeButton(), inScreenSpace, useNearestRegion);
1915         updateButtonLocation(region, touchRegionCache, mView.getRecentsButton(), inScreenSpace,
1916                 useNearestRegion);
1917         updateButtonLocation(region, touchRegionCache, mView.getImeSwitchButton(), inScreenSpace,
1918                 useNearestRegion);
1919         updateButtonLocation(
1920                 region, touchRegionCache, mView.getAccessibilityButton(), inScreenSpace,
1921                 useNearestRegion);
1922         if (mView.getFloatingRotationButton().isVisible()) {
1923             // Note: this button is floating so the nearest region doesn't apply
1924             updateButtonLocation(
1925                     region, mView.getFloatingRotationButton().getCurrentView(), inScreenSpace);
1926         }
1927         return region;
1928     }
1929 
updateButtonLocation( Region region, Map<View, Rect> touchRegionCache, ButtonDispatcher button, boolean inScreenSpace, boolean useNearestRegion)1930     private void updateButtonLocation(
1931             Region region,
1932             Map<View, Rect> touchRegionCache,
1933             ButtonDispatcher button,
1934             boolean inScreenSpace,
1935             boolean useNearestRegion) {
1936         if (button == null) {
1937             return;
1938         }
1939         View view = button.getCurrentView();
1940         if (view == null || !button.isVisible()) {
1941             return;
1942         }
1943         // If the button is tappable from perspective of NearestTouchFrame, then we'll
1944         // include the regions where the tap is valid instead of just the button layout location
1945         if (useNearestRegion && touchRegionCache.containsKey(view)) {
1946             region.op(touchRegionCache.get(view), Region.Op.UNION);
1947             return;
1948         }
1949         updateButtonLocation(region, view, inScreenSpace);
1950     }
1951 
updateButtonLocation(Region region, View view, boolean inScreenSpace)1952     private void updateButtonLocation(Region region, View view, boolean inScreenSpace) {
1953         Rect bounds = new Rect();
1954         if (inScreenSpace) {
1955             view.getBoundsOnScreen(bounds);
1956         } else {
1957             int[] location = new int[2];
1958             view.getLocationInWindow(location);
1959             bounds.set(location[0], location[1],
1960                     location[0] + view.getWidth(),
1961                     location[1] + view.getHeight());
1962         }
1963         region.op(bounds, Region.Op.UNION);
1964     }
1965 
setOrientedHandleSamplingRegion(Rect orientedHandleSamplingRegion)1966     void setOrientedHandleSamplingRegion(Rect orientedHandleSamplingRegion) {
1967         mOrientedHandleSamplingRegion = orientedHandleSamplingRegion;
1968         mRegionSamplingHelper.updateSamplingRect();
1969     }
1970 
calculateSamplingRect()1971     private Rect calculateSamplingRect() {
1972         mSamplingBounds.setEmpty();
1973         // TODO: Extend this to 2/3 button layout as well
1974         View view = mView.getHomeHandle().getCurrentView();
1975 
1976         if (view != null) {
1977             int[] pos = new int[2];
1978             view.getLocationOnScreen(pos);
1979             Point displaySize = new Point();
1980             view.getContext().getDisplay().getRealSize(displaySize);
1981             final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin,
1982                     displaySize.y - mView.getNavBarHeight(),
1983                     pos[0] + view.getWidth() + mNavColorSampleMargin,
1984                     displaySize.y);
1985             mSamplingBounds.set(samplingBounds);
1986         }
1987 
1988         return mSamplingBounds;
1989     }
1990 
setNavigationBarLumaSamplingEnabled(boolean enable)1991     void setNavigationBarLumaSamplingEnabled(boolean enable) {
1992         if (enable) {
1993             mRegionSamplingHelper.start(mSamplingBounds);
1994         } else {
1995             mRegionSamplingHelper.stop();
1996         }
1997     }
1998 
setNavBarMode(int mode)1999     private void setNavBarMode(int mode) {
2000         mView.setNavBarMode(mode, mNavigationModeController.getImeDrawsImeNavBar());
2001         if (isGesturalMode(mode)) {
2002             mRegionSamplingHelper.start(mSamplingBounds);
2003         } else {
2004             mRegionSamplingHelper.stop();
2005         }
2006     }
2007 
onBarTransition(int newMode)2008     void onBarTransition(int newMode) {
2009         if (newMode == MODE_OPAQUE) {
2010             // If the nav bar background is opaque, stop auto tinting since we know the icons are
2011             // showing over a dark background
2012             mRegionSamplingHelper.stop();
2013             getBarTransitions().getLightTransitionsController().setIconsDark(
2014                     false /* dark */, true /* animate */);
2015         } else {
2016             mRegionSamplingHelper.start(mSamplingBounds);
2017         }
2018     }
2019 
2020     private final ModeChangedListener mModeChangedListener = new ModeChangedListener() {
2021         @Override
2022         public void onNavigationModeChanged(int mode) {
2023             mNavBarMode = mode;
2024 
2025             if (!QuickStepContract.isGesturalMode(mode)) {
2026                 // Reset the override alpha
2027                 if (getBarTransitions() != null) {
2028                     getBarTransitions().setBackgroundOverrideAlpha(1f);
2029                 }
2030             }
2031 
2032             // Update the window layout params when the nav mode changes as that will affect the
2033             // system gesture insets
2034             setNavBarMode(mode);
2035             repositionNavigationBar(mCurrentRotation);
2036 
2037             if (!canShowSecondaryHandle()) {
2038                 resetSecondaryHandle();
2039             }
2040             mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
2041         }
2042     };
2043 
2044     private final Gefingerpoken mTouchHandler = new Gefingerpoken() {
2045         private boolean mDeadZoneConsuming;
2046 
2047         @Override
2048         public boolean onInterceptTouchEvent(MotionEvent ev) {
2049             if (isGesturalMode(mNavBarMode) && mImeVisible
2050                     && ev.getAction() == MotionEvent.ACTION_DOWN) {
2051                 SysUiStatsLog.write(SysUiStatsLog.IME_TOUCH_REPORTED,
2052                         (int) ev.getX(), (int) ev.getY());
2053             }
2054             return shouldDeadZoneConsumeTouchEvents(ev);
2055         }
2056 
2057         @Override
2058         public boolean onTouchEvent(MotionEvent ev) {
2059             shouldDeadZoneConsumeTouchEvents(ev);
2060             return false;
2061         }
2062 
2063         private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
2064             int action = event.getActionMasked();
2065             if (action == MotionEvent.ACTION_DOWN) {
2066                 mDeadZoneConsuming = false;
2067             }
2068             if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
2069                 switch (action) {
2070                     case MotionEvent.ACTION_DOWN:
2071                         // Allow gestures starting in the deadzone to be slippery
2072                         mView.setSlippery(true);
2073                         mDeadZoneConsuming = true;
2074                         break;
2075                     case MotionEvent.ACTION_CANCEL:
2076                     case MotionEvent.ACTION_UP:
2077                         // When a gesture started in the deadzone is finished, restore
2078                         // slippery state
2079                         mView.updateSlippery();
2080                         mDeadZoneConsuming = false;
2081                         break;
2082                 }
2083                 return true;
2084             }
2085             return false;
2086         }
2087     };
2088 }
2089