Show accessibility floating menu after user unlock their phone
* Accessibiltiy floating menu needs to retrieve information from AccessibilityManagerService and it will be ready before receiving onUserUnlocked() * When enter the lockscreen, destroy the accessibility floating menu; when exist the lockscreen, show the accessibility floating menu. Bug: 184272107 Bug: 178285746 Test: atest AccessibilityFloatingMenuControllerTest Change-Id: I052a57296fab2afa243bd6b696bdfdb4235599a3
This commit is contained in:
@@ -20,11 +20,12 @@ 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;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
||||
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
|
||||
import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode;
|
||||
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
|
||||
@@ -37,41 +38,52 @@ import javax.inject.Inject;
|
||||
@SysUISingleton
|
||||
public class AccessibilityFloatingMenuController implements
|
||||
AccessibilityButtonModeObserver.ModeChangedListener,
|
||||
AccessibilityButtonTargetsObserver.TargetsChangedListener,
|
||||
AccessibilityManager.AccessibilityStateChangeListener {
|
||||
AccessibilityButtonTargetsObserver.TargetsChangedListener {
|
||||
|
||||
private final Context mContext;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
|
||||
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
|
||||
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
|
||||
@VisibleForTesting
|
||||
IAccessibilityFloatingMenu mFloatingMenu;
|
||||
private int mBtnMode;
|
||||
private String mBtnTargets;
|
||||
private boolean mIsKeyguardVisible;
|
||||
private boolean mIsAccessibilityManagerServiceReady;
|
||||
|
||||
@VisibleForTesting
|
||||
final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() {
|
||||
// Accessibility floating menu needs to retrieve information from
|
||||
// AccessibilityManagerService, and it would be ready before onUserUnlocked().
|
||||
@Override
|
||||
public void onUserUnlocked() {
|
||||
mIsAccessibilityManagerServiceReady = true;
|
||||
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
|
||||
}
|
||||
|
||||
// Keyguard state would be changed before AccessibilityManagerService is ready to retrieve,
|
||||
// need to wait until receive onUserUnlocked().
|
||||
@Override
|
||||
public void onKeyguardVisibilityChanged(boolean showing) {
|
||||
mIsKeyguardVisible = showing;
|
||||
if (mIsAccessibilityManagerServiceReady) {
|
||||
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Inject
|
||||
public AccessibilityFloatingMenuController(Context context,
|
||||
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
|
||||
AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
|
||||
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor) {
|
||||
mContext = context;
|
||||
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
|
||||
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
|
||||
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
|
||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||
|
||||
mAccessibilityButtonModeObserver.addListener(this);
|
||||
mAccessibilityButtonTargetsObserver.addListener(this);
|
||||
mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
|
||||
mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
|
||||
|
||||
// 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);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +94,7 @@ public class AccessibilityFloatingMenuController implements
|
||||
@Override
|
||||
public void onAccessibilityButtonModeChanged(@AccessibilityButtonMode int mode) {
|
||||
mBtnMode = mode;
|
||||
handleFloatingMenuVisibility(mBtnMode, mBtnTargets);
|
||||
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,27 +106,39 @@ public class AccessibilityFloatingMenuController implements
|
||||
@Override
|
||||
public void onAccessibilityButtonTargetsChanged(String targets) {
|
||||
mBtnTargets = targets;
|
||||
handleFloatingMenuVisibility(mBtnMode, mBtnTargets);
|
||||
handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mIsKeyguardVisible = false;
|
||||
mIsAccessibilityManagerServiceReady = false;
|
||||
mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
|
||||
mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
|
||||
registerContentObservers();
|
||||
}
|
||||
|
||||
private void registerContentObservers() {
|
||||
mAccessibilityButtonModeObserver.addListener(this);
|
||||
mAccessibilityButtonTargetsObserver.addListener(this);
|
||||
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Handles the accessibility floating menu visibility with the given values.
|
||||
*
|
||||
* @param enabled Whether accessibility is enabled.
|
||||
* @param keyguardVisible the keyguard visibility status. Not show the
|
||||
* {@link AccessibilityFloatingMenu} when keyguard appears.
|
||||
* @param mode accessibility button mode {@link AccessibilityButtonMode}
|
||||
* @param targets accessibility button list; it should comes from
|
||||
* {@link android.provider.Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS}.
|
||||
*/
|
||||
@Override
|
||||
public void onAccessibilityStateChanged(boolean enabled) {
|
||||
if (enabled) {
|
||||
handleFloatingMenuVisibility(mBtnMode, mBtnTargets);
|
||||
private void handleFloatingMenuVisibility(boolean keyguardVisible,
|
||||
@AccessibilityButtonMode int mode, String targets) {
|
||||
if (keyguardVisible) {
|
||||
destroyFloatingMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
mAccessibilityManager.removeAccessibilityStateChangeListener(this);
|
||||
}
|
||||
|
||||
private void handleFloatingMenuVisibility(@AccessibilityButtonMode int mode, String targets) {
|
||||
if (shouldShowFloatingMenu(mode, targets)) {
|
||||
showFloatingMenu();
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.util.NotificationMessagingUtil;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.ViewMediatorCallback;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.systemui.Prefs;
|
||||
@@ -263,9 +264,10 @@ public class DependencyProvider {
|
||||
@SysUISingleton
|
||||
public AccessibilityFloatingMenuController provideAccessibilityFloatingMenuController(
|
||||
Context context, AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
|
||||
AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
|
||||
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor) {
|
||||
return new AccessibilityFloatingMenuController(context, accessibilityButtonTargetsObserver,
|
||||
accessibilityButtonModeObserver);
|
||||
accessibilityButtonModeObserver, keyguardUpdateMonitor);
|
||||
}
|
||||
|
||||
/** */
|
||||
|
||||
@@ -24,15 +24,15 @@ 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;
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
|
||||
@@ -41,7 +41,8 @@ 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.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
@@ -56,11 +57,13 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
|
||||
@Rule
|
||||
public MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
private AccessibilityFloatingMenuController mController;
|
||||
private AccessibilityButtonTargetsObserver mTargetsObserver;
|
||||
private AccessibilityButtonModeObserver mModeObserver;
|
||||
@Mock
|
||||
private AccessibilityManager mMockA11yManager;
|
||||
@Captor
|
||||
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
|
||||
private KeyguardUpdateMonitorCallback mKeyguardCallback;
|
||||
|
||||
@Test
|
||||
public void initController_registerListeners() {
|
||||
@@ -70,37 +73,56 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
|
||||
any(AccessibilityButtonTargetsObserver.TargetsChangedListener.class));
|
||||
verify(mModeObserver).addListener(
|
||||
any(AccessibilityButtonModeObserver.ModeChangedListener.class));
|
||||
verify(mMockA11yManager).addAccessibilityStateChangeListener(any(
|
||||
AccessibilityManager.AccessibilityStateChangeListener.class));
|
||||
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.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);
|
||||
|
||||
public void onUserUnlocked_keyguardNotShow_showWidget() {
|
||||
enableAccessibilityFloatingMenuConfig();
|
||||
mController = setUpController();
|
||||
captureKeyguardUpdateMonitorCallback();
|
||||
mKeyguardCallback.onKeyguardVisibilityChanged(false);
|
||||
|
||||
mKeyguardCallback.onUserUnlocked();
|
||||
|
||||
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);
|
||||
|
||||
public void onUserUnlocked_keyguardShowing_destroyWidget() {
|
||||
enableAccessibilityFloatingMenuConfig();
|
||||
mController = setUpController();
|
||||
mController.onAccessibilityStateChanged(true);
|
||||
captureKeyguardUpdateMonitorCallback();
|
||||
mKeyguardCallback.onKeyguardVisibilityChanged(true);
|
||||
|
||||
mKeyguardCallback.onUserUnlocked();
|
||||
|
||||
assertThat(mController.mFloatingMenu).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onKeyguardVisibilityChanged_showing_destroyWidget() {
|
||||
enableAccessibilityFloatingMenuConfig();
|
||||
mController = setUpController();
|
||||
mController.mFloatingMenu = new AccessibilityFloatingMenu(mContext);
|
||||
captureKeyguardUpdateMonitorCallback();
|
||||
mKeyguardCallback.onUserUnlocked();
|
||||
|
||||
mKeyguardCallback.onKeyguardVisibilityChanged(true);
|
||||
|
||||
assertThat(mController.mFloatingMenu).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onKeyguardVisibilityChanged_notShow_showWidget() {
|
||||
enableAccessibilityFloatingMenuConfig();
|
||||
mController = setUpController();
|
||||
captureKeyguardUpdateMonitorCallback();
|
||||
mKeyguardCallback.onUserUnlocked();
|
||||
|
||||
mKeyguardCallback.onKeyguardVisibilityChanged(false);
|
||||
|
||||
assertThat(mController.mFloatingMenu).isNotNull();
|
||||
verify(mMockA11yManager).removeAccessibilityStateChangeListener(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,7 +148,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAccessibilityButtonModeChanged_navBarModeAndHasButtonTargets_showWidget() {
|
||||
public void onAccessibilityButtonModeChanged_navBarModeAndHasButtonTargets_destroyWidget() {
|
||||
Settings.Secure.putString(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS);
|
||||
mController = setUpController();
|
||||
@@ -170,7 +192,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAccessibilityButtonTargetsChanged_navBarModeAndHasButtonTargets_showWidget() {
|
||||
public void onAccessibilityButtonTargetsChanged_navBarModeAndHasButtonTargets_destroyWidget() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
|
||||
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
|
||||
@@ -182,7 +204,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAccessibilityButtonTargetsChanged_buttonModeAndNoButtonTargets_destroyWidget() {
|
||||
public void onAccessibilityButtonTargetsChanged_navBarModeAndNoButtonTargets_destroyWidget() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
|
||||
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
|
||||
@@ -196,9 +218,21 @@ 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);
|
||||
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
|
||||
|
||||
return new AccessibilityFloatingMenuController(mContext, mTargetsObserver,
|
||||
mModeObserver);
|
||||
mModeObserver, mKeyguardUpdateMonitor);
|
||||
}
|
||||
|
||||
private void enableAccessibilityFloatingMenuConfig() {
|
||||
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);
|
||||
}
|
||||
|
||||
private void captureKeyguardUpdateMonitorCallback() {
|
||||
verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardCallbackCaptor.capture());
|
||||
mKeyguardCallback = mKeyguardCallbackCaptor.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user