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