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.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
22 import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
23 import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
24 import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
25 import static android.view.Display.DEFAULT_DISPLAY;
26 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
27 import static android.view.WindowInsets.Type.ime;
28 
29 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
30 import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
31 import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
33 
34 import static com.google.common.truth.Truth.assertThat;
35 
36 import static org.junit.Assert.assertEquals;
37 import static org.junit.Assert.assertFalse;
38 import static org.junit.Assert.assertTrue;
39 import static org.mockito.ArgumentMatchers.any;
40 import static org.mockito.ArgumentMatchers.anyBoolean;
41 import static org.mockito.ArgumentMatchers.anyLong;
42 import static org.mockito.Matchers.eq;
43 import static org.mockito.Mockito.doNothing;
44 import static org.mockito.Mockito.doReturn;
45 import static org.mockito.Mockito.mock;
46 import static org.mockito.Mockito.never;
47 import static org.mockito.Mockito.spy;
48 import static org.mockito.Mockito.times;
49 import static org.mockito.Mockito.verify;
50 import static org.mockito.Mockito.when;
51 
52 import android.app.ActivityManager;
53 import android.content.Context;
54 import android.content.res.Resources;
55 import android.hardware.display.DisplayManagerGlobal;
56 import android.os.Handler;
57 import android.os.SystemClock;
58 import android.provider.DeviceConfig;
59 import android.telecom.TelecomManager;
60 import android.testing.TestableLooper;
61 import android.testing.TestableLooper.RunWithLooper;
62 import android.view.Display;
63 import android.view.DisplayInfo;
64 import android.view.IWindowManager;
65 import android.view.MotionEvent;
66 import android.view.View;
67 import android.view.ViewRootImpl;
68 import android.view.ViewTreeObserver;
69 import android.view.WindowInsets;
70 import android.view.WindowManager;
71 import android.view.WindowMetrics;
72 import android.view.accessibility.AccessibilityManager;
73 import android.view.inputmethod.InputMethodManager;
74 
75 import androidx.test.ext.junit.runners.AndroidJUnit4;
76 import androidx.test.filters.SmallTest;
77 
78 import com.android.internal.logging.MetricsLogger;
79 import com.android.internal.logging.UiEventLogger;
80 import com.android.systemui.SysuiTestCase;
81 import com.android.systemui.SysuiTestableContext;
82 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
83 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
84 import com.android.systemui.accessibility.SystemActions;
85 import com.android.systemui.assist.AssistManager;
86 import com.android.systemui.dump.DumpManager;
87 import com.android.systemui.keyguard.WakefulnessLifecycle;
88 import com.android.systemui.model.SysUiState;
89 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
90 import com.android.systemui.navigationbar.buttons.DeadZone;
91 import com.android.systemui.navigationbar.buttons.KeyButtonView;
92 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
93 import com.android.systemui.plugins.statusbar.StatusBarStateController;
94 import com.android.systemui.recents.OverviewProxyService;
95 import com.android.systemui.recents.Recents;
96 import com.android.systemui.settings.DisplayTracker;
97 import com.android.systemui.settings.FakeDisplayTracker;
98 import com.android.systemui.settings.UserContextProvider;
99 import com.android.systemui.settings.UserTracker;
100 import com.android.systemui.shade.NotificationShadeWindowView;
101 import com.android.systemui.shade.ShadeViewController;
102 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
103 import com.android.systemui.shared.rotation.RotationButtonController;
104 import com.android.systemui.shared.system.TaskStackChangeListeners;
105 import com.android.systemui.statusbar.CommandQueue;
106 import com.android.systemui.statusbar.NotificationRemoteInputManager;
107 import com.android.systemui.statusbar.NotificationShadeDepthController;
108 import com.android.systemui.statusbar.NotificationShadeWindowController;
109 import com.android.systemui.statusbar.phone.AutoHideController;
110 import com.android.systemui.statusbar.phone.CentralSurfaces;
111 import com.android.systemui.statusbar.phone.LightBarController;
112 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
113 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
114 import com.android.systemui.statusbar.policy.ConfigurationController;
115 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
116 import com.android.systemui.statusbar.policy.KeyguardStateController;
117 import com.android.systemui.util.DeviceConfigProxyFake;
118 import com.android.systemui.util.concurrency.FakeExecutor;
119 import com.android.systemui.util.time.FakeSystemClock;
120 import com.android.systemui.utils.leaks.LeakCheckedTest;
121 import com.android.wm.shell.back.BackAnimation;
122 import com.android.wm.shell.pip.Pip;
123 
124 import org.junit.Before;
125 import org.junit.Rule;
126 import org.junit.Test;
127 import org.junit.runner.RunWith;
128 import org.mockito.ArgumentCaptor;
129 import org.mockito.Mock;
130 import org.mockito.MockitoAnnotations;
131 
132 import java.util.Optional;
133 import java.util.concurrent.Executor;
134 
135 @RunWith(AndroidJUnit4.class)
136 @RunWithLooper(setAsMainLooper = true)
137 @SmallTest
138 public class NavigationBarTest extends SysuiTestCase {
139     private static final int EXTERNAL_DISPLAY_ID = 2;
140 
141     private NavigationBar mNavigationBar;
142     private NavigationBar mExternalDisplayNavigationBar;
143 
144     private SysuiTestableContext mSysuiTestableContextExternal;
145     @Mock
146     NavigationBarView mNavigationBarView;
147     @Mock
148     NavigationBarFrame mNavigationBarFrame;
149     @Mock
150     ButtonDispatcher mHomeButton;
151     @Mock
152     KeyButtonView mHomeButtonView;
153     @Mock
154     ButtonDispatcher mRecentsButton;
155     @Mock
156     ButtonDispatcher mAccessibilityButton;
157     @Mock
158     ButtonDispatcher mImeSwitchButton;
159     @Mock
160     ButtonDispatcher mBackButton;
161     @Mock
162     NavigationBarTransitions mNavigationBarTransitions;
163     @Mock
164     RotationButtonController mRotationButtonController;
165     @Mock
166     LightBarTransitionsController mLightBarTransitionsController;
167     @Mock
168     private SystemActions mSystemActions;
169     @Mock
170     private OverviewProxyService mOverviewProxyService;
171     @Mock
172     private StatusBarStateController mStatusBarStateController;
173     @Mock
174     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
175     @Mock
176     private NavigationModeController mNavigationModeController;
177     @Mock
178     private CommandQueue mCommandQueue;
179     private SysUiState mMockSysUiState;
180     @Mock
181     private Handler mHandler;
182     @Mock
183     private UserTracker mUserTracker;
184     @Mock
185     private UiEventLogger mUiEventLogger;
186     @Mock
187     private NavBarButtonClickLogger mNavBarButtonClickLogger;
188     @Mock
189     private NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger;
190     @Mock
191     private ViewTreeObserver mViewTreeObserver;
192     NavBarHelper mNavBarHelper;
193     @Mock
194     private LightBarController mLightBarController;
195     @Mock
196     private LightBarController.Factory mLightBarcontrollerFactory;
197     @Mock
198     private AutoHideController mAutoHideController;
199     @Mock
200     private AutoHideController.Factory mAutoHideControllerFactory;
201     @Mock
202     private WindowManager mWindowManager;
203     @Mock
204     private TelecomManager mTelecomManager;
205     @Mock
206     private InputMethodManager mInputMethodManager;
207     @Mock
208     private AssistManager mAssistManager;
209     @Mock
210     private DeadZone mDeadZone;
211     @Mock
212     private CentralSurfaces mCentralSurfaces;
213     @Mock
214     private KeyguardStateController mKeyguardStateController;
215     @Mock
216     private UserContextProvider mUserContextProvider;
217     @Mock
218     private WakefulnessLifecycle mWakefulnessLifecycle;
219     @Mock
220     private Resources mResources;
221     @Mock
222     private ViewRootImpl mViewRootImpl;
223     @Mock
224     private EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
225     @Mock
226     private EdgeBackGestureHandler mEdgeBackGestureHandler;
227     @Mock
228     private NotificationShadeWindowController mNotificationShadeWindowController;
229     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
230     private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake();
231     private TaskStackChangeListeners mTaskStackChangeListeners =
232             TaskStackChangeListeners.getTestInstance();
233 
234     @Rule
235     public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
236 
237     private final Executor mSynchronousExecutor = runnable -> runnable.run();
238 
239     @Before
setup()240     public void setup() throws Exception {
241         MockitoAnnotations.initMocks(this);
242 
243         when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
244         when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
245         when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton);
246         when(mNavigationBarView.getRecentsButton()).thenReturn(mRecentsButton);
247         when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton);
248         when(mNavigationBarView.getImeSwitchButton()).thenReturn(mImeSwitchButton);
249         when(mNavigationBarView.getBackButton()).thenReturn(mBackButton);
250         when(mNavigationBarView.getRotationButtonController())
251                 .thenReturn(mRotationButtonController);
252         when(mNavigationBarTransitions.getLightTransitionsController())
253                 .thenReturn(mLightBarTransitionsController);
254         when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
255         when(mNavigationBarView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
256         when(mUserContextProvider.createCurrentUserContext(any(Context.class)))
257                 .thenReturn(mContext);
258         when(mNavigationBarView.getResources()).thenReturn(mResources);
259         when(mNavigationBarView.getViewRootImpl()).thenReturn(mViewRootImpl);
260         when(mEdgeBackGestureHandlerFactory.create(any())).thenReturn(mEdgeBackGestureHandler);
261         setupSysuiDependency();
262         // This class inflates views that call Dependency.get, thus these injections are still
263         // necessary.
264         mDependency.injectTestDependency(AssistManager.class, mAssistManager);
265         mDependency.injectMockDependency(KeyguardStateController.class);
266         mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
267         mDependency.injectMockDependency(NavigationBarController.class);
268         mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
269         mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
270         TestableLooper.get(this).runWithLooper(() -> {
271             mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
272                     mock(AccessibilityButtonModeObserver.class),
273                     mock(AccessibilityButtonTargetsObserver.class),
274                     mSystemActions, mOverviewProxyService,
275                     () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
276                     mKeyguardStateController, mock(NavigationModeController.class),
277                     mEdgeBackGestureHandlerFactory, mock(IWindowManager.class),
278                     mock(UserTracker.class), mock(DisplayTracker.class),
279                     mNotificationShadeWindowController, mock(ConfigurationController.class),
280                     mock(DumpManager.class), mock(CommandQueue.class), mSynchronousExecutor));
281             mNavigationBar = createNavBar(mContext);
282             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
283         });
284     }
285 
setupSysuiDependency()286     private void setupSysuiDependency() {
287         Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
288                 new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
289         mSysuiTestableContextExternal = (SysuiTestableContext) getContext().createDisplayContext(
290                 display);
291 
292         Display defaultDisplay = mContext.getDisplay();
293         when(mWindowManager.getDefaultDisplay()).thenReturn(defaultDisplay);
294         WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
295                 .getMaximumWindowMetrics();
296         when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
297         WindowMetrics currentWindowMetrics = mContext.getSystemService(WindowManager.class)
298                 .getCurrentWindowMetrics();
299         when(mWindowManager.getCurrentWindowMetrics()).thenReturn(currentWindowMetrics);
300         doNothing().when(mWindowManager).addView(any(), any());
301         doNothing().when(mWindowManager).removeViewImmediate(any());
302         mMockSysUiState = mock(SysUiState.class);
303         when(mMockSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mMockSysUiState);
304 
305         mContext.addMockSystemService(WindowManager.class, mWindowManager);
306         mSysuiTestableContextExternal.addMockSystemService(WindowManager.class, mWindowManager);
307     }
308 
309     @Test
testHomeLongPress()310     public void testHomeLongPress() {
311         when(mAssistManager.shouldOverrideAssist(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS))
312                 .thenReturn(false);
313 
314         mNavigationBar.init();
315         mNavigationBar.onViewAttached();
316         mNavigationBar.onHomeLongClick(mHomeButtonView);
317 
318         verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
319         verify(mAssistManager).startAssist(any());
320     }
321 
322     @Test
testHomeLongPressOverride()323     public void testHomeLongPressOverride() {
324         when(mAssistManager.shouldOverrideAssist(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS))
325                 .thenReturn(true);
326 
327         mNavigationBar.init();
328         mNavigationBar.onViewAttached();
329         mNavigationBar.onHomeLongClick(mHomeButtonView);
330 
331         verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
332 
333         ArgumentCaptor<Runnable> onRippleInvisibleRunnableCaptor = ArgumentCaptor.forClass(
334                 Runnable.class);
335         // startAssist is not called initially
336         verify(mAssistManager, never()).startAssist(any());
337         // but a Runnable is added for when the ripple is invisible
338         verify(mHomeButtonView).setOnRippleInvisibleRunnable(
339                 onRippleInvisibleRunnableCaptor.capture());
340         // and when that runs, startAssist is called
341         onRippleInvisibleRunnableCaptor.getValue().run();
342         verify(mAssistManager).startAssist(any());
343     }
344 
345     @Test
testHomeLongPressWithCustomDuration()346     public void testHomeLongPressWithCustomDuration() throws Exception {
347         mDeviceConfigProxyFake.setProperty(
348                 DeviceConfig.NAMESPACE_SYSTEMUI,
349                 HOME_BUTTON_LONG_PRESS_DURATION_MS,
350                 "100",
351                 false);
352         when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
353         mNavigationBar.init();
354         mNavigationBar.onViewAttached();
355 
356         mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
357                 /*downTime=*/SystemClock.uptimeMillis(),
358                 /*eventTime=*/SystemClock.uptimeMillis(),
359                 /*action=*/MotionEvent.ACTION_DOWN,
360                 0, 0, 0
361         ));
362         verify(mHandler, times(1)).postDelayed(any(), eq(100L));
363 
364         mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
365                 /*downTime=*/SystemClock.uptimeMillis(),
366                 /*eventTime=*/SystemClock.uptimeMillis(),
367                 /*action=*/MotionEvent.ACTION_UP,
368                 0, 0, 0
369         ));
370 
371         verify(mHandler, times(1)).removeCallbacks(any());
372     }
373 
374     @Test
onHomeTouch_isRinging_keyguardShowing_touchBlocked()375     public void onHomeTouch_isRinging_keyguardShowing_touchBlocked() {
376         when(mTelecomManager.isRinging()).thenReturn(true);
377         when(mKeyguardStateController.isShowing()).thenReturn(true);
378 
379         boolean result = mNavigationBar.onHomeTouch(
380                 mNavigationBar.getView(),
381                     MotionEvent.obtain(
382                     /*downTime=*/SystemClock.uptimeMillis(),
383                     /*eventTime=*/SystemClock.uptimeMillis(),
384                     /*action=*/MotionEvent.ACTION_DOWN,
385                     0, 0, 0));
386 
387         assertThat(result).isTrue();
388 
389         // Verify subsequent touches are also blocked
390         boolean nextTouchEvent = mNavigationBar.onHomeTouch(
391                 mNavigationBar.getView(),
392                 MotionEvent.obtain(
393                         /*downTime=*/SystemClock.uptimeMillis(),
394                         /*eventTime=*/SystemClock.uptimeMillis(),
395                         /*action=*/MotionEvent.ACTION_MOVE,
396                         0, 0, 0));
397         assertThat(nextTouchEvent).isTrue();
398     }
399 
400     @Test
onHomeTouch_isRinging_keyguardNotShowing_touchNotBlocked()401     public void onHomeTouch_isRinging_keyguardNotShowing_touchNotBlocked() {
402         when(mTelecomManager.isRinging()).thenReturn(true);
403         when(mKeyguardStateController.isShowing()).thenReturn(false);
404 
405         boolean result = mNavigationBar.onHomeTouch(
406                 mNavigationBar.getView(),
407                 MotionEvent.obtain(
408                         /*downTime=*/SystemClock.uptimeMillis(),
409                         /*eventTime=*/SystemClock.uptimeMillis(),
410                         /*action=*/MotionEvent.ACTION_DOWN,
411                         0, 0, 0));
412 
413         assertThat(result).isFalse();
414 
415         // Verify subsequent touches are also not blocked
416         boolean nextTouchEvent = mNavigationBar.onHomeTouch(
417                 mNavigationBar.getView(),
418                 MotionEvent.obtain(
419                         /*downTime=*/SystemClock.uptimeMillis(),
420                         /*eventTime=*/SystemClock.uptimeMillis(),
421                         /*action=*/MotionEvent.ACTION_MOVE,
422                         0, 0, 0));
423         assertThat(nextTouchEvent).isFalse();
424     }
425 
426     @Test
testRegisteredWithUserTracker()427     public void testRegisteredWithUserTracker() {
428         mNavigationBar.init();
429         mNavigationBar.onViewAttached();
430         verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
431     }
432 
433     @Test
testSetImeWindowStatusWhenImeSwitchOnDisplay()434     public void testSetImeWindowStatusWhenImeSwitchOnDisplay() {
435         // Create default & external NavBar fragment.
436         NavigationBar defaultNavBar = mNavigationBar;
437         NavigationBar externalNavBar = mExternalDisplayNavigationBar;
438         NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
439         WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
440         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
441         doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
442         doNothing().when(defaultNavBar).checkNavBarModes();
443         doNothing().when(externalNavBar).checkNavBarModes();
444         defaultNavBar.init();
445         externalNavBar.init();
446 
447         defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
448                 BACK_DISPOSITION_DEFAULT, true);
449 
450         // Verify IME window state will be updated in default NavBar & external NavBar state reset.
451         assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
452                         | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
453                 defaultNavBar.getNavigationIconHints());
454         assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
455         assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
456         assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
457                 != 0);
458 
459         externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
460                 BACK_DISPOSITION_DEFAULT, true);
461         defaultNavBar.setImeWindowStatus(
462                 DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
463         // Verify IME window state will be updated in external NavBar & default NavBar state reset.
464         assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
465                         | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
466                 externalNavBar.getNavigationIconHints());
467         assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
468         assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
469         assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
470                 != 0);
471     }
472 
473     @Test
testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange()474     public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() {
475         NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
476         doReturn(mockShadeWindowView).when(mNotificationShadeWindowController).getWindowRootView();
477         doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
478         doNothing().when(mNavigationBar).checkNavBarModes();
479         mNavigationBar.init();
480         WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
481         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
482 
483         // Verify navbar altered back icon when an app is showing IME
484         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
485                 BACK_DISPOSITION_DEFAULT, true);
486         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
487         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
488         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
489                 != 0);
490 
491         // Verify navbar didn't alter and showing back icon when the keyguard is showing without
492         // requesting IME insets visible.
493         doReturn(true).when(mKeyguardStateController).isShowing();
494         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
495                 BACK_DISPOSITION_DEFAULT, true);
496         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
497         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
498         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
499                 != 0);
500 
501         // Verify navbar altered and showing back icon when the keyguard is showing and
502         // requesting IME insets visible.
503         windowInsets = new WindowInsets.Builder().setVisible(ime(), true).build();
504         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
505         mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
506                 BACK_DISPOSITION_DEFAULT, true);
507         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
508         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
509         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
510                 != 0);
511     }
512 
513     @Test
testA11yEventAfterDetach()514     public void testA11yEventAfterDetach() {
515         mNavigationBar.init();
516         mNavigationBar.onViewAttached();
517         verify(mNavBarHelper).registerNavTaskStateUpdater(any(
518                 NavBarHelper.NavbarTaskbarStateUpdater.class));
519         mNavigationBar.onViewDetached();
520         verify(mNavBarHelper).removeNavTaskStateUpdater(any(
521                 NavBarHelper.NavbarTaskbarStateUpdater.class));
522 
523         // Should be safe even though the internal view is now null.
524         mNavigationBar.updateAccessibilityStateFlags();
525     }
526 
527     @Test
testCreateView_initiallyVisible_viewIsVisible()528     public void testCreateView_initiallyVisible_viewIsVisible() {
529         when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
530         mNavigationBar.init();
531         mNavigationBar.onViewAttached();
532 
533         verify(mNavigationBarView).setVisibility(View.VISIBLE);
534     }
535 
536     @Test
testCreateView_initiallyNotVisible_viewIsNotVisible()537     public void testCreateView_initiallyNotVisible_viewIsNotVisible() {
538         when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(false);
539         mNavigationBar.init();
540         mNavigationBar.onViewAttached();
541 
542         verify(mNavigationBarView).setVisibility(View.INVISIBLE);
543     }
544 
545     @Test
testOnInit_readCurrentSysuiState()546     public void testOnInit_readCurrentSysuiState() {
547         mNavigationBar.init();
548         verify(mNavBarHelper, times(1)).getCurrentSysuiState();
549     }
550 
551     @Test
testScreenPinningEnabled_updatesSysuiState()552     public void testScreenPinningEnabled_updatesSysuiState() {
553         mNavigationBar.init();
554         mTaskStackChangeListeners.getListenerImpl().onLockTaskModeChanged(
555                 ActivityManager.LOCK_TASK_MODE_PINNED);
556         verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_SCREEN_PINNING), eq(true));
557     }
558 
createNavBar(Context context)559     private NavigationBar createNavBar(Context context) {
560         DeviceProvisionedController deviceProvisionedController =
561                 mock(DeviceProvisionedController.class);
562         when(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
563         return spy(new NavigationBar(
564                 mNavigationBarView,
565                 mNavigationBarFrame,
566                 null,
567                 context,
568                 mWindowManager,
569                 () -> mAssistManager,
570                 mock(AccessibilityManager.class),
571                 deviceProvisionedController,
572                 new MetricsLogger(),
573                 mOverviewProxyService,
574                 mNavigationModeController,
575                 mStatusBarStateController,
576                 mStatusBarKeyguardViewManager,
577                 mMockSysUiState,
578                 mUserTracker,
579                 mCommandQueue,
580                 Optional.of(mock(Pip.class)),
581                 Optional.of(mock(Recents.class)),
582                 () -> Optional.of(mCentralSurfaces),
583                 mKeyguardStateController,
584                 mock(ShadeViewController.class),
585                 mock(PanelExpansionInteractor.class),
586                 mock(NotificationRemoteInputManager.class),
587                 mock(NotificationShadeDepthController.class),
588                 mHandler,
589                 mFakeExecutor,
590                 mFakeExecutor,
591                 mUiEventLogger,
592                 mNavBarHelper,
593                 mLightBarController,
594                 mLightBarcontrollerFactory,
595                 mAutoHideController,
596                 mAutoHideControllerFactory,
597                 Optional.of(mTelecomManager),
598                 mInputMethodManager,
599                 mDeadZone,
600                 mDeviceConfigProxyFake,
601                 mNavigationBarTransitions,
602                 Optional.of(mock(BackAnimation.class)),
603                 mUserContextProvider,
604                 mWakefulnessLifecycle,
605                 mTaskStackChangeListeners,
606                 new FakeDisplayTracker(mContext),
607                 mNavBarButtonClickLogger,
608                 mNavbarOrientationTrackingLogger));
609     }
610 
processAllMessages()611     private void processAllMessages() {
612         TestableLooper.get(this).processAllMessages();
613     }
614 }
615