• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.WINDOW_NAVIGATION_BAR;
20 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
21 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
22 
23 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.anyBoolean;
30 import static org.mockito.ArgumentMatchers.anyInt;
31 import static org.mockito.Mockito.doAnswer;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.when;
36 
37 import android.content.ComponentName;
38 import android.content.res.Configuration;
39 import android.view.IWindowManager;
40 import android.view.accessibility.AccessibilityManager;
41 
42 import androidx.test.filters.SmallTest;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
46 import com.android.systemui.SysuiTestCase;
47 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
48 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
49 import com.android.systemui.accessibility.SystemActions;
50 import com.android.systemui.assist.AssistManager;
51 import com.android.systemui.dump.DumpManager;
52 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
53 import com.android.systemui.recents.OverviewProxyService;
54 import com.android.systemui.settings.DisplayTracker;
55 import com.android.systemui.settings.UserTracker;
56 import com.android.systemui.statusbar.CommandQueue;
57 import com.android.systemui.statusbar.NotificationShadeWindowController;
58 import com.android.systemui.statusbar.phone.CentralSurfaces;
59 import com.android.systemui.statusbar.policy.ConfigurationController;
60 import com.android.systemui.statusbar.policy.FakeConfigurationController;
61 import com.android.systemui.statusbar.policy.KeyguardStateController;
62 
63 import dagger.Lazy;
64 
65 import org.junit.Before;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.Mock;
69 import org.mockito.MockitoAnnotations;
70 
71 import java.util.ArrayList;
72 import java.util.List;
73 import java.util.Optional;
74 import java.util.concurrent.Executor;
75 
76 /**
77  * Tests for {@link NavBarHelper}.
78  */
79 @RunWith(AndroidJUnit4.class)
80 @SmallTest
81 public class NavBarHelperTest extends SysuiTestCase {
82 
83     private static final int DISPLAY_ID = 0;
84     private static final int WINDOW = WINDOW_NAVIGATION_BAR;
85     private static final int STATE_ID = 0;
86 
87     @Mock
88     AccessibilityManager mAccessibilityManager;
89     @Mock
90     AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
91     @Mock
92     AccessibilityButtonTargetsObserver mAccessibilityButtonTargetObserver;
93     @Mock
94     SystemActions mSystemActions;
95     @Mock
96     OverviewProxyService mOverviewProxyService;
97     @Mock
98     Lazy<AssistManager> mAssistManagerLazy;
99     @Mock
100     AssistManager mAssistManager;
101     @Mock
102     NavigationModeController mNavigationModeController;
103     @Mock
104     UserTracker mUserTracker;
105     @Mock
106     ComponentName mAssistantComponent;
107     @Mock
108     DumpManager mDumpManager;
109     @Mock
110     NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
111     @Mock
112     CommandQueue mCommandQueue;
113     @Mock
114     IWindowManager mWm;
115     @Mock
116     DisplayTracker mDisplayTracker;
117     @Mock
118     EdgeBackGestureHandler mEdgeBackGestureHandler;
119     @Mock
120     EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
121     @Mock
122     NotificationShadeWindowController mNotificationShadeWindowController;
123     ConfigurationController mConfigurationController = new FakeConfigurationController();
124 
125     private AccessibilityManager.AccessibilityServicesStateChangeListener
126             mAccessibilityServicesStateChangeListener;
127 
128     private static final long ACCESSIBILITY_BUTTON_CLICKABLE_STATE =
129             SYSUI_STATE_A11Y_BUTTON_CLICKABLE | SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
130     private NavBarHelper mNavBarHelper;
131 
132     private final Executor mSynchronousExecutor = runnable -> runnable.run();
133 
134     @Before
setup()135     public void setup() {
136         MockitoAnnotations.initMocks(this);
137         when(mAssistManagerLazy.get()).thenReturn(mAssistManager);
138         when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
139         when(mUserTracker.getUserId()).thenReturn(1);
140         when(mDisplayTracker.getDefaultDisplayId()).thenReturn(0);
141         when(mEdgeBackGestureHandlerFactory.create(any())).thenReturn(mEdgeBackGestureHandler);
142 
143         doAnswer((invocation) -> mAccessibilityServicesStateChangeListener =
144                 invocation.getArgument(0)).when(
145                 mAccessibilityManager).addAccessibilityServicesStateChangeListener(any());
146         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
147                 mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
148                 mSystemActions, mOverviewProxyService, mAssistManagerLazy,
149                 () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
150                 mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
151                 mDisplayTracker, mNotificationShadeWindowController, mConfigurationController,
152                 mDumpManager, mCommandQueue, mSynchronousExecutor);
153     }
154 
155     @Test
registerListenersInCtor()156     public void registerListenersInCtor() {
157         verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
158         verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
159         verify(mCommandQueue, times(1)).addCallback(any());
160     }
161 
162     @Test
testSetupBarsRegistersListeners()163     public void testSetupBarsRegistersListeners() throws Exception {
164         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
165         verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
166         verify(mAccessibilityButtonTargetObserver, times(1)).addListener(mNavBarHelper);
167         verify(mAccessibilityManager, times(1)).addAccessibilityServicesStateChangeListener(
168                 mNavBarHelper);
169         verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
170         verify(mWm, times(1)).watchRotation(any(), anyInt());
171         verify(mWm, times(1)).registerWallpaperVisibilityListener(any(), anyInt());
172         verify(mEdgeBackGestureHandler, times(1)).onNavBarAttached();
173     }
174 
175     @Test
testCleanupBarsUnregistersListeners()176     public void testCleanupBarsUnregistersListeners() throws Exception {
177         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
178         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
179         verify(mAccessibilityButtonModeObserver, times(1)).removeListener(mNavBarHelper);
180         verify(mAccessibilityButtonTargetObserver, times(1)).removeListener(mNavBarHelper);
181         verify(mAccessibilityManager, times(1)).removeAccessibilityServicesStateChangeListener(
182                 mNavBarHelper);
183         verify(mWm, times(1)).removeRotationWatcher(any());
184         verify(mWm, times(1)).unregisterWallpaperVisibilityListener(any(), anyInt());
185         verify(mEdgeBackGestureHandler, times(1)).onNavBarDetached();
186     }
187 
188     @Test
replacingBarsHint()189     public void replacingBarsHint() {
190         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
191         mNavBarHelper.setTogglingNavbarTaskbar(true);
192         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
193         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
194         mNavBarHelper.setTogglingNavbarTaskbar(false);
195         // Use any state in cleanup to verify it was not called
196         verify(mAccessibilityButtonModeObserver, times(0)).removeListener(mNavBarHelper);
197     }
198 
199     @Test
callbacksFiredWhenRegistering()200     public void callbacksFiredWhenRegistering() {
201         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
202         verify(mNavbarTaskbarStateUpdater, times(1))
203                 .updateAccessibilityServicesState();
204         verify(mNavbarTaskbarStateUpdater, times(1))
205                 .updateAssistantAvailable(anyBoolean(), anyBoolean());
206         verify(mNavbarTaskbarStateUpdater, times(1))
207                 .updateRotationWatcherState(anyInt());
208         verify(mNavbarTaskbarStateUpdater, times(1))
209                 .updateWallpaperVisibility(anyBoolean(), anyInt());
210     }
211 
212     @Test
assistantCallbacksFiredAfterConnecting()213     public void assistantCallbacksFiredAfterConnecting() {
214         // 1st set of callbacks get called when registering
215         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
216 
217         mNavBarHelper.onConnectionChanged(false);
218         // assert no more callbacks fired
219         verify(mNavbarTaskbarStateUpdater, times(1))
220                 .updateAccessibilityServicesState();
221         verify(mNavbarTaskbarStateUpdater, times(1))
222                 .updateAssistantAvailable(anyBoolean(), anyBoolean());
223 
224         mNavBarHelper.onConnectionChanged(true);
225         // assert no more callbacks fired
226         verify(mNavbarTaskbarStateUpdater, times(1))
227                 .updateAccessibilityServicesState();
228         verify(mNavbarTaskbarStateUpdater, times(2))
229                 .updateAssistantAvailable(anyBoolean(), anyBoolean());
230     }
231 
232     @Test
a11yCallbacksFiredAfterModeChange()233     public void a11yCallbacksFiredAfterModeChange() {
234         // 1st set of callbacks get called when registering
235         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
236 
237         mNavBarHelper.onAccessibilityButtonModeChanged(0);
238         verify(mNavbarTaskbarStateUpdater, times(2))
239                 .updateAccessibilityServicesState();
240         verify(mNavbarTaskbarStateUpdater, times(1))
241                 .updateAssistantAvailable(anyBoolean(), anyBoolean());
242     }
243 
244     @Test
assistantCallbacksFiredAfterNavModeChange()245     public void assistantCallbacksFiredAfterNavModeChange() {
246         // 1st set of callbacks get called when registering
247         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
248 
249         mNavBarHelper.onNavigationModeChanged(0);
250         verify(mNavbarTaskbarStateUpdater, times(1))
251                 .updateAccessibilityServicesState();
252         verify(mNavbarTaskbarStateUpdater, times(2))
253                 .updateAssistantAvailable(anyBoolean(), anyBoolean());
254     }
255 
256     @Test
removeListenerNoCallbacksFired()257     public void removeListenerNoCallbacksFired() {
258         // 1st set of callbacks get called when registering
259         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
260 
261         // Remove listener
262         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
263 
264         // Would have fired 2nd callback if not removed
265         mNavBarHelper.onAccessibilityButtonModeChanged(0);
266 
267         // assert no more callbacks fired
268         verify(mNavbarTaskbarStateUpdater, times(1))
269                 .updateAccessibilityServicesState();
270         verify(mNavbarTaskbarStateUpdater, times(1))
271                 .updateAssistantAvailable(anyBoolean(), anyBoolean());
272     }
273 
274     @Test
initNavBarHelper_buttonModeNavBar_a11yButtonClickableState()275     public void initNavBarHelper_buttonModeNavBar_a11yButtonClickableState() {
276         when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
277                 .thenReturn(createFakeShortcutTargets());
278 
279         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
280 
281         assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(
282                 ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
283     }
284 
285     @Test
initAccessibilityStateWithFloatingMenuModeAndTargets_disableClickableState()286     public void initAccessibilityStateWithFloatingMenuModeAndTargets_disableClickableState() {
287         when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
288                 ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
289 
290         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
291 
292         assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(/* disable_clickable_state */ 0);
293     }
294 
295     @Test
onA11yServicesStateChangedWithMultipleServices_a11yButtonClickableState()296     public void onA11yServicesStateChangedWithMultipleServices_a11yButtonClickableState() {
297         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
298         when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
299                 ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
300 
301         when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
302                 .thenReturn(createFakeShortcutTargets());
303         mAccessibilityServicesStateChangeListener.onAccessibilityServicesStateChanged(
304                 mAccessibilityManager);
305 
306         assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(
307                 ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
308     }
309 
310     @Test
saveMostRecentSysuiState()311     public void saveMostRecentSysuiState() {
312         mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, STATE_ID);
313         NavBarHelper.CurrentSysuiState state1 = mNavBarHelper.getCurrentSysuiState();
314 
315         // Update window state
316         int newState = STATE_ID + 1;
317         mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, newState);
318         NavBarHelper.CurrentSysuiState state2 = mNavBarHelper.getCurrentSysuiState();
319 
320         // Ensure we get most recent state back
321         assertThat(state1.mWindowState).isNotEqualTo(state2.mWindowState);
322         assertThat(state1.mWindowStateDisplayId).isEqualTo(state2.mWindowStateDisplayId);
323         assertThat(state2.mWindowState).isEqualTo(newState);
324     }
325 
326     @Test
ignoreNonNavbarSysuiState()327     public void ignoreNonNavbarSysuiState() {
328         mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, STATE_ID);
329         NavBarHelper.CurrentSysuiState state1 = mNavBarHelper.getCurrentSysuiState();
330 
331         // Update window state for other window type
332         int newState = STATE_ID + 1;
333         mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW + 1, newState);
334         NavBarHelper.CurrentSysuiState state2 = mNavBarHelper.getCurrentSysuiState();
335 
336         // Ensure we get first state back
337         assertThat(state2.mWindowState).isEqualTo(state1.mWindowState);
338         assertThat(state2.mWindowState).isNotEqualTo(newState);
339     }
340 
341     @Test
configUpdatePropagatesToEdgeBackGestureHandler()342     public void configUpdatePropagatesToEdgeBackGestureHandler() {
343         mConfigurationController.onConfigurationChanged(Configuration.EMPTY);
344         verify(mEdgeBackGestureHandler, times(1)).onConfigurationChanged(any());
345     }
346 
createFakeShortcutTargets()347     private List<String> createFakeShortcutTargets() {
348         return new ArrayList<>(List.of("a", "b", "c", "d"));
349     }
350 }
351