From c1e28cc20e583b3b80a22d906c9d81591fe73e1f Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Tue, 22 Dec 2020 10:32:40 +0800 Subject: [PATCH] Observer for the accessibility button mode and notify its listeners for the changes * Update accessibility button state (visibility) in navigationbar when accessibility button mode listener receive the changes. Cherry picked from commit 2c60295be9ceb019ac934b38da14f97f08b1c6f0 Bug: 173938894 Bug: 173940864 Test: atest AccessibilityButtonModeObserverTest Change-Id: I03b961c95a20e4698491443012beb3ab73333199 Merged-In: I03b961c95a20e4698491443012beb3ab73333199 --- .../AccessibilityButtonModeObserver.java | 142 ++++++++++++++++++ .../systemui/dagger/DependencyProvider.java | 3 + .../systemui/navigationbar/NavigationBar.java | 26 +++- .../NavigationBarController.java | 5 + .../AccessibilityButtonModeObserverTest.java | 84 +++++++++++ .../NavigationBarControllerTest.java | 2 + .../navigationbar/NavigationBarTest.java | 2 + 7 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java new file mode 100644 index 0000000000000..39d8dbb3beec0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + +import android.annotation.IntDef; +import android.annotation.MainThread; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; + +import androidx.annotation.NonNull; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.inject.Inject; + +/** + * Observes changes of the accessibility button mode + * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE} and notify its listeners. + */ +@MainThread +@SysUISingleton +public class AccessibilityButtonModeObserver { + + private static final int ACCESSIBILITY_BUTTON_MODE_DEFAULT = + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + + private final ContentResolver mContentResolver; + @VisibleForTesting + final ContentObserver mContentObserver; + + private final List mListeners = new ArrayList<>(); + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU + }) + private @interface AccessibilityButtonMode {} + + /** Listener for accessibility button mode changes. */ + public interface ModeChangedListener { + + /** + * Called when accessibility button mode changes. + * + * @param mode Current accessibility button mode. + */ + void onAccessibilityButtonModeChanged(@AccessibilityButtonMode int mode); + } + + @Inject + public AccessibilityButtonModeObserver(Context context) { + mContentResolver = context.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + updateAccessibilityButtonModeChanged(); + } + }; + } + + /** + * Registers a listener to receive updates from settings key {@link + * Settings.Secure#ACCESSIBILITY_BUTTON_MODE}. + * + * @param listener A {@link ModeChangedListener} object. + */ + public void addListener(@NonNull ModeChangedListener listener) { + Objects.requireNonNull(listener, "listener must be non-null"); + + mListeners.add(listener); + + if (mListeners.size() == 1) { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */ + false, mContentObserver); + } + } + + /** + * Unregisters a listener previously registered with {@link #addListener(ModeChangedListener)}. + * + * @param listener A {@link ModeChangedListener} object. + */ + public void removeListener(@NonNull ModeChangedListener listener) { + Objects.requireNonNull(listener, "listener must be non-null"); + + mListeners.remove(listener); + + if (mListeners.isEmpty()) { + mContentResolver.unregisterContentObserver(mContentObserver); + } + } + + /** + * Gets the current accessibility button mode from the current user's settings. + * + * See {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE}. + */ + @AccessibilityButtonMode + public int getCurrentAccessibilityButtonMode() { + return Settings.Secure.getInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_DEFAULT); + } + + private void updateAccessibilityButtonModeChanged() { + final int mode = getCurrentAccessibilityButtonMode(); + final int listenerSize = mListeners.size(); + for (int i = 0; i < listenerSize; i++) { + mListeners.get(i).onAccessibilityButtonModeChanged(mode); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 8e344d2c2df74..d60122def85a7 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -46,6 +46,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.Prefs; import com.android.systemui.R; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.ModeSwitchesController; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; @@ -213,6 +214,7 @@ public class DependencyProvider { MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, + AccessibilityButtonModeObserver accessibilityButtonModeObserver, StatusBarStateController statusBarStateController, SysUiState sysUiFlagsContainer, BroadcastDispatcher broadcastDispatcher, @@ -237,6 +239,7 @@ public class DependencyProvider { metricsLogger, overviewProxyService, navigationModeController, + accessibilityButtonModeObserver, statusBarStateController, sysUiFlagsContainer, broadcastDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index a0906dfdfd5e4..06e0e94914d25 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -113,6 +113,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; @@ -158,7 +159,8 @@ import dagger.Lazy; * Contains logic for a navigation bar view. */ public class NavigationBar implements View.OnAttachStateChangeListener, - Callbacks, NavigationModeController.ModeChangedListener, DisplayManager.DisplayListener { + Callbacks, NavigationModeController.ModeChangedListener, + AccessibilityButtonModeObserver.ModeChangedListener, DisplayManager.DisplayListener { public static final String TAG = "NavigationBar"; private static final boolean DEBUG = false; @@ -186,6 +188,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final NotificationRemoteInputManager mNotificationRemoteInputManager; private final OverviewProxyService mOverviewProxyService; private final NavigationModeController mNavigationModeController; + private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; private final BroadcastDispatcher mBroadcastDispatcher; private final CommandQueue mCommandQueue; private final Optional mPipOptional; @@ -226,6 +229,7 @@ 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; @@ -443,6 +447,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, + AccessibilityButtonModeObserver accessibilityButtonModeObserver, StatusBarStateController statusBarStateController, SysUiState sysUiFlagsContainer, BroadcastDispatcher broadcastDispatcher, @@ -470,7 +475,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNotificationRemoteInputManager = notificationRemoteInputManager; mOverviewProxyService = overviewProxyService; mNavigationModeController = navigationModeController; - mNavBarMode = navigationModeController.addListener(this); + mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; mBroadcastDispatcher = broadcastDispatcher; mCommandQueue = commandQueue; mPipOptional = pipOptional; @@ -480,6 +485,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; mUiEventLogger = uiEventLogger; + + mNavBarMode = mNavigationModeController.addListener(this); + mAccessibilityButtonModeObserver.addListener(this); + mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); } public View getView() { @@ -560,6 +569,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mContext.getSystemService(WindowManager.class).removeViewImmediate( mNavigationBarView.getRootView()); mNavigationModeController.removeListener(this); + mAccessibilityButtonModeObserver.removeListener(this); mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); mContentResolver.unregisterContentObserver(mAssistContentObserver); @@ -1450,6 +1460,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, outFeedbackEnabled[0] = feedbackEnabled; } + // If accessibility button is floating menu mode, click and long click state should be + // disabled. + if (mA11yBtnMode == Settings.Secure.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); } @@ -1540,6 +1556,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } + @Override + public void onAccessibilityButtonModeChanged(int mode) { + mA11yBtnMode = mode; + updateAccessibilityServicesState(mAccessibilityManager); + } + public void disableAnimationsDuringHide(long delay) { mNavigationBarView.setLayoutTransitionsEnabled(false); mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true), diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index ca69e6de2d0a9..50efa8def5bfc 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -43,6 +43,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.Dumpable; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; @@ -91,6 +92,7 @@ public class NavigationBarController implements Callbacks, private final MetricsLogger mMetricsLogger; private final OverviewProxyService mOverviewProxyService; private final NavigationModeController mNavigationModeController; + private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; private final StatusBarStateController mStatusBarStateController; private final SysUiState mSysUiFlagsContainer; private final BroadcastDispatcher mBroadcastDispatcher; @@ -127,6 +129,7 @@ public class NavigationBarController implements Callbacks, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, + AccessibilityButtonModeObserver accessibilityButtonModeObserver, StatusBarStateController statusBarStateController, SysUiState sysUiFlagsContainer, BroadcastDispatcher broadcastDispatcher, @@ -151,6 +154,7 @@ public class NavigationBarController implements Callbacks, mMetricsLogger = metricsLogger; mOverviewProxyService = overviewProxyService; mNavigationModeController = navigationModeController; + mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; mStatusBarStateController = statusBarStateController; mSysUiFlagsContainer = sysUiFlagsContainer; mBroadcastDispatcher = broadcastDispatcher; @@ -289,6 +293,7 @@ public class NavigationBarController implements Callbacks, mMetricsLogger, mOverviewProxyService, mNavigationModeController, + mAccessibilityButtonModeObserver, mStatusBarStateController, mSysUiFlagsContainer, mBroadcastDispatcher, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java new file mode 100644 index 0000000000000..0eab0bb167b08 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility; + + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.provider.Settings; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class AccessibilityButtonModeObserverTest extends SysuiTestCase { + + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private AccessibilityButtonModeObserver.ModeChangedListener mListener; + + private AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; + + private static final int TEST_A11Y_BTN_MODE_VALUE = + Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + + @Before + public void setUpController() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + mAccessibilityButtonModeObserver = new AccessibilityButtonModeObserver(mContext); + } + + @Test + public void onChange_haveListener_invokeCallback() { + mAccessibilityButtonModeObserver.addListener(mListener); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE); + + mAccessibilityButtonModeObserver.mContentObserver.onChange(false); + + verify(mListener).onAccessibilityButtonModeChanged(TEST_A11Y_BTN_MODE_VALUE); + } + + @Test + public void onChange_noListener_noInvokeCallback() { + mAccessibilityButtonModeObserver.addListener(mListener); + mAccessibilityButtonModeObserver.removeListener(mListener); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE); + + mAccessibilityButtonModeObserver.mContentObserver.onChange(false); + + verify(mListener, never()).onAccessibilityButtonModeChanged(anyInt()); + } +} 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 e761da4c4675c..c29b812dde7a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -45,6 +45,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -93,6 +94,7 @@ public class NavigationBarControllerTest extends SysuiTestCase { mock(MetricsLogger.class), mock(OverviewProxyService.class), mock(NavigationModeController.class), + mock(AccessibilityButtonModeObserver.class), mock(StatusBarStateController.class), mock(SysUiState.class), mock(BroadcastDispatcher.class), 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 22c553b764b24..f0c48bd198ffd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -67,6 +67,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -259,6 +260,7 @@ public class NavigationBarTest extends SysuiTestCase { new MetricsLogger(), mOverviewProxyService, mock(NavigationModeController.class), + mock(AccessibilityButtonModeObserver.class), mock(StatusBarStateController.class), mMockSysUiState, mBroadcastDispatcher,