From a9bf9c83128908b18a5fe4d71f13fb3c7e07def7 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Mon, 7 Jun 2021 14:07:47 -0700 Subject: [PATCH] Extract a11y from NavigationBar to share with TaskbarDelegate When taskbar is showing, navigation bar isn't created which means all of the a11y registers are never registered. NavigationBarA11yHelper maintains existing a11y flag logic from NavigationBar but any class can register with it to get updates. Bug: 180046394 Test: A11y works as expected on 3 button phone Right events getting pass through to launcher when on larger device Change-Id: I9eeb566fca0e5142b38127aa8f32c21632c33caa --- .../shared/recents/IOverviewProxy.aidl | 13 +-- .../shared/recents/utilities/Utilities.java | 35 +++++- .../shared/system/QuickStepContract.java | 6 +- .../systemui/dagger/DependencyProvider.java | 7 +- .../systemui/navigationbar/NavigationBar.java | 106 ++++-------------- .../NavigationBarA11yHelper.java | 89 +++++++++++++++ .../NavigationBarController.java | 13 ++- .../navigationbar/TaskbarDelegate.java | 57 +++++++++- .../recents/OverviewProxyService.java | 14 --- .../NavigationBarControllerTest.java | 3 +- .../navigationbar/NavigationBarTest.java | 3 +- 11 files changed, 223 insertions(+), 123 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl index 0657c0e1725c4..de9558ebca47b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl @@ -76,25 +76,18 @@ oneway interface IOverviewProxy { */ void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17; - /** - * Sent when IME status changes - */ - void onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) = 18; - /** * Sent when suggested rotation button could be shown */ - void onRotationProposal(int rotation, boolean isValid) = 19; - + void onRotationProposal(int rotation, boolean isValid) = 18; /** * Sent when disable flags change */ - void disable(int displayId, int state1, int state2, boolean animate) = 20; + void disable(int displayId, int state1, int state2, boolean animate) = 19; /** * Sent when behavior changes. See WindowInsetsController#@Behavior */ - void onSystemBarAttributesChanged(int displayId, int behavior) = 21; + void onSystemBarAttributesChanged(int displayId, int behavior) = 20; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 7e9832aa34195..a624f06110e61 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -16,11 +16,14 @@ package com.android.systemui.shared.recents.utilities; +import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; + import android.graphics.Color; +import android.inputmethodservice.InputMethodService; import android.os.Handler; import android.os.Message; import android.view.Surface; -import android.view.View; /* Common code */ public class Utilities { @@ -77,4 +80,34 @@ public class Utilities { public static float clamp(float value, float min, float max) { return Math.max(min, Math.min(max, value)); } + + /** + * @return updated set of flags from InputMethodService based off {@param oldHints} + * Leaves original hints unmodified + */ + public static int calculateBackDispositionHints(int oldHints, int backDisposition, + boolean imeShown, boolean showImeSwitcher) { + int hints = oldHints; + switch (backDisposition) { + case InputMethodService.BACK_DISPOSITION_DEFAULT: + case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: + case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: + if (imeShown) { + hints |= NAVIGATION_HINT_BACK_ALT; + } else { + hints &= ~NAVIGATION_HINT_BACK_ALT; + } + break; + case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: + hints &= ~NAVIGATION_HINT_BACK_ALT; + break; + } + if (showImeSwitcher) { + hints |= NAVIGATION_HINT_IME_SHOWN; + } else { + hints &= ~NAVIGATION_HINT_IME_SHOWN; + } + + return hints; + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index c468e416f8a57..153708a19d220 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -112,6 +112,8 @@ public class QuickStepContract { public static final int SYSUI_STATE_IME_SHOWING = 1 << 18; // The window magnification is overlapped with system gesture insets at the bottom. public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19; + // ImeSwitcher is showing + public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -133,7 +135,8 @@ public class QuickStepContract { SYSUI_STATE_ONE_HANDED_ACTIVE, SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, SYSUI_STATE_IME_SHOWING, - SYSUI_STATE_MAGNIFICATION_OVERLAP + SYSUI_STATE_MAGNIFICATION_OVERLAP, + SYSUI_STATE_IME_SWITCHER_SHOWING }) public @interface SystemUiStateFlags {} @@ -162,6 +165,7 @@ public class QuickStepContract { ? "allow_gesture" : ""); str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : ""); str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : ""); + str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : ""); return str.toString(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 746621dfda274..9a121ba8dc2dc 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -60,6 +60,7 @@ import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarA11yHelper; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarOverlayController; import com.android.systemui.navigationbar.NavigationModeController; @@ -231,7 +232,8 @@ public class DependencyProvider { @Main Handler mainHandler, UiEventLogger uiEventLogger, NavigationBarOverlayController navBarOverlayController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + NavigationBarA11yHelper navigationBarA11yHelper) { return new NavigationBarController(context, windowManager, assistManagerLazy, @@ -256,7 +258,8 @@ public class DependencyProvider { mainHandler, uiEventLogger, navBarOverlayController, - configurationController); + configurationController, + navigationBarA11yHelper); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index dda1029f61545..2d65685061b9d 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -44,6 +44,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; @@ -54,9 +55,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; -import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.IdRes; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; @@ -101,7 +100,6 @@ import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; import android.view.inputmethod.InputMethodManager; import androidx.annotation.VisibleForTesting; @@ -129,6 +127,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; +import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.AutoHideUiElement; @@ -149,7 +148,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; import java.io.PrintWriter; -import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.function.Consumer; @@ -160,8 +158,7 @@ import dagger.Lazy; * Contains logic for a navigation bar view. */ public class NavigationBar implements View.OnAttachStateChangeListener, - Callbacks, NavigationModeController.ModeChangedListener, - AccessibilityButtonModeObserver.ModeChangedListener { + Callbacks, NavigationModeController.ModeChangedListener { public static final String TAG = "NavigationBar"; private static final boolean DEBUG = false; @@ -178,7 +175,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Context mContext; private final WindowManager mWindowManager; private final AccessibilityManager mAccessibilityManager; - private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; private final DeviceProvisionedController mDeviceProvisionedController; private final StatusBarStateController mStatusBarStateController; private final MetricsLogger mMetricsLogger; @@ -199,6 +195,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Handler mHandler; private final NavigationBarOverlayController mNavbarOverlayController; private final UiEventLogger mUiEventLogger; + private final NavigationBarA11yHelper mNavigationBarA11yHelper; private Bundle mSavedState; private NavigationBarView mNavigationBarView; @@ -232,7 +229,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private boolean mTransientShown; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; - private int mA11yBtnMode; private LightBarController mLightBarController; private AutoHideController mAutoHideController; @@ -459,11 +455,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, SystemActions systemActions, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + NavigationBarA11yHelper navigationBarA11yHelper) { mContext = context; mWindowManager = windowManager; mAccessibilityManager = accessibilityManager; - mAccessibilityManagerWrapper = accessibilityManagerWrapper; mDeviceProvisionedController = deviceProvisionedController; mStatusBarStateController = statusBarStateController; mMetricsLogger = metricsLogger; @@ -484,10 +480,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; mUiEventLogger = uiEventLogger; - + mNavigationBarA11yHelper = navigationBarA11yHelper; mNavBarMode = mNavigationModeController.addListener(this); - mAccessibilityButtonModeObserver.addListener(this); - mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); } public NavigationBarView getView() { @@ -578,9 +572,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mContext.getSystemService(WindowManager.class).removeViewImmediate( mNavigationBarView.getRootView()); mNavigationModeController.removeListener(this); - mAccessibilityButtonModeObserver.removeListener(this); - mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); + mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener); mContentResolver.unregisterContentObserver(mAssistContentObserver); mDeviceProvisionedController.removeCallback(mUserSetupListener); @@ -601,7 +594,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); mNavigationBarView.setBehavior(mBehavior); - mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); + mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener); mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener); mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener); @@ -861,26 +854,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return; } boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; - int hints = mNavigationIconHints; - switch (backDisposition) { - case InputMethodService.BACK_DISPOSITION_DEFAULT: - case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: - case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: - if (imeShown) { - hints |= NAVIGATION_HINT_BACK_ALT; - } else { - hints &= ~NAVIGATION_HINT_BACK_ALT; - } - break; - case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: - hints &= ~NAVIGATION_HINT_BACK_ALT; - break; - } - if (showImeSwitcher) { - hints |= NAVIGATION_HINT_IME_SHOWN; - } else { - hints &= ~NAVIGATION_HINT_IME_SHOWN; - } + int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition, + imeShown, showImeSwitcher); if (hints == mNavigationIconHints) return; mNavigationIconHints = hints; @@ -1140,7 +1115,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); - updateAccessibilityServicesState(mAccessibilityManager); + updateAccessibilityServicesState(); ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton(); imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick); @@ -1361,9 +1336,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } - void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { - boolean[] feedbackEnabled = new boolean[1]; - int a11yFlags = getA11yButtonState(feedbackEnabled); + void updateAccessibilityServicesState() { + int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; @@ -1381,7 +1355,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, public void updateSystemUiStateFlags(int a11yFlags) { if (a11yFlags < 0) { - a11yFlags = getA11yButtonState(null); + a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); } boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; @@ -1391,6 +1365,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) + .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, + (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, allowSystemGestureIgnoringBarVisibility()) .commitUpdate(mDisplayId); @@ -1406,44 +1382,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } - /** - * Returns the system UI flags corresponding the the current accessibility button state - * - * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled. - */ - public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) { - boolean feedbackEnabled = false; - // AccessibilityManagerService resolves services for the current user since the local - // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission - final List services = - mAccessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_ALL_MASK); - final List a11yButtonTargets = - mAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON); - final int requestingServices = a11yButtonTargets.size(); - for (int i = services.size() - 1; i >= 0; --i) { - AccessibilityServiceInfo info = services.get(i); - if (info.feedbackType != 0 && info.feedbackType != - AccessibilityServiceInfo.FEEDBACK_GENERIC) { - feedbackEnabled = true; - } - } - - if (outFeedbackEnabled != null) { - outFeedbackEnabled[0] = feedbackEnabled; - } - - // If accessibility button is floating menu mode, click and long click state should be - // disabled. - if (mA11yBtnMode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { - return 0; - } - - return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0) - | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0); - } - private void updateAssistantEntrypoints() { mAssistantAvailable = mAssistManagerLazy.get() .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; @@ -1539,12 +1477,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } - @Override - public void onAccessibilityButtonModeChanged(int mode) { - mA11yBtnMode = mode; - updateAccessibilityServicesState(mAccessibilityManager); - } - public void disableAnimationsDuringHide(long delay) { mNavigationBarView.setLayoutTransitionsEnabled(false); mHandler.postDelayed(mEnableLayoutTransitions, @@ -1569,7 +1501,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.getBarTransitions().finishAnimations(); } - private final AccessibilityServicesStateChangeListener mAccessibilityListener = + private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener = this::updateAccessibilityServicesState; private boolean canShowSecondaryHandle() { @@ -1600,7 +1532,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } if (Intent.ACTION_USER_SWITCHED.equals(action)) { // The accessibility settings may be different for the new user - updateAccessibilityServicesState(mAccessibilityManager); + updateAccessibilityServicesState(); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java new file mode 100644 index 0000000000000..35e1a9dfed334 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java @@ -0,0 +1,89 @@ +package com.android.systemui.navigationbar; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; + +import android.view.accessibility.AccessibilityManager; + +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +/** + * Extracts shared elements of a11y necessary between navbar and taskbar delegate + */ +@SysUISingleton +public final class NavigationBarA11yHelper implements + AccessibilityButtonModeObserver.ModeChangedListener { + private int mA11yBtnMode; + private final AccessibilityManager mAccessibilityManager; + private final List mA11yEventListeners = new ArrayList<>(); + + @Inject + public NavigationBarA11yHelper(AccessibilityManager accessibilityManager, + AccessibilityManagerWrapper accessibilityManagerWrapper, + AccessibilityButtonModeObserver accessibilityButtonModeObserver) { + mAccessibilityManager = accessibilityManager; + mA11yBtnMode = accessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); + accessibilityManagerWrapper.addCallback( + accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate()); + accessibilityButtonModeObserver.addListener(this); + } + + public void registerA11yEventListener(NavA11yEventListener listener) { + mA11yEventListeners.add(listener); + } + + public void removeA11yEventListener(NavA11yEventListener listener) { + mA11yEventListeners.remove(listener); + } + + private void dispatchEventUpdate() { + for (NavA11yEventListener listener : mA11yEventListeners) { + listener.updateAccessibilityServicesState(); + } + } + + @Override + public void onAccessibilityButtonModeChanged(int mode) { + mA11yBtnMode = mode; + dispatchEventUpdate(); + } + + /** + * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and + * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE} + * + * @return the a11y button clickable and long_clickable states, or 0 if there is no + * a11y button in the navbar + */ + public int getA11yButtonState() { + // AccessibilityManagerService resolves services for the current user since the local + // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission + final List a11yButtonTargets = + mAccessibilityManager.getAccessibilityShortcutTargets( + AccessibilityManager.ACCESSIBILITY_BUTTON); + final int requestingServices = a11yButtonTargets.size(); + + // If accessibility button is floating menu mode, click and long click state should be + // disabled. + if (mA11yBtnMode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { + return 0; + } + + return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0) + | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0); + } + + public interface NavA11yEventListener { + void updateAccessibilityServicesState(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 8b5a537ba2425..30eb645e43928 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -111,6 +111,7 @@ public class NavigationBarController implements Callbacks, private final SystemActions mSystemActions; private final UiEventLogger mUiEventLogger; private final Handler mHandler; + private final NavigationBarA11yHelper mNavigationBarA11yHelper; private final DisplayManager mDisplayManager; private final NavigationBarOverlayController mNavBarOverlayController; private final TaskbarDelegate mTaskbarDelegate; @@ -151,7 +152,8 @@ public class NavigationBarController implements Callbacks, @Main Handler mainHandler, UiEventLogger uiEventLogger, NavigationBarOverlayController navBarOverlayController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + NavigationBarA11yHelper navigationBarA11yHelper) { mContext = context; mWindowManager = windowManager; mAssistManagerLazy = assistManagerLazy; @@ -175,6 +177,7 @@ public class NavigationBarController implements Callbacks, mSystemActions = systemActions; mUiEventLogger = uiEventLogger; mHandler = mainHandler; + mNavigationBarA11yHelper = navigationBarA11yHelper; mDisplayManager = mContext.getSystemService(DisplayManager.class); commandQueue.addCallback(this); configurationController.addCallback(this); @@ -182,7 +185,8 @@ public class NavigationBarController implements Callbacks, mNavBarOverlayController = navBarOverlayController; mNavMode = mNavigationModeController.addListener(this); mNavigationModeController.addListener(this); - mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService); + mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService, + navigationBarA11yHelper, mSysUiFlagsContainer); mIsTablet = isTablet(mContext.getResources().getConfiguration()); } @@ -241,10 +245,12 @@ public class NavigationBarController implements Callbacks, // Remove navigation bar when taskbar is showing, currently only for 3 button mode removeNavigationBar(mContext.getDisplayId()); mCommandQueue.addCallback(mTaskbarDelegate); + mTaskbarDelegate.init(mContext.getDisplayId()); } else if (mNavigationBars.get(mContext.getDisplayId()) == null) { // Add navigation bar after taskbar goes away createNavigationBar(mContext.getDisplay(), null, null); mCommandQueue.removeCallback(mTaskbarDelegate); + mTaskbarDelegate.destroy(); } return true; @@ -361,7 +367,8 @@ public class NavigationBarController implements Callbacks, mSystemActions, mHandler, mNavBarOverlayController, - mUiEventLogger); + mUiEventLogger, + mNavigationBarA11yHelper); mNavigationBars.put(displayId, navBar); View navigationBarView = navBar.createView(savedState); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index 5d39ffe91c923..e9674c9acedeb 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -16,25 +16,76 @@ package com.android.systemui.navigationbar; +import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; + +import android.inputmethodservice.InputMethodService; import android.os.IBinder; import com.android.internal.view.AppearanceRegion; +import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.statusbar.CommandQueue; public class TaskbarDelegate implements CommandQueue.Callbacks { private final OverviewProxyService mOverviewProxyService; + private final NavigationBarA11yHelper mNavigationBarA11yHelper; + private final SysUiState mSysUiState; + private int mDisplayId; + private int mNavigationIconHints; + private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener = + this::updateSysuiFlags; - public TaskbarDelegate(OverviewProxyService overviewProxyService) { + public TaskbarDelegate(OverviewProxyService overviewProxyService, + NavigationBarA11yHelper navigationBarA11yHelper, + SysUiState sysUiState) { mOverviewProxyService = overviewProxyService; + mNavigationBarA11yHelper = navigationBarA11yHelper; + mSysUiState = sysUiState; + } + + public void destroy() { + mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener); + } + + public void init(int displayId) { + mDisplayId = displayId; + mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener); + // Set initial state for any listeners + updateSysuiFlags(); + } + + private void updateSysuiFlags() { + int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); + boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; + boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; + + mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable) + .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable) + .setFlag(SYSUI_STATE_IME_SHOWING, + (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) + .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, + (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) + .commitUpdate(mDisplayId); } @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { - mOverviewProxyService.notifyImeWindowStatus(displayId, token, vis, backDisposition, - showImeSwitcher); + boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; + int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition, + imeShown, showImeSwitcher); + if (hints != mNavigationIconHints) { + mNavigationIconHints = hints; + updateSysuiFlags(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 56d45d3117d69..c0831a4688172 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -974,20 +974,6 @@ public class OverviewProxyService extends CurrentUserTracker implements } } - public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { - try { - if (mOverviewProxy != null) { - mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition, - showImeSwitcher); - } else { - Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status."); - } - } catch (RemoteException e) { - Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e); - } - } - public void onRotationProposal(int rotation, boolean isValid) { try { if (mOverviewProxy != null) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java index da63b8a3ca4b5..3b5431f1445ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -108,7 +108,8 @@ public class NavigationBarControllerTest extends SysuiTestCase { Dependency.get(Dependency.MAIN_HANDLER), mock(UiEventLogger.class), mock(NavigationBarOverlayController.class), - mock(ConfigurationController.class))); + mock(ConfigurationController.class), + mock(NavigationBarA11yHelper.class))); initializeNavigationBars(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 4ec45b444c465..6359ca835e8fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -276,7 +276,8 @@ public class NavigationBarTest extends SysuiTestCase { mock(SystemActions.class), mHandler, mock(NavigationBarOverlayController.class), - mUiEventLogger)); + mUiEventLogger, + mock(NavigationBarA11yHelper.class))); } private void processAllMessages() {