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 2c60295be9

Bug: 173938894
Bug: 173940864
Test: atest AccessibilityButtonModeObserverTest
Change-Id: I03b961c95a20e4698491443012beb3ab73333199
Merged-In: I03b961c95a20e4698491443012beb3ab73333199
This commit is contained in:
jasonwshsu
2020-12-22 10:32:40 +08:00
parent eae3a9e4e3
commit c1e28cc20e
7 changed files with 262 additions and 2 deletions

View File

@@ -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<ModeChangedListener> 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);
}
}
}

View File

@@ -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,

View File

@@ -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<Pip> 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),

View File

@@ -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,

View File

@@ -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());
}
}

View File

@@ -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),

View File

@@ -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,