• 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.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
20 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
21 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
22 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
23 import static android.app.StatusBarManager.WindowType;
24 import static android.app.StatusBarManager.WindowVisibleState;
25 import static android.app.StatusBarManager.windowStateToString;
26 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
27 import static android.view.Display.DEFAULT_DISPLAY;
28 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
29 import static android.view.InsetsState.containsType;
30 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
31 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
32 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
33 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
34 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
35 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
36 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
37 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
38 
39 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
40 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
41 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
42 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
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_NAV_BAR_HIDDEN;
48 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
49 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
50 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
51 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
52 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
53 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
54 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
55 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
56 
57 import android.accessibilityservice.AccessibilityServiceInfo;
58 import android.annotation.IdRes;
59 import android.annotation.Nullable;
60 import android.app.ActivityManager;
61 import android.app.ActivityTaskManager;
62 import android.app.IActivityTaskManager;
63 import android.app.StatusBarManager;
64 import android.content.BroadcastReceiver;
65 import android.content.ContentResolver;
66 import android.content.Context;
67 import android.content.Intent;
68 import android.content.IntentFilter;
69 import android.content.res.Configuration;
70 import android.database.ContentObserver;
71 import android.graphics.PixelFormat;
72 import android.graphics.Rect;
73 import android.graphics.RectF;
74 import android.inputmethodservice.InputMethodService;
75 import android.net.Uri;
76 import android.os.Binder;
77 import android.os.Bundle;
78 import android.os.Handler;
79 import android.os.IBinder;
80 import android.os.Looper;
81 import android.os.RemoteException;
82 import android.os.UserHandle;
83 import android.provider.DeviceConfig;
84 import android.provider.Settings;
85 import android.telecom.TelecomManager;
86 import android.text.TextUtils;
87 import android.util.Log;
88 import android.view.Display;
89 import android.view.Gravity;
90 import android.view.HapticFeedbackConstants;
91 import android.view.IWindowManager;
92 import android.view.InsetsState.InternalInsetsType;
93 import android.view.KeyEvent;
94 import android.view.LayoutInflater;
95 import android.view.MotionEvent;
96 import android.view.Surface;
97 import android.view.View;
98 import android.view.ViewTreeObserver;
99 import android.view.WindowInsetsController.Appearance;
100 import android.view.WindowInsetsController.Behavior;
101 import android.view.WindowManager;
102 import android.view.accessibility.AccessibilityEvent;
103 import android.view.accessibility.AccessibilityManager;
104 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
105 import android.view.inputmethod.InputMethodManager;
106 
107 import androidx.annotation.VisibleForTesting;
108 
109 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
110 import com.android.internal.logging.MetricsLogger;
111 import com.android.internal.logging.UiEvent;
112 import com.android.internal.logging.UiEventLogger;
113 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
114 import com.android.internal.util.LatencyTracker;
115 import com.android.internal.view.AppearanceRegion;
116 import com.android.systemui.Dependency;
117 import com.android.systemui.R;
118 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
119 import com.android.systemui.accessibility.SystemActions;
120 import com.android.systemui.assist.AssistManager;
121 import com.android.systemui.broadcast.BroadcastDispatcher;
122 import com.android.systemui.dagger.qualifiers.Main;
123 import com.android.systemui.model.SysUiState;
124 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
125 import com.android.systemui.navigationbar.buttons.KeyButtonView;
126 import com.android.systemui.navigationbar.buttons.RotationContextButton;
127 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
128 import com.android.systemui.plugins.DarkIconDispatcher;
129 import com.android.systemui.plugins.statusbar.StatusBarStateController;
130 import com.android.systemui.recents.OverviewProxyService;
131 import com.android.systemui.recents.Recents;
132 import com.android.systemui.settings.UserTracker;
133 import com.android.systemui.shared.system.ActivityManagerWrapper;
134 import com.android.systemui.shared.system.QuickStepContract;
135 import com.android.systemui.statusbar.AutoHideUiElement;
136 import com.android.systemui.statusbar.CommandQueue;
137 import com.android.systemui.statusbar.CommandQueue.Callbacks;
138 import com.android.systemui.statusbar.NotificationRemoteInputManager;
139 import com.android.systemui.statusbar.NotificationShadeDepthController;
140 import com.android.systemui.statusbar.StatusBarState;
141 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
142 import com.android.systemui.statusbar.phone.AutoHideController;
143 import com.android.systemui.statusbar.phone.BarTransitions;
144 import com.android.systemui.statusbar.phone.LightBarController;
145 import com.android.systemui.statusbar.phone.ShadeController;
146 import com.android.systemui.statusbar.phone.StatusBar;
147 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
148 import com.android.systemui.statusbar.policy.BatteryController;
149 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
150 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
151 import com.android.wm.shell.pip.Pip;
152 
153 import java.io.PrintWriter;
154 import java.util.List;
155 import java.util.Locale;
156 import java.util.Optional;
157 import java.util.function.Consumer;
158 
159 import dagger.Lazy;
160 
161 /**
162  * Contains logic for a navigation bar view.
163  */
164 public class NavigationBar implements View.OnAttachStateChangeListener,
165         Callbacks, NavigationModeController.ModeChangedListener,
166         AccessibilityButtonModeObserver.ModeChangedListener {
167 
168     public static final String TAG = "NavigationBar";
169     private static final boolean DEBUG = false;
170     private static final String EXTRA_DISABLE_STATE = "disabled_state";
171     private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
172     private static final String EXTRA_APPEARANCE = "appearance";
173     private static final String EXTRA_BEHAVIOR = "behavior";
174     private static final String EXTRA_TRANSIENT_STATE = "transient_state";
175 
176     /** Allow some time inbetween the long press for back and recents. */
177     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
178     private static final long AUTODIM_TIMEOUT_MS = 2250;
179 
180     private final Context mContext;
181     private final WindowManager mWindowManager;
182     private final AccessibilityManager mAccessibilityManager;
183     private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
184     private final DeviceProvisionedController mDeviceProvisionedController;
185     private final StatusBarStateController mStatusBarStateController;
186     private final MetricsLogger mMetricsLogger;
187     private final Lazy<AssistManager> mAssistManagerLazy;
188     private final SysUiState mSysUiFlagsContainer;
189     private final Lazy<StatusBar> mStatusBarLazy;
190     private final ShadeController mShadeController;
191     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
192     private final OverviewProxyService mOverviewProxyService;
193     private final NavigationModeController mNavigationModeController;
194     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
195     private final BroadcastDispatcher mBroadcastDispatcher;
196     private final CommandQueue mCommandQueue;
197     private final Optional<Pip> mPipOptional;
198     private final Optional<LegacySplitScreen> mSplitScreenOptional;
199     private final Optional<Recents> mRecentsOptional;
200     private final SystemActions mSystemActions;
201     private final Handler mHandler;
202     private final NavigationBarOverlayController mNavbarOverlayController;
203     private final UiEventLogger mUiEventLogger;
204     private final UserTracker mUserTracker;
205     private final NotificationShadeDepthController mNotificationShadeDepthController;
206 
207     private Bundle mSavedState;
208     private NavigationBarView mNavigationBarView;
209 
210     private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
211 
212     private int mNavigationIconHints = 0;
213     private @TransitionMode int mNavigationBarMode;
214     private ContentResolver mContentResolver;
215     private boolean mAssistantAvailable;
216     private boolean mLongPressHomeEnabled;
217     private boolean mAssistantTouchGestureEnabled;
218 
219     private int mDisabledFlags1;
220     private int mDisabledFlags2;
221     private long mLastLockToAppLongPress;
222 
223     private Locale mLocale;
224     private int mLayoutDirection;
225 
226     private boolean mAllowForceNavBarHandleOpaque;
227     private boolean mForceNavBarHandleOpaque;
228     private Optional<Long> mHomeButtonLongPressDurationMs;
229     private boolean mIsCurrentUserSetup;
230 
231     /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
232     private @Appearance int mAppearance;
233 
234     /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */
235     private @Behavior int mBehavior;
236 
237     private boolean mTransientShown;
238     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
239     private LightBarController mLightBarController;
240     private AutoHideController mAutoHideController;
241 
242     @VisibleForTesting
243     public int mDisplayId;
244     private boolean mIsOnDefaultDisplay;
245     public boolean mHomeBlockedThisTouch;
246 
247     /**
248      * When user is QuickSwitching between apps of different orientations, we'll draw a fake
249      * home handle on the orientation they originally touched down to start their swipe
250      * gesture to indicate to them that they can continue in that orientation without having to
251      * rotate the phone
252      * The secondary handle will show when we get
253      * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the
254      * original handle hidden and we'll flip the visibilities once the
255      * {@link #mTasksFrozenListener} fires
256      */
257     private QuickswitchOrientedNavHandle mOrientationHandle;
258     private WindowManager.LayoutParams mOrientationParams;
259     private int mStartingQuickSwitchRotation = -1;
260     private int mCurrentRotation;
261     private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener;
262     private boolean mShowOrientedHandleForImmersiveMode;
263 
264     @com.android.internal.annotations.VisibleForTesting
265     public enum NavBarActionEvent implements UiEventLogger.UiEventEnum {
266 
267         @UiEvent(doc = "Assistant invoked via home button long press.")
268         NAVBAR_ASSIST_LONGPRESS(550);
269 
270         private final int mId;
271 
NavBarActionEvent(int id)272         NavBarActionEvent(int id) {
273             mId = id;
274         }
275 
276         @Override
getId()277         public int getId() {
278             return mId;
279         }
280     }
281 
282     private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
283         @Override
284         public void synchronizeState() {
285             checkNavBarModes();
286         }
287 
288         @Override
289         public boolean shouldHideOnTouch() {
290             return !mNotificationRemoteInputManager.getController().isRemoteInputActive();
291         }
292 
293         @Override
294         public boolean isVisible() {
295             return isTransientShown();
296         }
297 
298         @Override
299         public void hide() {
300             clearTransient();
301         }
302     };
303 
304     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
305         @Override
306         public void onConnectionChanged(boolean isConnected) {
307             mNavigationBarView.updateStates();
308             updateScreenPinningGestures();
309 
310             // Send the assistant availability upon connection
311             if (isConnected) {
312                 updateAssistantEntrypoints();
313             }
314         }
315 
316         @Override
317         public void onQuickStepStarted() {
318             // Use navbar dragging as a signal to hide the rotate button
319             mNavigationBarView.getRotationButtonController().setRotateSuggestionButtonState(false);
320 
321             // Hide the notifications panel when quick step starts
322             mShadeController.collapsePanel(true /* animate */);
323         }
324 
325         @Override
326         public void onPrioritizedRotation(@Surface.Rotation int rotation) {
327             mStartingQuickSwitchRotation = rotation;
328             if (rotation == -1) {
329                 mShowOrientedHandleForImmersiveMode = false;
330             }
331             orientSecondaryHomeHandle();
332         }
333 
334         @Override
335         public void startAssistant(Bundle bundle) {
336             mAssistManagerLazy.get().startAssist(bundle);
337         }
338 
339         @Override
340         public void onNavBarButtonAlphaChanged(float alpha, boolean animate) {
341             if (!mIsCurrentUserSetup) {
342                 // If the current user is not yet setup, then don't update any button alphas
343                 return;
344             }
345             if (QuickStepContract.isLegacyMode(mNavBarMode)) {
346                 // Don't allow the bar buttons to be affected by the alpha
347                 return;
348             }
349 
350             ButtonDispatcher buttonDispatcher = null;
351             boolean forceVisible = false;
352             if (QuickStepContract.isGesturalMode(mNavBarMode)) {
353                 // Disallow home handle animations when in gestural
354                 animate = false;
355                 forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
356                 buttonDispatcher = mNavigationBarView.getHomeHandle();
357                 if (getBarTransitions() != null) {
358                     getBarTransitions().setBackgroundOverrideAlpha(alpha);
359                 }
360             } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
361                 buttonDispatcher = mNavigationBarView.getBackButton();
362             }
363             if (buttonDispatcher != null) {
364                 buttonDispatcher.setVisibility(
365                         (forceVisible || alpha > 0) ? View.VISIBLE : View.INVISIBLE);
366                 buttonDispatcher.setAlpha(forceVisible ? 1f : alpha, animate);
367             }
368         }
369 
370         @Override
371         public void onHomeRotationEnabled(boolean enabled) {
372             mNavigationBarView.getRotationButtonController().setHomeRotationEnabled(enabled);
373         }
374 
375         @Override
376         public void onOverviewShown(boolean fromHome) {
377             // If the overview has fixed orientation that may change display to natural rotation,
378             // we don't want the user rotation to be reset. So after user returns to application,
379             // it can keep in the original rotation.
380             mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
381         }
382 
383         @Override
384         public void onToggleRecentApps() {
385             // The same case as onOverviewShown but only for 3-button navigation.
386             mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
387         }
388     };
389 
390     private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener =
391             new NavigationBarTransitions.DarkIntensityListener() {
392                 @Override
393                 public void onDarkIntensity(float darkIntensity) {
394                     mOrientationHandle.setDarkIntensity(darkIntensity);
395                 }
396             };
397 
398     private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
399     private final Runnable mEnableLayoutTransitions = () ->
400             mNavigationBarView.setLayoutTransitionsEnabled(true);
401     private final Runnable mOnVariableDurationHomeLongClick = () -> {
402         if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) {
403             mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback(
404                     HapticFeedbackConstants.LONG_PRESS,
405                     HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
406         }
407     };
408 
409     private final ContentObserver mAssistContentObserver = new ContentObserver(
410             new Handler(Looper.getMainLooper())) {
411         @Override
412         public void onChange(boolean selfChange, Uri uri) {
413             updateAssistantEntrypoints();
414         }
415     };
416 
417     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
418             new DeviceConfig.OnPropertiesChangedListener() {
419                 @Override
420                 public void onPropertiesChanged(DeviceConfig.Properties properties) {
421                     if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
422                         mForceNavBarHandleOpaque = properties.getBoolean(
423                                 NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
424                     }
425 
426                     if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
427                         mHomeButtonLongPressDurationMs = Optional.of(
428                             properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
429                         ).filter(duration -> duration != 0);
430                         reconfigureHomeLongClick();
431                     }
432                 }
433             };
434 
435     private final DeviceProvisionedController.DeviceProvisionedListener mUserSetupListener =
436             new DeviceProvisionedController.DeviceProvisionedListener() {
437                 @Override
438                 public void onUserSetupChanged() {
439                     mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
440                 }
441             };
442 
443     private final NotificationShadeDepthController.DepthListener mDepthListener =
444             new NotificationShadeDepthController.DepthListener() {
445                 boolean mHasBlurs;
446 
447                 @Override
448                 public void onWallpaperZoomOutChanged(float zoomOut) {
449                 }
450 
451                 @Override
452                 public void onBlurRadiusChanged(int radius) {
453                     boolean hasBlurs = radius != 0;
454                     if (hasBlurs == mHasBlurs) {
455                         return;
456                     }
457                     mHasBlurs = hasBlurs;
458                     mNavigationBarView.setWindowHasBlurs(hasBlurs);
459                 }
460             };
461 
NavigationBar(Context context, WindowManager windowManager, Lazy<AssistManager> assistManagerLazy, AccessibilityManager accessibilityManager, AccessibilityManagerWrapper accessibilityManagerWrapper, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, AccessibilityButtonModeObserver accessibilityButtonModeObserver, StatusBarStateController statusBarStateController, SysUiState sysUiFlagsContainer, BroadcastDispatcher broadcastDispatcher, CommandQueue commandQueue, Optional<Pip> pipOptional, Optional<LegacySplitScreen> splitScreenOptional, Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy, ShadeController shadeController, NotificationRemoteInputManager notificationRemoteInputManager, NotificationShadeDepthController notificationShadeDepthController, SystemActions systemActions, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, UiEventLogger uiEventLogger, UserTracker userTracker)462     public NavigationBar(Context context,
463             WindowManager windowManager,
464             Lazy<AssistManager> assistManagerLazy,
465             AccessibilityManager accessibilityManager,
466             AccessibilityManagerWrapper accessibilityManagerWrapper,
467             DeviceProvisionedController deviceProvisionedController,
468             MetricsLogger metricsLogger,
469             OverviewProxyService overviewProxyService,
470             NavigationModeController navigationModeController,
471             AccessibilityButtonModeObserver accessibilityButtonModeObserver,
472             StatusBarStateController statusBarStateController,
473             SysUiState sysUiFlagsContainer,
474             BroadcastDispatcher broadcastDispatcher,
475             CommandQueue commandQueue,
476             Optional<Pip> pipOptional,
477             Optional<LegacySplitScreen> splitScreenOptional,
478             Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
479             ShadeController shadeController,
480             NotificationRemoteInputManager notificationRemoteInputManager,
481             NotificationShadeDepthController notificationShadeDepthController,
482             SystemActions systemActions,
483             @Main Handler mainHandler,
484             NavigationBarOverlayController navbarOverlayController,
485             UiEventLogger uiEventLogger,
486             UserTracker userTracker) {
487         mContext = context;
488         mWindowManager = windowManager;
489         mAccessibilityManager = accessibilityManager;
490         mAccessibilityManagerWrapper = accessibilityManagerWrapper;
491         mDeviceProvisionedController = deviceProvisionedController;
492         mStatusBarStateController = statusBarStateController;
493         mMetricsLogger = metricsLogger;
494         mAssistManagerLazy = assistManagerLazy;
495         mSysUiFlagsContainer = sysUiFlagsContainer;
496         mStatusBarLazy = statusBarLazy;
497         mShadeController = shadeController;
498         mNotificationRemoteInputManager = notificationRemoteInputManager;
499         mOverviewProxyService = overviewProxyService;
500         mNavigationModeController = navigationModeController;
501         mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
502         mBroadcastDispatcher = broadcastDispatcher;
503         mCommandQueue = commandQueue;
504         mPipOptional = pipOptional;
505         mSplitScreenOptional = splitScreenOptional;
506         mRecentsOptional = recentsOptional;
507         mSystemActions = systemActions;
508         mHandler = mainHandler;
509         mNavbarOverlayController = navbarOverlayController;
510         mUiEventLogger = uiEventLogger;
511         mUserTracker = userTracker;
512         mNotificationShadeDepthController = notificationShadeDepthController;
513 
514         mNavBarMode = mNavigationModeController.addListener(this);
515         mAccessibilityButtonModeObserver.addListener(this);
516     }
517 
getView()518     public NavigationBarView getView() {
519         return mNavigationBarView;
520     }
521 
createView(Bundle savedState)522     public View createView(Bundle savedState) {
523         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
524                 WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
525                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
526                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
527                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
528                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
529                         | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
530                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
531                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
532                 PixelFormat.TRANSLUCENT);
533         lp.token = new Binder();
534         lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
535         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
536         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
537         lp.windowAnimations = 0;
538         lp.setTitle("NavigationBar" + mContext.getDisplayId());
539         lp.setFitInsetsTypes(0 /* types */);
540         lp.setTrustedOverlay();
541 
542         NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
543                 R.layout.navigation_bar_window, null);
544         View barView = LayoutInflater.from(frame.getContext()).inflate(
545                 R.layout.navigation_bar, frame);
546         barView.addOnAttachStateChangeListener(this);
547         mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
548 
549         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
550         mContext.getSystemService(WindowManager.class).addView(frame, lp);
551         mDisplayId = mContext.getDisplayId();
552         mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
553 
554         mCommandQueue.addCallback(this);
555         mAssistantAvailable = mAssistManagerLazy.get()
556                 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
557         mContentResolver = mContext.getContentResolver();
558         mContentResolver.registerContentObserver(
559                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
560                 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
561         mContentResolver.registerContentObserver(
562                 Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
563                 false, mAssistContentObserver, UserHandle.USER_ALL);
564         mContentResolver.registerContentObserver(
565                 Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
566                 false, mAssistContentObserver, UserHandle.USER_ALL);
567         mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
568                 R.bool.allow_force_nav_bar_handle_opaque);
569         mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
570                 DeviceConfig.NAMESPACE_SYSTEMUI,
571                 NAV_BAR_HANDLE_FORCE_OPAQUE,
572                 /* defaultValue = */ true);
573         mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
574                 DeviceConfig.NAMESPACE_SYSTEMUI,
575                 HOME_BUTTON_LONG_PRESS_DURATION_MS,
576                 /* defaultValue = */ 0
577         )).filter(duration -> duration != 0);
578         DeviceConfig.addOnPropertiesChangedListener(
579                 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
580         updateAssistantEntrypoints();
581 
582         if (savedState != null) {
583             mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
584             mDisabledFlags2 = savedState.getInt(EXTRA_DISABLE2_STATE, 0);
585             mAppearance = savedState.getInt(EXTRA_APPEARANCE, 0);
586             mBehavior = savedState.getInt(EXTRA_BEHAVIOR, 0);
587             mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false);
588         }
589         mSavedState = savedState;
590 
591         // Respect the latest disabled-flags.
592         mCommandQueue.recomputeDisableFlags(mDisplayId, false);
593 
594         mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
595         mDeviceProvisionedController.addCallback(mUserSetupListener);
596         mNotificationShadeDepthController.addListener(mDepthListener);
597 
598         setAccessibilityFloatingMenuModeIfNeeded();
599 
600         return barView;
601     }
602 
destroyView()603     public void destroyView() {
604         mCommandQueue.removeCallback(this);
605         mContext.getSystemService(WindowManager.class).removeViewImmediate(
606                 mNavigationBarView.getRootView());
607         mNavigationModeController.removeListener(this);
608         mAccessibilityButtonModeObserver.removeListener(this);
609 
610         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
611         mContentResolver.unregisterContentObserver(mAssistContentObserver);
612         mDeviceProvisionedController.removeCallback(mUserSetupListener);
613         mNotificationShadeDepthController.removeListener(mDepthListener);
614 
615         DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
616     }
617 
618     @Override
onViewAttachedToWindow(View v)619     public void onViewAttachedToWindow(View v) {
620         final Display display = v.getDisplay();
621         mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
622         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
623         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
624         mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
625         if (mSavedState != null) {
626             mNavigationBarView.getLightTransitionsController().restoreState(mSavedState);
627         }
628         mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
629         mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
630         mNavigationBarView.setBehavior(mBehavior);
631 
632         mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
633 
634         mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
635         mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
636 
637         prepareNavigationBarView();
638         checkNavBarModes();
639 
640         IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
641         filter.addAction(Intent.ACTION_SCREEN_ON);
642         filter.addAction(Intent.ACTION_USER_SWITCHED);
643         mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
644                 Handler.getMain(), UserHandle.ALL);
645         notifyNavigationBarScreenOn();
646 
647         mOverviewProxyService.addCallback(mOverviewProxyListener);
648         updateSystemUiStateFlags(-1);
649 
650         // Currently there is no accelerometer sensor on non-default display.
651         if (mIsOnDefaultDisplay) {
652             final RotationButtonController rotationButtonController =
653                     mNavigationBarView.getRotationButtonController();
654             rotationButtonController.addRotationCallback(mRotationWatcher);
655 
656             // Reset user rotation pref to match that of the WindowManager if starting in locked
657             // mode. This will automatically happen when switching from auto-rotate to locked mode.
658             if (display != null && rotationButtonController.isRotationLocked()) {
659                 rotationButtonController.setRotationLockedAtAngle(display.getRotation());
660             }
661         } else {
662             mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
663         }
664         setDisabled2Flags(mDisabledFlags2);
665 
666         initSecondaryHomeHandleForRotation();
667 
668         // Unfortunately, we still need it because status bar needs LightBarController
669         // before notifications creation. We cannot directly use getLightBarController()
670         // from NavigationBarFragment directly.
671         LightBarController lightBarController = mIsOnDefaultDisplay
672                 ? Dependency.get(LightBarController.class)
673                 : new LightBarController(mContext,
674                         Dependency.get(DarkIconDispatcher.class),
675                         Dependency.get(BatteryController.class),
676                         Dependency.get(NavigationModeController.class));
677         setLightBarController(lightBarController);
678 
679         // TODO(b/118592525): to support multi-display, we start to add something which is
680         //                    per-display, while others may be global. I think it's time to
681         //                    add a new class maybe named DisplayDependency to solve
682         //                    per-display Dependency problem.
683         AutoHideController autoHideController = mIsOnDefaultDisplay
684                 ? Dependency.get(AutoHideController.class)
685                 : new AutoHideController(mContext, mHandler,
686                         Dependency.get(IWindowManager.class));
687         setAutoHideController(autoHideController);
688         restoreAppearanceAndTransientState();
689     }
690 
691     @Override
onViewDetachedFromWindow(View v)692     public void onViewDetachedFromWindow(View v) {
693         mNavigationBarView.getBarTransitions().destroy();
694         mNavigationBarView.getLightTransitionsController().destroy(mContext);
695         mOverviewProxyService.removeCallback(mOverviewProxyListener);
696         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
697         if (mOrientationHandle != null) {
698             resetSecondaryHandle();
699             getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
700             mWindowManager.removeView(mOrientationHandle);
701             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
702                     mOrientationHandleGlobalLayoutListener);
703         }
704         mHandler.removeCallbacks(mAutoDim);
705         mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
706         mHandler.removeCallbacks(mEnableLayoutTransitions);
707         mNavigationBarView = null;
708         mOrientationHandle = null;
709     }
710 
711     // TODO: Remove this when we update nav bar recreation
onSaveInstanceState(Bundle outState)712     public void onSaveInstanceState(Bundle outState) {
713         outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
714         outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2);
715         outState.putInt(EXTRA_APPEARANCE, mAppearance);
716         outState.putInt(EXTRA_BEHAVIOR, mBehavior);
717         outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
718         mNavigationBarView.getLightTransitionsController().saveState(outState);
719     }
720 
721     /**
722      * Called when a non-reloading configuration change happens and we need to update.
723      */
onConfigurationChanged(Configuration newConfig)724     public void onConfigurationChanged(Configuration newConfig) {
725         final Locale locale = mContext.getResources().getConfiguration().locale;
726         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
727         if (!locale.equals(mLocale) || ld != mLayoutDirection) {
728             if (DEBUG) {
729                 Log.v(TAG, String.format(
730                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
731                         locale, ld));
732             }
733             mLocale = locale;
734             mLayoutDirection = ld;
735             refreshLayout(ld);
736         }
737 
738         repositionNavigationBar();
739         if (canShowSecondaryHandle()) {
740             int rotation = newConfig.windowConfiguration.getRotation();
741             if (rotation != mCurrentRotation) {
742                 mCurrentRotation = rotation;
743                 orientSecondaryHomeHandle();
744             }
745         }
746     }
747 
initSecondaryHomeHandleForRotation()748     private void initSecondaryHomeHandleForRotation() {
749         if (mNavBarMode != NAV_BAR_MODE_GESTURAL) {
750             return;
751         }
752 
753         mOrientationHandle = new QuickswitchOrientedNavHandle(mContext);
754         mOrientationHandle.setId(R.id.secondary_home_handle);
755 
756         getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
757         mOrientationParams = new WindowManager.LayoutParams(0, 0,
758                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
759                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
760                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
761                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
762                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
763                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
764                 PixelFormat.TRANSLUCENT);
765         mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
766         mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
767         mWindowManager.addView(mOrientationHandle, mOrientationParams);
768         mOrientationHandle.setVisibility(View.GONE);
769         mOrientationParams.setFitInsetsTypes(0 /* types*/);
770         mOrientationHandleGlobalLayoutListener =
771                 () -> {
772                     if (mStartingQuickSwitchRotation == -1) {
773                         return;
774                     }
775 
776                     RectF boundsOnScreen = mOrientationHandle.computeHomeHandleBounds();
777                     mOrientationHandle.mapRectFromViewToScreenCoords(boundsOnScreen, true);
778                     Rect boundsRounded = new Rect();
779                     boundsOnScreen.roundOut(boundsRounded);
780                     mNavigationBarView.setOrientedHandleSamplingRegion(boundsRounded);
781                 };
782         mOrientationHandle.getViewTreeObserver().addOnGlobalLayoutListener(
783                 mOrientationHandleGlobalLayoutListener);
784     }
785 
orientSecondaryHomeHandle()786     private void orientSecondaryHomeHandle() {
787         if (!canShowSecondaryHandle()) {
788             return;
789         }
790 
791         if (mStartingQuickSwitchRotation == -1 || mSplitScreenOptional
792                 .map(LegacySplitScreen::isDividerVisible).orElse(false)) {
793             // Hide the secondary home handle if we are in multiwindow since apps in multiwindow
794             // aren't allowed to set the display orientation
795             resetSecondaryHandle();
796         } else {
797             int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
798             if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) {
799                 // Curious if starting quickswitch can change between the if check and our delta
800                 Log.d(TAG, "secondary nav delta rotation: " + deltaRotation
801                         + " current: " + mCurrentRotation
802                         + " starting: " + mStartingQuickSwitchRotation);
803             }
804             int height = 0;
805             int width = 0;
806             Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
807             mOrientationHandle.setDeltaRotation(deltaRotation);
808             switch (deltaRotation) {
809                 case Surface.ROTATION_90:
810                 case Surface.ROTATION_270:
811                     height = dispSize.height();
812                     width = mNavigationBarView.getHeight();
813                     break;
814                 case Surface.ROTATION_180:
815                 case Surface.ROTATION_0:
816                     // TODO(b/152683657): Need to determine best UX for this
817                     if (!mShowOrientedHandleForImmersiveMode) {
818                         resetSecondaryHandle();
819                         return;
820                     }
821                     width = dispSize.width();
822                     height = mNavigationBarView.getHeight();
823                     break;
824             }
825 
826             mOrientationParams.gravity =
827                     deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM :
828                             (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT);
829             mOrientationParams.height = height;
830             mOrientationParams.width = width;
831             mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
832             mNavigationBarView.setVisibility(View.GONE);
833             mOrientationHandle.setVisibility(View.VISIBLE);
834         }
835     }
836 
resetSecondaryHandle()837     private void resetSecondaryHandle() {
838         if (mOrientationHandle != null) {
839             // Case where nav mode is changed w/o ever invoking a quickstep
840             // mOrientedHandle is initialized lazily
841             mOrientationHandle.setVisibility(View.GONE);
842         }
843         mNavigationBarView.setVisibility(View.VISIBLE);
844         mNavigationBarView.setOrientedHandleSamplingRegion(null);
845     }
846 
reconfigureHomeLongClick()847     private void reconfigureHomeLongClick() {
848         if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
849             return;
850         }
851         if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
852             mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
853             mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
854             mNavigationBarView.getHomeButton().setOnLongClickListener(null);
855         } else {
856             mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true);
857             mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true);
858             mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
859         }
860     }
861 
deltaRotation(int oldRotation, int newRotation)862     private int deltaRotation(int oldRotation, int newRotation) {
863         int delta = newRotation - oldRotation;
864         if (delta < 0) delta += 4;
865         return delta;
866     }
867 
dump(PrintWriter pw)868     public void dump(PrintWriter pw) {
869         pw.println("NavigationBar (displayId=" + mDisplayId + "):");
870         pw.println("  mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
871         pw.println("  mCurrentRotation=" + mCurrentRotation);
872         pw.println("  mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
873         pw.println("  mLongPressHomeEnabled=" + mLongPressHomeEnabled);
874         pw.println("  mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
875         pw.println("  mNavigationBarWindowState="
876                 + windowStateToString(mNavigationBarWindowState));
877         pw.println("  mNavigationBarMode="
878                 + BarTransitions.modeToString(mNavigationBarMode));
879         dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
880         mNavigationBarView.dump(pw);
881     }
882 
883     // ----- CommandQueue Callbacks -----
884 
885     @Override
setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)886     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
887             boolean showImeSwitcher) {
888         if (displayId != mDisplayId) {
889             return;
890         }
891         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
892         int hints = mNavigationIconHints;
893         switch (backDisposition) {
894             case InputMethodService.BACK_DISPOSITION_DEFAULT:
895             case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
896             case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
897                 if (imeShown) {
898                     hints |= NAVIGATION_HINT_BACK_ALT;
899                 } else {
900                     hints &= ~NAVIGATION_HINT_BACK_ALT;
901                 }
902                 break;
903             case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
904                 hints &= ~NAVIGATION_HINT_BACK_ALT;
905                 break;
906         }
907         if (showImeSwitcher) {
908             hints |= NAVIGATION_HINT_IME_SHOWN;
909         } else {
910             hints &= ~NAVIGATION_HINT_IME_SHOWN;
911         }
912         if (hints == mNavigationIconHints) return;
913 
914         mNavigationIconHints = hints;
915         mNavigationBarView.setNavigationIconHints(hints);
916         checkBarModes();
917         updateSystemUiStateFlags(-1);
918     }
919 
920     @Override
setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state)921     public void setWindowState(
922             int displayId, @WindowType int window, @WindowVisibleState int state) {
923         if (displayId == mDisplayId
924                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
925                 && mNavigationBarWindowState != state) {
926             mNavigationBarWindowState = state;
927             updateSystemUiStateFlags(-1);
928             mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN;
929             if (mOrientationHandle != null
930                     && mStartingQuickSwitchRotation != -1) {
931                 orientSecondaryHomeHandle();
932             }
933             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
934             mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
935         }
936     }
937 
938     @Override
onRotationProposal(final int rotation, boolean isValid)939     public void onRotationProposal(final int rotation, boolean isValid) {
940         final int winRotation = mNavigationBarView.getDisplay().getRotation();
941         final boolean rotateSuggestionsDisabled = RotationButtonController
942                 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
943         final RotationButtonController rotationButtonController =
944                 mNavigationBarView.getRotationButtonController();
945         final RotationButton rotationButton = rotationButtonController.getRotationButton();
946 
947         if (RotationContextButton.DEBUG_ROTATION) {
948             Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
949                     + ", winRotation=" + Surface.rotationToString(winRotation)
950                     + ", isValid=" + isValid + ", mNavBarWindowState="
951                     + StatusBarManager.windowStateToString(mNavigationBarWindowState)
952                     + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
953                     + ", isRotateButtonVisible=" + rotationButton.isVisible());
954         }
955 
956         // Respect the disabled flag, no need for action as flag change callback will handle hiding
957         if (rotateSuggestionsDisabled) return;
958 
959         rotationButtonController.onRotationProposal(rotation, winRotation, isValid);
960     }
961 
962     @Override
onRecentsAnimationStateChanged(boolean running)963     public void onRecentsAnimationStateChanged(boolean running) {
964         if (running) {
965             mNavbarOverlayController.setButtonState(/* visible */false, /* force */true);
966         }
967         mNavigationBarView.getRotationButtonController().setRecentsAnimationRunning(running);
968     }
969 
970     /** Restores the appearance and the transient saved state to {@link NavigationBar}. */
restoreAppearanceAndTransientState()971     public void restoreAppearanceAndTransientState() {
972         final int barMode = barMode(mTransientShown, mAppearance);
973         mNavigationBarMode = barMode;
974         checkNavBarModes();
975         if (mAutoHideController != null) {
976             mAutoHideController.touchAutoHide();
977         }
978         if (mLightBarController != null) {
979             mLightBarController.onNavigationBarAppearanceChanged(mAppearance,
980                     true /* nbModeChanged */, barMode, false /* navbarColorManagedByIme */);
981         }
982     }
983 
984     @Override
onSystemBarAttributesChanged(int displayId, @Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @Behavior int behavior, boolean isFullscreen)985     public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
986             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
987             @Behavior int behavior, boolean isFullscreen) {
988         if (displayId != mDisplayId) {
989             return;
990         }
991         boolean nbModeChanged = false;
992         if (mAppearance != appearance) {
993             mAppearance = appearance;
994             nbModeChanged = updateBarMode(barMode(mTransientShown, appearance));
995         }
996         if (mLightBarController != null) {
997             mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
998                     mNavigationBarMode, navbarColorManagedByIme);
999         }
1000         if (mBehavior != behavior) {
1001             mBehavior = behavior;
1002             mNavigationBarView.setBehavior(behavior);
1003             updateSystemUiStateFlags(-1);
1004         }
1005     }
1006 
1007     @Override
showTransient(int displayId, @InternalInsetsType int[] types)1008     public void showTransient(int displayId, @InternalInsetsType int[] types) {
1009         if (displayId != mDisplayId) {
1010             return;
1011         }
1012         if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
1013             return;
1014         }
1015         if (!mTransientShown) {
1016             mTransientShown = true;
1017             handleTransientChanged();
1018         }
1019     }
1020 
1021     @Override
abortTransient(int displayId, @InternalInsetsType int[] types)1022     public void abortTransient(int displayId, @InternalInsetsType int[] types) {
1023         if (displayId != mDisplayId) {
1024             return;
1025         }
1026         if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
1027             return;
1028         }
1029         clearTransient();
1030     }
1031 
clearTransient()1032     private void clearTransient() {
1033         if (mTransientShown) {
1034             mTransientShown = false;
1035             handleTransientChanged();
1036         }
1037     }
1038 
handleTransientChanged()1039     private void handleTransientChanged() {
1040         mNavigationBarView.onTransientStateChanged(mTransientShown);
1041         final int barMode = barMode(mTransientShown, mAppearance);
1042         if (updateBarMode(barMode) && mLightBarController != null) {
1043             mLightBarController.onNavigationBarModeChanged(barMode);
1044         }
1045     }
1046 
1047     // Returns true if the bar mode is changed.
updateBarMode(int barMode)1048     private boolean updateBarMode(int barMode) {
1049         if (mNavigationBarMode != barMode) {
1050             mNavigationBarMode = barMode;
1051             checkNavBarModes();
1052             if (mAutoHideController != null) {
1053                 mAutoHideController.touchAutoHide();
1054             }
1055             return true;
1056         }
1057         return false;
1058     }
1059 
barMode(boolean isTransient, int appearance)1060     private static @TransitionMode int barMode(boolean isTransient, int appearance) {
1061         final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS;
1062         if (isTransient) {
1063             return MODE_SEMI_TRANSPARENT;
1064         } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
1065             return MODE_LIGHTS_OUT;
1066         } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
1067             return MODE_LIGHTS_OUT_TRANSPARENT;
1068         } else if ((appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) != 0) {
1069             return MODE_OPAQUE;
1070         } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS) != 0) {
1071             return MODE_SEMI_TRANSPARENT;
1072         } else {
1073             return MODE_TRANSPARENT;
1074         }
1075     }
1076 
1077     @Override
disable(int displayId, int state1, int state2, boolean animate)1078     public void disable(int displayId, int state1, int state2, boolean animate) {
1079         if (displayId != mDisplayId) {
1080             return;
1081         }
1082         // Navigation bar flags are in both state1 and state2.
1083         final int masked = state1 & (StatusBarManager.DISABLE_HOME
1084                 | StatusBarManager.DISABLE_RECENT
1085                 | StatusBarManager.DISABLE_BACK
1086                 | StatusBarManager.DISABLE_SEARCH);
1087         if (masked != mDisabledFlags1) {
1088             mDisabledFlags1 = masked;
1089             mNavigationBarView.setDisabledFlags(state1);
1090             updateScreenPinningGestures();
1091         }
1092 
1093         // Only default display supports rotation suggestions.
1094         if (mIsOnDefaultDisplay) {
1095             final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
1096             if (masked2 != mDisabledFlags2) {
1097                 mDisabledFlags2 = masked2;
1098                 setDisabled2Flags(masked2);
1099             }
1100         }
1101     }
1102 
setDisabled2Flags(int state2)1103     private void setDisabled2Flags(int state2) {
1104         // Method only called on change of disable2 flags
1105         mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2);
1106     }
1107 
1108     // ----- Internal stuff -----
1109 
refreshLayout(int layoutDirection)1110     private void refreshLayout(int layoutDirection) {
1111         mNavigationBarView.setLayoutDirection(layoutDirection);
1112     }
1113 
shouldDisableNavbarGestures()1114     private boolean shouldDisableNavbarGestures() {
1115         return !mDeviceProvisionedController.isDeviceProvisioned()
1116                 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
1117     }
1118 
repositionNavigationBar()1119     private void repositionNavigationBar() {
1120         if (!mNavigationBarView.isAttachedToWindow()) return;
1121 
1122         prepareNavigationBarView();
1123 
1124         mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
1125                 ((View) mNavigationBarView.getParent()).getLayoutParams());
1126     }
1127 
updateScreenPinningGestures()1128     private void updateScreenPinningGestures() {
1129         // Change the cancel pin gesture to home and back if recents button is invisible
1130         boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
1131         ButtonDispatcher backButton = mNavigationBarView.getBackButton();
1132         ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
1133         if (pinningActive) {
1134             boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
1135             backButton.setOnLongClickListener(recentsVisible
1136                     ? this::onLongPressBackRecents
1137                     : this::onLongPressBackHome);
1138             recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
1139         } else {
1140             backButton.setOnLongClickListener(null);
1141             recentsButton.setOnLongClickListener(null);
1142         }
1143         // Note, this needs to be set after even if we're setting the listener to null
1144         backButton.setLongClickable(pinningActive);
1145         recentsButton.setLongClickable(pinningActive);
1146     }
1147 
notifyNavigationBarScreenOn()1148     private void notifyNavigationBarScreenOn() {
1149         mNavigationBarView.updateNavButtonIcons();
1150     }
1151 
prepareNavigationBarView()1152     private void prepareNavigationBarView() {
1153         mNavigationBarView.reorient();
1154 
1155         ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
1156         recentsButton.setOnClickListener(this::onRecentsClick);
1157         recentsButton.setOnTouchListener(this::onRecentsTouch);
1158 
1159         ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
1160         homeButton.setOnTouchListener(this::onHomeTouch);
1161 
1162         reconfigureHomeLongClick();
1163 
1164         ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
1165         accessibilityButton.setOnClickListener(this::onAccessibilityClick);
1166         accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
1167         updateAccessibilityServicesState(mAccessibilityManager);
1168 
1169         ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
1170         imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
1171 
1172         updateScreenPinningGestures();
1173     }
1174 
1175     @VisibleForTesting
onHomeTouch(View v, MotionEvent event)1176     boolean onHomeTouch(View v, MotionEvent event) {
1177         if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
1178             return true;
1179         }
1180         // If an incoming call is ringing, HOME is totally disabled.
1181         // (The user is already on the InCallUI at this point,
1182         // and their ONLY options are to answer or reject the call.)
1183         switch (event.getAction()) {
1184             case MotionEvent.ACTION_DOWN:
1185                 mHomeBlockedThisTouch = false;
1186                 TelecomManager telecomManager =
1187                         mContext.getSystemService(TelecomManager.class);
1188                 if (telecomManager != null && telecomManager.isRinging()) {
1189                     if (mStatusBarLazy.get().isKeyguardShowing()) {
1190                         Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
1191                                 "No heads up");
1192                         mHomeBlockedThisTouch = true;
1193                         return true;
1194                     }
1195                 }
1196                 if (mLongPressHomeEnabled) {
1197                     mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
1198                         mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
1199                     });
1200                 }
1201                 break;
1202             case MotionEvent.ACTION_UP:
1203             case MotionEvent.ACTION_CANCEL:
1204                 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
1205                 mStatusBarLazy.get().awakenDreams();
1206                 break;
1207         }
1208         return false;
1209     }
1210 
onVerticalChanged(boolean isVertical)1211     private void onVerticalChanged(boolean isVertical) {
1212         mStatusBarLazy.get().setQsScrimEnabled(!isVertical);
1213     }
1214 
onNavigationTouch(View v, MotionEvent event)1215     private boolean onNavigationTouch(View v, MotionEvent event) {
1216         if (mAutoHideController != null) {
1217             mAutoHideController.checkUserAutoHide(event);
1218         }
1219         return false;
1220     }
1221 
1222     @VisibleForTesting
onHomeLongClick(View v)1223     boolean onHomeLongClick(View v) {
1224         if (!mNavigationBarView.isRecentsButtonVisible()
1225                 && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
1226             return onLongPressBackHome(v);
1227         }
1228         if (shouldDisableNavbarGestures()) {
1229             return false;
1230         }
1231         mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);
1232         mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS);
1233         Bundle args = new Bundle();
1234         args.putInt(
1235                 AssistManager.INVOCATION_TYPE_KEY,
1236                 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
1237         mAssistManagerLazy.get().startAssist(args);
1238         mStatusBarLazy.get().awakenDreams();
1239         mNavigationBarView.abortCurrentGesture();
1240         return true;
1241     }
1242 
1243     // additional optimization when we have software system buttons - start loading the recent
1244     // tasks on touch down
onRecentsTouch(View v, MotionEvent event)1245     private boolean onRecentsTouch(View v, MotionEvent event) {
1246         int action = event.getAction() & MotionEvent.ACTION_MASK;
1247         if (action == MotionEvent.ACTION_DOWN) {
1248             mCommandQueue.preloadRecentApps();
1249         } else if (action == MotionEvent.ACTION_CANCEL) {
1250             mCommandQueue.cancelPreloadRecentApps();
1251         } else if (action == MotionEvent.ACTION_UP) {
1252             if (!v.isPressed()) {
1253                 mCommandQueue.cancelPreloadRecentApps();
1254             }
1255         }
1256         return false;
1257     }
1258 
onRecentsClick(View v)1259     private void onRecentsClick(View v) {
1260         if (LatencyTracker.isEnabled(mContext)) {
1261             LatencyTracker.getInstance(mContext).onActionStart(
1262                     LatencyTracker.ACTION_TOGGLE_RECENTS);
1263         }
1264         mStatusBarLazy.get().awakenDreams();
1265         mCommandQueue.toggleRecentApps();
1266     }
1267 
onImeSwitcherClick(View v)1268     private void onImeSwitcherClick(View v) {
1269         mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
1270                 true /* showAuxiliarySubtypes */, mDisplayId);
1271     };
1272 
onLongPressBackHome(View v)1273     private boolean onLongPressBackHome(View v) {
1274         return onLongPressNavigationButtons(v, R.id.back, R.id.home);
1275     }
1276 
onLongPressBackRecents(View v)1277     private boolean onLongPressBackRecents(View v) {
1278         return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
1279     }
1280 
1281     /**
1282      * This handles long-press of both back and recents/home. Back is the common button with
1283      * combination of recents if it is visible or home if recents is invisible.
1284      * They are handled together to capture them both being long-pressed
1285      * at the same time to exit screen pinning (lock task).
1286      *
1287      * When accessibility mode is on, only a long-press from recents/home
1288      * is required to exit.
1289      *
1290      * In all other circumstances we try to pass through long-press events
1291      * for Back, so that apps can still use it.  Which can be from two things.
1292      * 1) Not currently in screen pinning (lock task).
1293      * 2) Back is long-pressed without recents/home.
1294      */
onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2)1295     private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
1296         try {
1297             boolean sendBackLongPress = false;
1298             IActivityTaskManager activityManager = ActivityTaskManager.getService();
1299             boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
1300             boolean inLockTaskMode = activityManager.isInLockTaskMode();
1301             boolean stopLockTaskMode = false;
1302             try {
1303                 if (inLockTaskMode && !touchExplorationEnabled) {
1304                     long time = System.currentTimeMillis();
1305 
1306                     // If we recently long-pressed the other button then they were
1307                     // long-pressed 'together'
1308                     if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
1309                         stopLockTaskMode = true;
1310                         return true;
1311                     } else if (v.getId() == btnId1) {
1312                         ButtonDispatcher button = btnId2 == R.id.recent_apps
1313                                 ? mNavigationBarView.getRecentsButton()
1314                                 : mNavigationBarView.getHomeButton();
1315                         if (!button.getCurrentView().isPressed()) {
1316                             // If we aren't pressing recents/home right now then they presses
1317                             // won't be together, so send the standard long-press action.
1318                             sendBackLongPress = true;
1319                         }
1320                     }
1321                     mLastLockToAppLongPress = time;
1322                 } else {
1323                     // If this is back still need to handle sending the long-press event.
1324                     if (v.getId() == btnId1) {
1325                         sendBackLongPress = true;
1326                     } else if (touchExplorationEnabled && inLockTaskMode) {
1327                         // When in accessibility mode a long press that is recents/home (not back)
1328                         // should stop lock task.
1329                         stopLockTaskMode = true;
1330                         return true;
1331                     } else if (v.getId() == btnId2) {
1332                         return btnId2 == R.id.recent_apps
1333                                 ? onLongPressRecents()
1334                                 : onHomeLongClick(
1335                                         mNavigationBarView.getHomeButton().getCurrentView());
1336                     }
1337                 }
1338             } finally {
1339                 if (stopLockTaskMode) {
1340                     activityManager.stopSystemLockTaskMode();
1341                     // When exiting refresh disabled flags.
1342                     mNavigationBarView.updateNavButtonIcons();
1343                 }
1344             }
1345 
1346             if (sendBackLongPress) {
1347                 KeyButtonView keyButtonView = (KeyButtonView) v;
1348                 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
1349                 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
1350                 return true;
1351             }
1352         } catch (RemoteException e) {
1353             Log.d(TAG, "Unable to reach activity manager", e);
1354         }
1355         return false;
1356     }
1357 
onLongPressRecents()1358     private boolean onLongPressRecents() {
1359         if (mRecentsOptional.isPresent() || !ActivityTaskManager.supportsMultiWindow(mContext)
1360                 || ActivityManager.isLowRamDeviceStatic()
1361                 // If we are connected to the overview service, then disable the recents button
1362                 || mOverviewProxyService.getProxy() != null
1363                 || !mSplitScreenOptional.map(splitScreen ->
1364                 splitScreen.getDividerView().getSnapAlgorithm().isSplitScreenFeasible())
1365                 .orElse(false)) {
1366             return false;
1367         }
1368 
1369         return mStatusBarLazy.get().toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
1370                 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
1371     }
1372 
onAccessibilityClick(View v)1373     private void onAccessibilityClick(View v) {
1374         final Display display = v.getDisplay();
1375         mAccessibilityManager.notifyAccessibilityButtonClicked(
1376                 display != null ? display.getDisplayId() : DEFAULT_DISPLAY);
1377     }
1378 
onAccessibilityLongClick(View v)1379     private boolean onAccessibilityLongClick(View v) {
1380         final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
1381         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
1382         final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
1383         intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
1384         mContext.startActivityAsUser(intent, UserHandle.CURRENT);
1385         return true;
1386     }
1387 
updateAccessibilityServicesState(AccessibilityManager accessibilityManager)1388     void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
1389         boolean[] feedbackEnabled = new boolean[1];
1390         int a11yFlags = getA11yButtonState(feedbackEnabled);
1391 
1392         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
1393         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
1394         mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
1395 
1396         updateSystemUiStateFlags(a11yFlags);
1397     }
1398 
setAccessibilityFloatingMenuModeIfNeeded()1399     private void setAccessibilityFloatingMenuModeIfNeeded() {
1400         if (QuickStepContract.isGesturalMode(mNavBarMode)) {
1401             Settings.Secure.putIntForUser(mContentResolver,
1402                     Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
1403                     ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU, UserHandle.USER_CURRENT);
1404         }
1405     }
1406 
updateSystemUiStateFlags(int a11yFlags)1407     public void updateSystemUiStateFlags(int a11yFlags) {
1408         if (a11yFlags < 0) {
1409             a11yFlags = getA11yButtonState(null);
1410         }
1411         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
1412         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
1413 
1414         mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
1415                 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
1416                 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
1417                 .setFlag(SYSUI_STATE_IME_SHOWING,
1418                         (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
1419                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
1420                         allowSystemGestureIgnoringBarVisibility())
1421                 .commitUpdate(mDisplayId);
1422         registerAction(clickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON);
1423         registerAction(longClickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER);
1424     }
1425 
registerAction(boolean register, int actionId)1426     private void registerAction(boolean register, int actionId) {
1427         if (register) {
1428             mSystemActions.register(actionId);
1429         } else {
1430             mSystemActions.unregister(actionId);
1431         }
1432     }
1433 
1434     /**
1435      * Returns the system UI flags corresponding the the current accessibility button state
1436      *
1437      * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
1438      */
getA11yButtonState(@ullable boolean[] outFeedbackEnabled)1439     public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
1440         boolean feedbackEnabled = false;
1441         // AccessibilityManagerService resolves services for the current user since the local
1442         // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
1443         final List<AccessibilityServiceInfo> services =
1444                 mAccessibilityManager.getEnabledAccessibilityServiceList(
1445                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
1446         final List<String> a11yButtonTargets =
1447                 mAccessibilityManager.getAccessibilityShortcutTargets(
1448                         AccessibilityManager.ACCESSIBILITY_BUTTON);
1449         final int requestingServices = a11yButtonTargets.size();
1450         for (int i = services.size() - 1; i >= 0; --i) {
1451             AccessibilityServiceInfo info = services.get(i);
1452             if (info.feedbackType != 0 && info.feedbackType !=
1453                     AccessibilityServiceInfo.FEEDBACK_GENERIC) {
1454                 feedbackEnabled = true;
1455             }
1456         }
1457 
1458         if (outFeedbackEnabled != null) {
1459             outFeedbackEnabled[0] = feedbackEnabled;
1460         }
1461 
1462         // If accessibility button is floating menu mode, click and long click state should be
1463         // disabled.
1464         if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
1465                 == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
1466             return 0;
1467         }
1468 
1469         return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
1470                 | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
1471     }
1472 
updateAssistantEntrypoints()1473     private void updateAssistantEntrypoints() {
1474         mAssistantAvailable = mAssistManagerLazy.get()
1475                 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
1476         boolean longPressDefault = mContext.getResources().getBoolean(
1477                 com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
1478         mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
1479                 Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
1480                 mUserTracker.getUserId()) != 0;
1481         boolean gestureDefault = mContext.getResources().getBoolean(
1482                 com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
1483         mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
1484                 Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
1485                 mUserTracker.getUserId()) != 0;
1486         if (mOverviewProxyService.getProxy() != null) {
1487             try {
1488                 mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
1489                         && mAssistantTouchGestureEnabled
1490                         && QuickStepContract.isGesturalMode(mNavBarMode));
1491             } catch (RemoteException e) {
1492                 Log.w(TAG, "Unable to send assistant availability data to launcher");
1493             }
1494         }
1495         reconfigureHomeLongClick();
1496     }
1497 
1498     // ----- Methods that DisplayNavigationBarController talks to -----
1499 
1500     /** Applies auto dimming animation on navigation bar when touched. */
touchAutoDim()1501     public void touchAutoDim() {
1502         getBarTransitions().setAutoDim(false);
1503         mHandler.removeCallbacks(mAutoDim);
1504         int state = mStatusBarStateController.getState();
1505         if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) {
1506             mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS);
1507         }
1508     }
1509 
setLightBarController(LightBarController lightBarController)1510     public void setLightBarController(LightBarController lightBarController) {
1511         mLightBarController = lightBarController;
1512         if (mLightBarController != null) {
1513             mLightBarController.setNavigationBar(
1514                     mNavigationBarView.getLightTransitionsController());
1515         }
1516     }
1517 
1518     /** Sets {@link AutoHideController} to the navigation bar. */
setAutoHideController(AutoHideController autoHideController)1519     public void setAutoHideController(AutoHideController autoHideController) {
1520         mAutoHideController = autoHideController;
1521         if (mAutoHideController != null) {
1522             mAutoHideController.setNavigationBar(mAutoHideUiElement);
1523         }
1524         mNavigationBarView.setAutoHideController(autoHideController);
1525     }
1526 
isTransientShown()1527     private boolean isTransientShown() {
1528         return mTransientShown;
1529     }
1530 
checkBarModes()1531     private void checkBarModes() {
1532         // We only have status bar on default display now.
1533         if (mIsOnDefaultDisplay) {
1534             mStatusBarLazy.get().checkBarModes();
1535         } else {
1536             checkNavBarModes();
1537         }
1538     }
1539 
isNavBarWindowVisible()1540     public boolean isNavBarWindowVisible() {
1541         return mNavigationBarWindowState == WINDOW_STATE_SHOWING;
1542     }
1543 
allowSystemGestureIgnoringBarVisibility()1544     private boolean allowSystemGestureIgnoringBarVisibility() {
1545         return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
1546     }
1547 
1548     /**
1549      * Checks current navigation bar mode and make transitions.
1550      */
checkNavBarModes()1551     public void checkNavBarModes() {
1552         final boolean anim = mStatusBarLazy.get().isDeviceInteractive()
1553                 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
1554         mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
1555     }
1556 
1557     @Override
onNavigationModeChanged(int mode)1558     public void onNavigationModeChanged(int mode) {
1559         mNavBarMode = mode;
1560         if (!QuickStepContract.isGesturalMode(mode)) {
1561             // Reset the override alpha
1562             if (getBarTransitions() != null) {
1563                 getBarTransitions().setBackgroundOverrideAlpha(1f);
1564             }
1565         }
1566         updateScreenPinningGestures();
1567         setAccessibilityFloatingMenuModeIfNeeded();
1568 
1569         if (!canShowSecondaryHandle()) {
1570             resetSecondaryHandle();
1571         }
1572     }
1573 
1574     @Override
onAccessibilityButtonModeChanged(int mode)1575     public void onAccessibilityButtonModeChanged(int mode) {
1576         updateAccessibilityServicesState(mAccessibilityManager);
1577     }
1578 
disableAnimationsDuringHide(long delay)1579     public void disableAnimationsDuringHide(long delay) {
1580         mNavigationBarView.setLayoutTransitionsEnabled(false);
1581         mHandler.postDelayed(mEnableLayoutTransitions,
1582                 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
1583     }
1584 
1585     /**
1586      * Performs transitions on navigation bar.
1587      *
1588      * @param barMode transition bar mode.
1589      * @param animate shows animations if {@code true}.
1590      */
transitionTo(@ransitionMode int barMode, boolean animate)1591     public void transitionTo(@TransitionMode int barMode, boolean animate) {
1592         getBarTransitions().transitionTo(barMode, animate);
1593     }
1594 
getBarTransitions()1595     public NavigationBarTransitions getBarTransitions() {
1596         return mNavigationBarView.getBarTransitions();
1597     }
1598 
finishBarAnimations()1599     public void finishBarAnimations() {
1600         mNavigationBarView.getBarTransitions().finishAnimations();
1601     }
1602 
1603     private final AccessibilityServicesStateChangeListener mAccessibilityListener =
1604             this::updateAccessibilityServicesState;
1605 
canShowSecondaryHandle()1606     private boolean canShowSecondaryHandle() {
1607         return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
1608     }
1609 
1610     private final Consumer<Integer> mRotationWatcher = rotation -> {
1611         if (mNavigationBarView.needsReorient(rotation)) {
1612             repositionNavigationBar();
1613         }
1614     };
1615 
1616     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1617         @Override
1618         public void onReceive(Context context, Intent intent) {
1619             // TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is
1620             // async, but we've already cleared the fields. Just return early in this case.
1621             if (mNavigationBarView == null) {
1622                 return;
1623             }
1624             String action = intent.getAction();
1625             if (Intent.ACTION_SCREEN_OFF.equals(action)
1626                     || Intent.ACTION_SCREEN_ON.equals(action)) {
1627                 notifyNavigationBarScreenOn();
1628                 mNavigationBarView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action));
1629             }
1630             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
1631                 // The accessibility settings may be different for the new user
1632                 updateAccessibilityServicesState(mAccessibilityManager);
1633             }
1634         }
1635     };
1636 
1637     @VisibleForTesting
getNavigationIconHints()1638     int getNavigationIconHints() {
1639         return mNavigationIconHints;
1640     }
1641 }
1642