From c1e28cc20e583b3b80a22d906c9d81591fe73e1f Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Tue, 22 Dec 2020 10:32:40 +0800 Subject: [PATCH 1/6] 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, From 5154c4cfae9a1314012cc8dff088f750bc053810 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Mon, 11 Jan 2021 21:27:05 +0800 Subject: [PATCH 2/6] Handle the basic logic to show or hide accessibility floating menu * AccessibilityFloatingMenuController will listen to the changes from AccessibilityButtonTargetsObserver and AccessibilityButtonModeObserver then handle the visibility of FloatingMenu * Add abstract class SecureSettingsContentObserver to handle a registry of listeners of specific types and dispatch changes to child Cherry picked from commit 523514fd24d72b8420c0ca184643751980e6f218 Bug: 173940864 Test: atest AccessibilityButtonTargetsObserverTest AccessibilityButtonModeObserverTest AccessibilityFloatingMenuControllerTest Change-Id: I07fded55340644bce84a3f17a7a4da950268d2d2 Merged-In: I07fded55340644bce84a3f17a7a4da950268d2d2 --- .../src/com/android/systemui/Dependency.java | 14 ++ .../AccessibilityButtonModeObserver.java | 91 +++------- .../AccessibilityButtonTargetsObserver.java | 66 ++++++++ .../SecureSettingsContentObserver.java | 121 +++++++++++++ .../AccessibilityFloatingMenuController.java | 117 +++++++++++++ .../systemui/dagger/DependencyProvider.java | 12 ++ .../systemui/statusbar/phone/StatusBar.java | 2 + .../phone/dagger/StatusBarPhoneModule.java | 3 + .../AccessibilityButtonModeObserverTest.java | 15 +- ...ccessibilityButtonTargetsObserverTest.java | 93 ++++++++++ .../SecureSettingsContentObserverTest.java | 77 +++++++++ ...cessibilityFloatingMenuControllerTest.java | 160 ++++++++++++++++++ .../statusbar/phone/StatusBarTest.java | 3 + 13 files changed, 707 insertions(+), 67 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java create mode 100644 packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java create mode 100644 packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index ae4c8e5a33279..06b486ec43d0b 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -34,6 +34,9 @@ import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.clock.ClockManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; +import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -284,6 +287,8 @@ public class Dependency { @Inject Lazy mIWindowManager; @Inject Lazy mOverviewProxyService; @Inject Lazy mNavBarModeController; + @Inject Lazy mAccessibilityButtonModeObserver; + @Inject Lazy mAccessibilityButtonListController; @Inject Lazy mEnhancedEstimates; @Inject Lazy mVibratorHelper; @Inject Lazy mIStatusBarService; @@ -294,6 +299,7 @@ public class Dependency { @Inject Lazy mNotificationRemoteInputManagerCallback; @Inject Lazy mAppOpsController; @Inject Lazy mNavigationBarController; + @Inject Lazy mAccessibilityFloatingMenuController; @Inject Lazy mStatusBarStateController; @Inject Lazy mNotificationLockscreenUserManager; @Inject Lazy mNotificationGroupAlertTransferHelper; @@ -470,6 +476,11 @@ public class Dependency { mProviders.put(NavigationModeController.class, mNavBarModeController::get); + mProviders.put(AccessibilityButtonModeObserver.class, + mAccessibilityButtonModeObserver::get); + mProviders.put(AccessibilityButtonTargetsObserver.class, + mAccessibilityButtonListController::get); + mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get); mProviders.put(VibratorHelper.class, mVibratorHelper::get); @@ -489,6 +500,9 @@ public class Dependency { mProviders.put(NavigationBarController.class, mNavigationBarController::get); + mProviders.put(AccessibilityFloatingMenuController.class, + mAccessibilityFloatingMenuController::get); + mProviders.put(StatusBarStateController.class, mStatusBarStateController::get); mProviders.put(NotificationLockscreenUserManager.class, mNotificationLockscreenUserManager::get); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java index 39d8dbb3beec0..9bedb1ea75637 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java @@ -21,23 +21,14 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGAT 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 android.util.Log; -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; @@ -47,23 +38,20 @@ import javax.inject.Inject; */ @MainThread @SysUISingleton -public class AccessibilityButtonModeObserver { +public class AccessibilityButtonModeObserver extends + SecureSettingsContentObserver { + + private static final String TAG = "A11yButtonModeObserver"; 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 {} + public @interface AccessibilityButtonMode {} /** Listener for accessibility button mode changes. */ public interface ModeChangedListener { @@ -71,54 +59,20 @@ public class AccessibilityButtonModeObserver { /** * Called when accessibility button mode changes. * - * @param mode Current accessibility button mode. + * @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(); - } - }; + super(context, Settings.Secure.ACCESSIBILITY_BUTTON_MODE); } - /** - * 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); - } + @Override + void onValueChanged(ModeChangedListener listener, String value) { + final int mode = parseAccessibilityButtonMode(value); + listener.onAccessibilityButtonModeChanged(mode); } /** @@ -126,17 +80,22 @@ public class AccessibilityButtonModeObserver { * * 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); + final String value = getSettingsValue(); + + return parseAccessibilityButtonMode(value); } - private void updateAccessibilityButtonModeChanged() { - final int mode = getCurrentAccessibilityButtonMode(); - final int listenerSize = mListeners.size(); - for (int i = 0; i < listenerSize; i++) { - mListeners.get(i).onAccessibilityButtonModeChanged(mode); + private int parseAccessibilityButtonMode(String value) { + int mode; + + try { + mode = Integer.parseInt(value); + } catch (NumberFormatException e) { + Log.e(TAG, "Invalid string for " + e); + mode = ACCESSIBILITY_BUTTON_MODE_DEFAULT; } + + return mode; } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java new file mode 100644 index 0000000000000..b32ebccdd7dbb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java @@ -0,0 +1,66 @@ +/* + * 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 android.content.Context; +import android.provider.Settings; + +import androidx.annotation.MainThread; +import androidx.annotation.Nullable; + +import com.android.systemui.dagger.SysUISingleton; + +import javax.inject.Inject; + +/** + * Controller for tracking the current accessibility button list. + * + * @see Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS + */ +@MainThread +@SysUISingleton +public class AccessibilityButtonTargetsObserver extends + SecureSettingsContentObserver { + + /** Listener for accessibility button targets changes. */ + public interface TargetsChangedListener { + + /** + * Called when accessibility button targets changes. + * + * @param targets Current content of {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS} + */ + void onAccessibilityButtonTargetsChanged(String targets); + } + + @Inject + public AccessibilityButtonTargetsObserver(Context context) { + super(context, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); + } + + @Override + void onValueChanged(TargetsChangedListener listener, String value) { + listener.onAccessibilityButtonTargetsChanged(value); + } + + /** Returns the current string from settings key + * {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS}. */ + @Nullable + public String getCurrentAccessibilityButtonTargets() { + return getSettingsValue(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java new file mode 100644 index 0000000000000..4f8d8666a24ae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java @@ -0,0 +1,121 @@ +/* + * 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 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 java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Provides basic methods for adding, removing arbitrary listeners and inquiry given {@code + * secureSettingsKey} value; it must comes from {@link Settings.Secure}. + * + * This abstract class is intended to be subclassed and specialized to maintain + * a registry of listeners of specific types and dispatch changes to them. + * + * @param The listener type + */ +public abstract class SecureSettingsContentObserver { + + private final ContentResolver mContentResolver; + @VisibleForTesting + final ContentObserver mContentObserver; + + private final String mKey; + + @VisibleForTesting + final List mListeners = new ArrayList<>(); + + protected SecureSettingsContentObserver(Context context, String secureSettingsKey) { + mKey = secureSettingsKey; + mContentResolver = context.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + updateValueChanged(); + } + }; + } + + /** + * Registers a listener to receive updates from given settings key {@code secureSettingsKey}. + * + * @param listener A listener to be added to receive the changes + */ + public void addListener(@NonNull T listener) { + Objects.requireNonNull(listener, "listener must be non-null"); + + mListeners.add(listener); + + if (mListeners.size() == 1) { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(mKey), /* notifyForDescendants= */ + false, mContentObserver); + } + } + + /** + * Unregisters a listener previously registered with {@link #addListener(T listener)}. + * + * @param listener A listener to be removed from receiving the changes + */ + public void removeListener(@NonNull T listener) { + Objects.requireNonNull(listener, "listener must be non-null"); + + mListeners.remove(listener); + + if (mListeners.isEmpty()) { + mContentResolver.unregisterContentObserver(mContentObserver); + } + } + + /** + * Gets the value from the current user's secure settings. + * + * See {@link Settings.Secure}. + */ + public final String getSettingsValue() { + return Settings.Secure.getString(mContentResolver, mKey); + } + + private void updateValueChanged() { + final String value = getSettingsValue(); + final int listenerSize = mListeners.size(); + for (int i = 0; i < listenerSize; i++) { + onValueChanged(mListeners.get(i), value); + } + } + + /** + * Called when the registered value from {@code secureSettingsKey} changes. + * + * @param listener A listener could be used to receive the updates + * @param value Content changed value from {@code secureSettingsKey} + */ + abstract void onValueChanged(T listener, String value); +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java new file mode 100644 index 0000000000000..fddbee393e4fb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java @@ -0,0 +1,117 @@ +/* + * 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.floatingmenu; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + +import android.content.Context; +import android.text.TextUtils; + +import androidx.annotation.MainThread; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode; +import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; +import com.android.systemui.dagger.SysUISingleton; + +import javax.inject.Inject; + +/** A controller to handle the lifecycle of accessibility floating menu. */ +@MainThread +@SysUISingleton +public class AccessibilityFloatingMenuController implements + AccessibilityButtonModeObserver.ModeChangedListener, + AccessibilityButtonTargetsObserver.TargetsChangedListener { + + private final Context mContext; + private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; + private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver; + + @VisibleForTesting + IAccessibilityFloatingMenu mFloatingMenu; + private int mBtnMode; + private String mBtnTargets; + + @Inject + public AccessibilityFloatingMenuController(Context context, + AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver, + AccessibilityButtonModeObserver accessibilityButtonModeObserver) { + mContext = context; + mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver; + mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; + + mAccessibilityButtonModeObserver.addListener(this); + mAccessibilityButtonTargetsObserver.addListener(this); + mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); + mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); + + handleFloatingMenuVisibility(mBtnMode, mBtnTargets); + } + + /** + * Handles visibility of the accessibility floating menu when accessibility button mode changes. + * + * @param mode Current accessibility button mode. + */ + @Override + public void onAccessibilityButtonModeChanged(@AccessibilityButtonMode int mode) { + mBtnMode = mode; + handleFloatingMenuVisibility(mBtnMode, mBtnTargets); + } + + /** + * Handles visibility of the accessibility floating menu when accessibility button targets + * changes. + * List should come from {@link android.provider.Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS}. + * @param targets Current accessibility button list. + */ + @Override + public void onAccessibilityButtonTargetsChanged(String targets) { + mBtnTargets = targets; + handleFloatingMenuVisibility(mBtnMode, mBtnTargets); + } + + private void handleFloatingMenuVisibility(@AccessibilityButtonMode int mode, String targets) { + if (shouldShowFloatingMenu(mode, targets)) { + showFloatingMenu(); + } else { + destroyFloatingMenu(); + } + } + + private boolean shouldShowFloatingMenu(@AccessibilityButtonMode int mode, String targets) { + return mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU && !TextUtils.isEmpty(targets); + } + + private void showFloatingMenu() { + if (mFloatingMenu == null) { + mFloatingMenu = new AccessibilityFloatingMenu(mContext); + } + + mFloatingMenu.show(); + } + + private void destroyFloatingMenu() { + if (mFloatingMenu == null) { + return; + } + + mFloatingMenu.hide(); + mFloatingMenu = null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index d60122def85a7..1a729295165c4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -47,8 +47,10 @@ 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.AccessibilityButtonTargetsObserver; import com.android.systemui.accessibility.ModeSwitchesController; import com.android.systemui.accessibility.SystemActions; +import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger; @@ -257,6 +259,16 @@ public class DependencyProvider { configurationController); } + /** */ + @Provides + @SysUISingleton + public AccessibilityFloatingMenuController provideAccessibilityFloatingMenuController( + Context context, AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver, + AccessibilityButtonModeObserver accessibilityButtonModeObserver) { + return new AccessibilityFloatingMenuController(context, accessibilityButtonTargetsObserver, + accessibilityButtonModeObserver); + } + /** */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c6ef8a3d7ba4b..6dd00a5ec70d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -144,6 +144,7 @@ import com.android.systemui.InitController; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.charging.WirelessChargingAnimation; @@ -737,6 +738,7 @@ public class StatusBar extends SystemUI implements DemoMode, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, NavigationBarController navigationBarController, + AccessibilityFloatingMenuController accessibilityFloatingMenuController, Lazy assistManagerLazy, ConfigurationController configurationController, NotificationShadeWindowController notificationShadeWindowController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index b572c57590ae5..17bb449ec5f49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -29,6 +29,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; +import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; @@ -164,6 +165,7 @@ public interface StatusBarPhoneModule { VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, NavigationBarController navigationBarController, + AccessibilityFloatingMenuController accessibilityFloatingMenuController, Lazy assistManagerLazy, ConfigurationController configurationController, NotificationShadeWindowController notificationShadeWindowController, @@ -245,6 +247,7 @@ public interface StatusBarPhoneModule { visualStabilityManager, deviceProvisionedController, navigationBarController, + accessibilityFloatingMenuController, assistManagerLazy, configurationController, notificationShadeWindowController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java index 0eab0bb167b08..01b7adefbacf5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java @@ -17,6 +17,8 @@ package com.android.systemui.accessibility; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -52,7 +54,7 @@ public class AccessibilityButtonModeObserverTest extends SysuiTestCase { Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; @Before - public void setUpController() { + public void setUp() { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_MODE, Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); @@ -81,4 +83,15 @@ public class AccessibilityButtonModeObserverTest extends SysuiTestCase { verify(mListener, never()).onAccessibilityButtonModeChanged(anyInt()); } + + @Test + public void getCurrentAccessibilityButtonMode_expectedValue() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE); + + final int actualValue = + mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); + + assertThat(actualValue).isEqualTo(TEST_A11Y_BTN_MODE_VALUE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java new file mode 100644 index 0000000000000..1e49fc998ea4f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java @@ -0,0 +1,93 @@ +/* + * 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 com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyString; +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; + +/** Test for {@link AccessibilityButtonTargetsObserver}. */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class AccessibilityButtonTargetsObserverTest extends SysuiTestCase { + + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private AccessibilityButtonTargetsObserver.TargetsChangedListener mListener; + + private AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver; + + private static final String TEST_A11Y_BTN_TARGETS = "Magnification"; + + @Before + public void setUp() { + mAccessibilityButtonTargetsObserver = new AccessibilityButtonTargetsObserver(mContext); + } + + @Test + public void onChange_haveListener_invokeCallback() { + mAccessibilityButtonTargetsObserver.addListener(mListener); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + + mAccessibilityButtonTargetsObserver.mContentObserver.onChange(false); + + verify(mListener).onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS); + } + + @Test + public void onChange_listenerRemoved_noInvokeCallback() { + mAccessibilityButtonTargetsObserver.addListener(mListener); + mAccessibilityButtonTargetsObserver.removeListener(mListener); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + + mAccessibilityButtonTargetsObserver.mContentObserver.onChange(false); + + verify(mListener, never()).onAccessibilityButtonTargetsChanged(anyString()); + } + + @Test + public void getCurrentAccessibilityButtonTargets_expectedValue() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + + final String actualValue = + mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); + + assertThat(actualValue).isEqualTo(TEST_A11Y_BTN_TARGETS); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java new file mode 100644 index 0000000000000..5b1c441a71a1c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java @@ -0,0 +1,77 @@ +/* + * 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 com.google.common.truth.Truth.assertThat; + +import android.content.Context; +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.Test; +import org.junit.runner.RunWith; + +/** Test for {@link SecureSettingsContentObserver}. */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class SecureSettingsContentObserverTest extends SysuiTestCase { + + private FakeSecureSettingsContentObserver mTestObserver; + + @Before + public void setUpObserver() { + mTestObserver = new FakeSecureSettingsContentObserver(mContext, + Settings.Secure.ACCESSIBILITY_BUTTON_MODE); + } + + @Test(expected = NullPointerException.class) + public void addNullListener_throwNPE() { + mTestObserver.addListener(null); + } + + @Test(expected = NullPointerException.class) + public void removeNullListener_throwNPE() { + mTestObserver.removeListener(null); + } + + @Test + public void checkValue() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 1); + + assertThat(mTestObserver.getSettingsValue()).isEqualTo("1"); + } + + + private static class FakeSecureSettingsContentObserver extends + SecureSettingsContentObserver { + + protected FakeSecureSettingsContentObserver(Context context, + String secureSettingsKey) { + super(context, secureSettingsKey); + } + + @Override + void onValueChanged(Object listener, String value) { + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java new file mode 100644 index 0000000000000..0663f46a00e98 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -0,0 +1,160 @@ +/* + * 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.floatingmenu; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.provider.Settings; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Test for {@link AccessibilityFloatingMenuController}. */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { + + private static final String TEST_A11Y_BTN_TARGETS = "Magnification"; + + private AccessibilityFloatingMenuController mController; + private AccessibilityButtonTargetsObserver mTargetsObserver; + private AccessibilityButtonModeObserver mModeObserver; + + @Test + public void initController_registerListeners() { + mController = setUpController(); + + verify(mTargetsObserver).addListener( + any(AccessibilityButtonTargetsObserver.TargetsChangedListener.class)); + verify(mModeObserver).addListener( + any(AccessibilityButtonModeObserver.ModeChangedListener.class)); + } + + @Test + public void onAccessibilityButtonModeChanged_floatingModeAndHasButtonTargets_showWidget() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + mController = setUpController(); + + mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + + assertThat(mController.mFloatingMenu).isNotNull(); + } + + @Test + public void onAccessibilityButtonModeChanged_floatingModeAndNoButtonTargets_destroyWidget() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, ""); + mController = setUpController(); + + mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + + assertThat(mController.mFloatingMenu).isNull(); + } + + @Test + public void onAccessibilityButtonModeChanged_navBarModeAndHasButtonTargets_showWidget() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + mController = setUpController(); + + mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + assertThat(mController.mFloatingMenu).isNull(); + } + + @Test + public void onAccessibilityButtonModeChanged_navBarModeAndNoButtonTargets_destroyWidget() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, ""); + mController = setUpController(); + + mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + assertThat(mController.mFloatingMenu).isNull(); + } + + @Test + public void onAccessibilityButtonTargetsChanged_floatingModeAndHasButtonTargets_showWidget() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + mController = setUpController(); + + mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS); + + assertThat(mController.mFloatingMenu).isNotNull(); + } + + @Test + public void onAccessibilityButtonTargetsChanged_floatingModeAndNoButtonTargets_destroyWidget() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + mController = setUpController(); + + mController.onAccessibilityButtonTargetsChanged(""); + + assertThat(mController.mFloatingMenu).isNull(); + } + + @Test + public void onAccessibilityButtonTargetsChanged_navBarModeAndHasButtonTargets_showWidget() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + mController = setUpController(); + + mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS); + + assertThat(mController.mFloatingMenu).isNull(); + } + + @Test + public void onAccessibilityButtonTargetsChanged_buttonModeAndNoButtonTargets_destroyWidget() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + mController = setUpController(); + + mController.onAccessibilityButtonTargetsChanged(""); + + assertThat(mController.mFloatingMenu).isNull(); + } + + private AccessibilityFloatingMenuController setUpController() { + mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class)); + mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class)); + + return new AccessibilityFloatingMenuController(mContext, mTargetsObserver, mModeObserver); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 18bdd41dada05..0b454bfa8b691 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -78,6 +78,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; @@ -210,6 +211,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private NotificationMediaManager mNotificationMediaManager; @Mock private NavigationBarController mNavigationBarController; + @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController; @Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier; @Mock private SysuiColorExtractor mColorExtractor; @Mock private ColorExtractor.GradientColors mGradientColors; @@ -385,6 +387,7 @@ public class StatusBarTest extends SysuiTestCase { mVisualStabilityManager, mDeviceProvisionedController, mNavigationBarController, + mAccessibilityFloatingMenuController, () -> mAssistManager, configurationController, mNotificationShadeWindowController, From e24d701f4cc1151ebe53412be9acb2edd7fa05f0 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Thu, 14 Jan 2021 21:58:27 +0800 Subject: [PATCH 3/6] Handle the phone get booted case to show accessibility floating menu * AccessibilityManager might not ready when the phone just got booted. Cherry picked from commit bf0ab7163df50083b8d7d537846b52cdd79c544e Bug: 173940864 Test: atest AccessibilityFloatingMenuControllerTest Change-Id: I0339597cd53ca5daade427fef257bfddb3b86bd8 Merged-In: I0339597cd53ca5daade427fef257bfddb3b86bd8 --- .../AccessibilityFloatingMenuController.java | 32 ++++++++++++- ...cessibilityFloatingMenuControllerTest.java | 46 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java index fddbee393e4fb..112e9cae0a214 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATIN import android.content.Context; import android.text.TextUtils; +import android.view.accessibility.AccessibilityManager; import androidx.annotation.MainThread; @@ -36,9 +37,11 @@ import javax.inject.Inject; @SysUISingleton public class AccessibilityFloatingMenuController implements AccessibilityButtonModeObserver.ModeChangedListener, - AccessibilityButtonTargetsObserver.TargetsChangedListener { + AccessibilityButtonTargetsObserver.TargetsChangedListener, + AccessibilityManager.AccessibilityStateChangeListener { private final Context mContext; + private final AccessibilityManager mAccessibilityManager; private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver; @@ -54,13 +57,21 @@ public class AccessibilityFloatingMenuController implements mContext = context; mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver; mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; + mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mAccessibilityButtonModeObserver.addListener(this); mAccessibilityButtonTargetsObserver.addListener(this); mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); - handleFloatingMenuVisibility(mBtnMode, mBtnTargets); + // Accessibility floating menu widget needs accessibility service to work, but system + // accessibility might be unavailable during the phone get booted, hence it needs to wait + // for accessibility manager callback to work. + mAccessibilityManager.addAccessibilityStateChangeListener(this); + if (mAccessibilityManager.isEnabled()) { + handleFloatingMenuVisibility(mBtnMode, mBtnTargets); + mAccessibilityManager.removeAccessibilityStateChangeListener(this); + } } /** @@ -86,6 +97,23 @@ public class AccessibilityFloatingMenuController implements handleFloatingMenuVisibility(mBtnMode, mBtnTargets); } + /** + * Handles visibility of the accessibility floating menu when system accessibility state + * changes. + * If system accessibility become available onAccessibilityStateChanged(true), then we don't + * need to listen to this listener anymore. + * + * @param enabled Whether accessibility is enabled. + */ + @Override + public void onAccessibilityStateChanged(boolean enabled) { + if (enabled) { + handleFloatingMenuVisibility(mBtnMode, mBtnTargets); + } + + mAccessibilityManager.removeAccessibilityStateChangeListener(this); + } + private void handleFloatingMenuVisibility(@AccessibilityButtonMode int mode, String targets) { if (shouldShowFloatingMenu(mode, targets)) { showFloatingMenu(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index 0663f46a00e98..a83f0382ba7bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -24,10 +24,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; @@ -36,8 +38,12 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; +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; /** Test for {@link AccessibilityFloatingMenuController}. */ @RunWith(AndroidTestingRunner.class) @@ -47,9 +53,14 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private static final String TEST_A11Y_BTN_TARGETS = "Magnification"; + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + private AccessibilityFloatingMenuController mController; private AccessibilityButtonTargetsObserver mTargetsObserver; private AccessibilityButtonModeObserver mModeObserver; + @Mock + private AccessibilityManager mMockA11yManager; @Test public void initController_registerListeners() { @@ -59,6 +70,37 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { any(AccessibilityButtonTargetsObserver.TargetsChangedListener.class)); verify(mModeObserver).addListener( any(AccessibilityButtonModeObserver.ModeChangedListener.class)); + verify(mMockA11yManager).addAccessibilityStateChangeListener(any( + AccessibilityManager.AccessibilityStateChangeListener.class)); + } + + @Test + public void initController_accessibilityManagerEnabled_showWidget() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + when(mMockA11yManager.isEnabled()).thenReturn(true); + + mController = setUpController(); + + assertThat(mController.mFloatingMenu).isNotNull(); + verify(mMockA11yManager).removeAccessibilityStateChangeListener(mController); + } + + @Test + public void initController_accessibilityManagerDisabledThenCallbackToEnabled_showWidget() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS); + when(mMockA11yManager.isEnabled()).thenReturn(false); + + mController = setUpController(); + mController.onAccessibilityStateChanged(true); + + assertThat(mController.mFloatingMenu).isNotNull(); + verify(mMockA11yManager).removeAccessibilityStateChangeListener(mController); } @Test @@ -154,7 +196,9 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private AccessibilityFloatingMenuController setUpController() { mTargetsObserver = spy(Dependency.get(AccessibilityButtonTargetsObserver.class)); mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class)); + mContext.addMockSystemService(AccessibilityManager.class, mMockA11yManager); - return new AccessibilityFloatingMenuController(mContext, mTargetsObserver, mModeObserver); + return new AccessibilityFloatingMenuController(mContext, mTargetsObserver, + mModeObserver); } } From 12e09d10179420ac1b3877feca5a1e94578fed43 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Mon, 8 Mar 2021 18:10:35 +0800 Subject: [PATCH 4/6] Set default value of accessibility button mode Set floating menu mode by default, but if the user uses accessibility button in the navigation bar to trigger their accessibility features before migration, then set it to keep the navigation button mode. Cherry picked from commit a87f05d552421aa847a275dad7e9bec809df2d01 Bug: 173940304 Test: 1. Push old version of db and apk 2. Enable/Disable accessibility features in navigation bar 3. Push new version of db and apk Change-Id: I465eb2190f0e1ca9a6a6be928752a07d5e9576da Merged-In: I465eb2190f0e1ca9a6a6be928752a07d5e9576da --- .../SettingsProvider/res/values/defaults.xml | 4 ++ .../providers/settings/SettingsProvider.java | 42 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index d10ff402aa3ae..5d4078d3419b4 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -252,4 +252,8 @@ true + + + 1 + diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 400742ba7d78a..081f3f6737021 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3399,7 +3399,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 198; + private static final int SETTINGS_VERSION = 199; private final int mUserId; @@ -4897,6 +4897,36 @@ public class SettingsProvider extends ContentProvider { currentVersion = 198; } + if (currentVersion == 198) { + // Version 198: Set the default value for accessibility button. If the user + // uses accessibility button in the navigation bar to trigger their + // accessibility features (check if ACCESSIBILITY_BUTTON_TARGETS has value) + // then leave accessibility button mode in the navigation bar, otherwise, set it + // to the floating menu. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting accessibilityButtonMode = secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_BUTTON_MODE); + if (accessibilityButtonMode.isNull()) { + if (isAccessibilityButtonInNavigationBarOn(secureSettings)) { + secureSettings.insertSettingLocked(Secure.ACCESSIBILITY_BUTTON_MODE, + String.valueOf( + Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR), + /*tag= */ null, /* makeDefault= */ false, + SettingsState.SYSTEM_PACKAGE_NAME); + } else { + final int defAccessibilityButtonMode = + getContext().getResources().getInteger( + R.integer.def_accessibility_button_mode); + secureSettings.insertSettingLocked(Secure.ACCESSIBILITY_BUTTON_MODE, + String.valueOf(defAccessibilityButtonMode), /* tag= */ + null, /* makeDefault= */ true, + SettingsState.SYSTEM_PACKAGE_NAME); + } + } + + currentVersion = 199; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { @@ -5075,5 +5105,15 @@ public class SettingsProvider extends ContentProvider { } return items; } + + private boolean isAccessibilityButtonInNavigationBarOn(SettingsState secureSettings) { + final boolean hasValueInA11yBtnTargets = !TextUtils.isEmpty( + secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue()); + final int navigationMode = getContext().getResources().getInteger( + com.android.internal.R.integer.config_navBarInteractionMode); + + return hasValueInA11yBtnTargets && (navigationMode != NAV_BAR_MODE_GESTURAL); + } } } From f20f7a11d2518068e419fdf67911d8eb01924731 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Fri, 12 Mar 2021 17:27:31 +0800 Subject: [PATCH 5/6] Add the string for the accessibility floating menu tooltip Cherry picked from commit 232aa334733badca4567a5d72f9732d6b2e40626 Bug: 182532449 Bug: 175365399 Test: build pass Change-Id: Ie352b1f9274b7f63133d53a1cfe27f6deb4cf00a Merged-In: Ie352b1f9274b7f63133d53a1cfe27f6deb4cf00a --- packages/SystemUI/res/values/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 78180a7a80a85..685896523e361 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2673,6 +2673,12 @@ Switch + + + Accessibility button replaced the accessibility gesture\n\nView settings + + Move button to the edge to hide it temporarily + Device controls From 37efae848a95f911640a7310aab5ea490814bba9 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Wed, 20 Jan 2021 15:57:59 +0800 Subject: [PATCH 6/6] Set to accessibility floating menu mode when navigation mode is gestural cherry picked from commit bc4516eb226f209df62ea60faf4b563cf38261b6 Bug: 173938894 Test: manual test Change-Id: I2cbe690040a7e4ad15c1340a5ee146eb315b1d27 Merged-In: I2cbe690040a7e4ad15c1340a5ee146eb315b1d27 --- .../systemui/navigationbar/NavigationBar.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 06e0e94914d25..9d43e0c133200 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -23,6 +23,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.containsType; @@ -561,6 +562,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); mDeviceProvisionedController.addCallback(mUserSetupListener); + setAccessibilityFloatingMenuModeIfNeeded(); + return barView; } @@ -1405,6 +1408,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, updateSystemUiStateFlags(a11yFlags); } + private void setAccessibilityFloatingMenuModeIfNeeded() { + if (QuickStepContract.isGesturalMode(mNavBarMode)) { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + } + } + public void updateSystemUiStateFlags(int a11yFlags) { if (a11yFlags < 0) { a11yFlags = getA11yButtonState(null); @@ -1462,7 +1472,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, // If accessibility button is floating menu mode, click and long click state should be // disabled. - if (mA11yBtnMode == Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { + if (mA11yBtnMode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { return 0; } @@ -1550,6 +1560,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } updateScreenPinningGestures(); + setAccessibilityFloatingMenuModeIfNeeded(); if (!canShowSecondaryHandle()) { resetSecondaryHandle();