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 523514fd24
Bug: 173940864
Test: atest AccessibilityButtonTargetsObserverTest AccessibilityButtonModeObserverTest AccessibilityFloatingMenuControllerTest
Change-Id: I07fded55340644bce84a3f17a7a4da950268d2d2
Merged-In: I07fded55340644bce84a3f17a7a4da950268d2d2
This commit is contained in:
@@ -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<IWindowManager> mIWindowManager;
|
||||
@Inject Lazy<OverviewProxyService> mOverviewProxyService;
|
||||
@Inject Lazy<NavigationModeController> mNavBarModeController;
|
||||
@Inject Lazy<AccessibilityButtonModeObserver> mAccessibilityButtonModeObserver;
|
||||
@Inject Lazy<AccessibilityButtonTargetsObserver> mAccessibilityButtonListController;
|
||||
@Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
|
||||
@Inject Lazy<VibratorHelper> mVibratorHelper;
|
||||
@Inject Lazy<IStatusBarService> mIStatusBarService;
|
||||
@@ -294,6 +299,7 @@ public class Dependency {
|
||||
@Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
|
||||
@Inject Lazy<AppOpsController> mAppOpsController;
|
||||
@Inject Lazy<NavigationBarController> mNavigationBarController;
|
||||
@Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
|
||||
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
|
||||
@Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
|
||||
@Inject Lazy<NotificationGroupAlertTransferHelper> 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);
|
||||
|
||||
@@ -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<AccessibilityButtonModeObserver.ModeChangedListener> {
|
||||
|
||||
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<ModeChangedListener> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AccessibilityButtonTargetsObserver.TargetsChangedListener> {
|
||||
|
||||
/** 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();
|
||||
}
|
||||
}
|
||||
@@ -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 <T> The listener type
|
||||
*/
|
||||
public abstract class SecureSettingsContentObserver<T> {
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
@VisibleForTesting
|
||||
final ContentObserver mContentObserver;
|
||||
|
||||
private final String mKey;
|
||||
|
||||
@VisibleForTesting
|
||||
final List<T> 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<AssistManager> assistManagerLazy,
|
||||
ConfigurationController configurationController,
|
||||
NotificationShadeWindowController notificationShadeWindowController,
|
||||
|
||||
@@ -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<AssistManager> assistManagerLazy,
|
||||
ConfigurationController configurationController,
|
||||
NotificationShadeWindowController notificationShadeWindowController,
|
||||
@@ -245,6 +247,7 @@ public interface StatusBarPhoneModule {
|
||||
visualStabilityManager,
|
||||
deviceProvisionedController,
|
||||
navigationBarController,
|
||||
accessibilityFloatingMenuController,
|
||||
assistManagerLazy,
|
||||
configurationController,
|
||||
notificationShadeWindowController,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<Object> {
|
||||
|
||||
protected FakeSecureSettingsContentObserver(Context context,
|
||||
String secureSettingsKey) {
|
||||
super(context, secureSettingsKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onValueChanged(Object listener, String value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user