1/ Initialize assistant state change listeners once with the nav bar helper

- Consolidate the meaning of init/destroy into the
  registration/unregistration of the nav bar helper state callbacks.
  This allows us to track the case where we are replacing the bars
  to prevent churn in unregistering and reregistering shared callbacks
- Using the above, we can only register the assistant content observers
  when the first bar is registered, and keep it registered across
  bar replacements

Bug: 219035565
Test: atest NavBarHelperTests
Test: atest SystemUITests

Change-Id: I2444bb0b27086b72212ef05b8c975c19a05a35d5
This commit is contained in:
Winson Chung
2023-03-03 21:43:19 +00:00
parent 749de2e17a
commit 42ea688367
5 changed files with 110 additions and 51 deletions

View File

@@ -90,6 +90,7 @@ public final class NavBarHelper implements
AccessibilityButtonTargetsObserver.TargetsChangedListener,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
Dumpable, CommandQueue.Callbacks {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -98,7 +99,7 @@ public final class NavBarHelper implements
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
private final List<NavbarTaskbarStateUpdater> mStateListeners = new ArrayList<>();
private final Context mContext;
private final CommandQueue mCommandQueue;
private final ContentResolver mContentResolver;
@@ -107,13 +108,14 @@ public final class NavBarHelper implements
private boolean mAssistantTouchGestureEnabled;
private int mNavBarMode;
private int mA11yButtonState;
private boolean mTogglingNavbarTaskbar;
// Attributes used in NavBarHelper.CurrentSysuiState
private int mWindowStateDisplayId;
private @WindowVisibleState int mWindowState;
private final ContentObserver mAssistContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
// Listens for changes to the assistant
private final ContentObserver mAssistContentObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateAssistantAvailability();
@@ -147,18 +149,33 @@ public final class NavBarHelper implements
mKeyguardStateController = keyguardStateController;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(this);
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
mAccessibilityButtonModeObserver.addListener(this);
mAccessibilityButtonTargetsObserver.addListener(this);
mNavBarMode = navigationModeController.addListener(this);
mCommandQueue.addCallback(this);
overviewProxyService.addCallback(this);
dumpManager.registerDumpable(this);
}
public void init() {
/**
* Hints to the helper that bars are being replaced, which is a signal to potentially suppress
* normal setup/cleanup when no bars are present.
*/
public void setTogglingNavbarTaskbar(boolean togglingNavbarTaskbar) {
mTogglingNavbarTaskbar = togglingNavbarTaskbar;
}
/**
* Called when the first (non-replacing) bar is registered.
*/
private void setupOnFirstBar() {
// Setup accessibility listeners
mAccessibilityManager.addAccessibilityServicesStateChangeListener(this);
mAccessibilityButtonModeObserver.addListener(this);
mAccessibilityButtonTargetsObserver.addListener(this);
// Setup assistant listener
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
@@ -168,59 +185,76 @@ public final class NavBarHelper implements
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
updateAssistantAvailability();
updateA11yState();
mCommandQueue.addCallback(this);
}
public void destroy() {
mContentResolver.unregisterContentObserver(mAssistContentObserver);
mCommandQueue.removeCallback(this);
}
/**
* Called after the last (non-replacing) bar is unregistered.
*/
private void cleanupAfterLastBar() {
// Clean up accessibility listeners
mAccessibilityManager.removeAccessibilityServicesStateChangeListener(this);
mAccessibilityButtonModeObserver.removeListener(this);
mAccessibilityButtonTargetsObserver.removeListener(this);
// Clean up assistant listeners
mContentResolver.unregisterContentObserver(mAssistContentObserver);
}
/**
* Registers a listener for future updates to the shared navbar/taskbar state.
* @param listener Will immediately get callbacks based on current state
*/
public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
mA11yEventListeners.add(listener);
listener.updateAccessibilityServicesState();
listener.updateAssistantAvailable(mAssistantAvailable, mLongPressHomeEnabled);
mStateListeners.add(listener);
if (!mTogglingNavbarTaskbar && mStateListeners.size() == 1) {
setupOnFirstBar();
// Update the state once the first bar is registered
updateAssistantAvailability();
updateA11yState();
mCommandQueue.recomputeDisableFlags(mContext.getDisplayId(), false /* animate */);
} else {
listener.updateAccessibilityServicesState();
listener.updateAssistantAvailable(mAssistantAvailable, mLongPressHomeEnabled);
}
}
/**
* Removes a previously registered listener.
*/
public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
mA11yEventListeners.remove(listener);
mStateListeners.remove(listener);
if (!mTogglingNavbarTaskbar && mStateListeners.isEmpty()) {
cleanupAfterLastBar();
}
}
private void dispatchA11yEventUpdate() {
for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
for (NavbarTaskbarStateUpdater listener : mStateListeners) {
listener.updateAccessibilityServicesState();
}
}
private void dispatchAssistantEventUpdate(boolean assistantAvailable,
boolean longPressHomeEnabled) {
for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
for (NavbarTaskbarStateUpdater listener : mStateListeners) {
listener.updateAssistantAvailable(assistantAvailable, longPressHomeEnabled);
}
}
@Override
public void onAccessibilityServicesStateChanged(AccessibilityManager manager) {
dispatchA11yEventUpdate();
updateA11yState();
}
@Override
public void onAccessibilityButtonModeChanged(int mode) {
updateA11yState();
dispatchA11yEventUpdate();
}
@Override
public void onAccessibilityButtonTargetsChanged(String targets) {
updateA11yState();
dispatchA11yEventUpdate();
}
/**
@@ -262,6 +296,8 @@ public final class NavBarHelper implements
updateSystemAction(clickable, SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON);
updateSystemAction(longClickable, SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER);
}
dispatchA11yEventUpdate();
}
/**

View File

@@ -677,13 +677,14 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
// start firing, since the latter is source of truth
parseCurrentSysuiState();
mCommandQueue.addCallback(this);
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mNavBarHelper.init();
mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
HOME_BUTTON_LONG_PRESS_DURATION_MS,
/* defaultValue = */ 0
)).filter(duration -> duration != 0);
// This currently MUST be called after mHomeButtonLongPressDurationMs is initialized since
// the registration callbacks will trigger code that uses it
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mDeviceConfigProxy.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
@@ -709,7 +710,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mNavigationModeController.removeListener(mModeChangedListener);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.destroy();
mNotificationShadeDepthController.removeListener(mDepthListener);
mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
@@ -746,8 +746,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.getViewRootImpl().addSurfaceChangedCallback(mSurfaceChangedCallback);
notifyNavigationBarSurface();
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener);
mBackAnimation.ifPresent(mView::registerBackAnimation);
@@ -1487,6 +1485,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
void updateAccessibilityStateFlags() {
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
if (mView != null) {
int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;

View File

@@ -90,6 +90,7 @@ public class NavigationBarController implements
private final DisplayTracker mDisplayTracker;
private final DisplayManager mDisplayManager;
private final TaskbarDelegate mTaskbarDelegate;
private final NavBarHelper mNavBarHelper;
private int mNavMode;
@VisibleForTesting boolean mIsLargeScreen;
@@ -133,6 +134,7 @@ public class NavigationBarController implements
configurationController.addCallback(this);
mConfigChanges.applyNewConfig(mContext.getResources());
mNavMode = navigationModeController.addListener(this);
mNavBarHelper = navBarHelper;
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
navBarHelper, navigationModeController, sysUiFlagsContainer,
@@ -241,10 +243,15 @@ public class NavigationBarController implements
if (taskbarEnabled) {
Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
final int displayId = mContext.getDisplayId();
// Hint to NavBarHelper if we are replacing an existing bar to skip extra work
mNavBarHelper.setTogglingNavbarTaskbar(mNavigationBars.contains(displayId));
// Remove navigation bar when taskbar is showing
removeNavigationBar(mContext.getDisplayId());
mTaskbarDelegate.init(mContext.getDisplayId());
removeNavigationBar(displayId);
mTaskbarDelegate.init(displayId);
mNavBarHelper.setTogglingNavbarTaskbar(false);
Trace.endSection();
} else {
mTaskbarDelegate.destroy();
}

View File

@@ -239,7 +239,6 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mOverviewProxyService.addCallback(this);
onNavigationModeChanged(mNavigationModeController.addListener(this));
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.init();
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
@@ -264,7 +263,6 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mOverviewProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.destroy();
mEdgeBackGestureHandler.onNavBarDetached();
mScreenPinningNotify = null;
mWindowContext = null;

View File

@@ -128,20 +128,49 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void registerListenersInCtor() {
verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
verify(mCommandQueue, times(1)).addCallback(any());
}
@Test
public void registerAccessibilityContentObserver() {
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
verify(mAccessibilityButtonTargetObserver, times(1)).addListener(mNavBarHelper);
verify(mAccessibilityManager, times(1)).addAccessibilityServicesStateChangeListener(
mNavBarHelper);
}
@Test
public void unregisterAccessibilityContentObserver() {
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
verify(mAccessibilityButtonModeObserver, times(1)).removeListener(mNavBarHelper);
verify(mAccessibilityButtonTargetObserver, times(1)).removeListener(mNavBarHelper);
verify(mAccessibilityManager, times(1)).removeAccessibilityServicesStateChangeListener(
mNavBarHelper);
}
@Test
public void registerAssistantContentObserver() {
mNavBarHelper.init();
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
}
@Test
public void replacingBarsHint() {
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.setTogglingNavbarTaskbar(true);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.setTogglingNavbarTaskbar(false);
// Use any state in cleanup to verify it was not called
verify(mAccessibilityButtonModeObserver, times(0)).removeListener(mNavBarHelper);
}
@Test
public void callbacksFiredWhenRegistering() {
mNavBarHelper.init();
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
verify(mNavbarTaskbarStateUpdater, times(1))
.updateAccessibilityServicesState();
@@ -151,7 +180,6 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void assistantCallbacksFiredAfterConnecting() {
mNavBarHelper.init();
// 1st set of callbacks get called when registering
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
@@ -172,7 +200,6 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void a11yCallbacksFiredAfterModeChange() {
mNavBarHelper.init();
// 1st set of callbacks get called when registering
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
@@ -185,7 +212,6 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void assistantCallbacksFiredAfterNavModeChange() {
mNavBarHelper.init();
// 1st set of callbacks get called when registering
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
@@ -198,7 +224,6 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void removeListenerNoCallbacksFired() {
mNavBarHelper.init();
// 1st set of callbacks get called when registering
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
@@ -220,7 +245,7 @@ public class NavBarHelperTest extends SysuiTestCase {
when(mAccessibilityManager.getAccessibilityShortcutTargets(
AccessibilityManager.ACCESSIBILITY_BUTTON)).thenReturn(createFakeShortcutTargets());
mNavBarHelper.init();
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(
ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
@@ -230,13 +255,15 @@ public class NavBarHelperTest extends SysuiTestCase {
public void initAccessibilityStateWithFloatingMenuModeAndTargets_disableClickableState() {
when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
mNavBarHelper.init();
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(/* disable_clickable_state */ 0);
}
@Test
public void onA11yServicesStateChangedWithMultipleServices_a11yButtonClickableState() {
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
@@ -249,15 +276,8 @@ public class NavBarHelperTest extends SysuiTestCase {
ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
}
@Test
public void registerCommandQueueCallbacks() {
mNavBarHelper.init();
verify(mCommandQueue, times(1)).addCallback(any());
}
@Test
public void saveMostRecentSysuiState() {
mNavBarHelper.init();
mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, STATE_ID);
NavBarHelper.CurrentSysuiState state1 = mNavBarHelper.getCurrentSysuiState();
@@ -274,7 +294,6 @@ public class NavBarHelperTest extends SysuiTestCase {
@Test
public void ignoreNonNavbarSysuiState() {
mNavBarHelper.init();
mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, STATE_ID);
NavBarHelper.CurrentSysuiState state1 = mNavBarHelper.getCurrentSysuiState();