Merge "[Hearing device shortcut] Set shortcut trigger action to launch hearing aids settings"
This commit is contained in:
@@ -33,6 +33,7 @@ import static android.view.accessibility.AccessibilityManager.CONTRAST_DEFAULT_V
|
||||
import static android.view.accessibility.AccessibilityManager.CONTRAST_NOT_SET;
|
||||
import static android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
|
||||
@@ -132,6 +133,7 @@ import android.view.inputmethod.EditorInfo;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.accessibility.AccessibilityShortcutController;
|
||||
import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkFeatureInfo;
|
||||
import com.android.internal.accessibility.AccessibilityShortcutController.LaunchableFrameworkFeatureInfo;
|
||||
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
|
||||
import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -1810,6 +1812,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private void launchAccessibilitySubSettings(int displayId, ComponentName name) {
|
||||
final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
|
||||
final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, name.flattenToString());
|
||||
try {
|
||||
mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
|
||||
} catch (ActivityNotFoundException ignore) {
|
||||
// ignore the exception
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
|
||||
final AccessibilityUserState state = getCurrentUserStateLocked();
|
||||
mIsAccessibilityButtonShown = available;
|
||||
@@ -3245,7 +3259,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
return;
|
||||
}
|
||||
// In case user assigned an accessibility framework feature to the given shortcut.
|
||||
if (performAccessibilityFrameworkFeature(targetComponentName, shortcutType)) {
|
||||
if (performAccessibilityFrameworkFeature(displayId, targetComponentName, shortcutType)) {
|
||||
return;
|
||||
}
|
||||
// In case user assigned an accessibility shortcut target to the given shortcut.
|
||||
@@ -3260,8 +3274,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget,
|
||||
@ShortcutType int shortcutType) {
|
||||
private boolean performAccessibilityFrameworkFeature(int displayId,
|
||||
ComponentName assignedTarget, @ShortcutType int shortcutType) {
|
||||
final Map<ComponentName, FrameworkFeatureInfo> frameworkFeatureMap =
|
||||
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
|
||||
if (!frameworkFeatureMap.containsKey(assignedTarget)) {
|
||||
@@ -3270,6 +3284,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
final FrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
|
||||
final SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
|
||||
featureInfo.getSettingKey(), mCurrentUserId);
|
||||
|
||||
if (featureInfo instanceof LaunchableFrameworkFeatureInfo) {
|
||||
logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
|
||||
/* serviceEnabled= */ true);
|
||||
launchAccessibilityFrameworkFeature(displayId, assignedTarget);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assuming that the default state will be to have the feature off
|
||||
if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
|
||||
logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
|
||||
@@ -3373,6 +3395,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private void launchAccessibilityFrameworkFeature(int displayId, ComponentName assignedTarget) {
|
||||
if (assignedTarget.equals(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)) {
|
||||
launchAccessibilitySubSettings(displayId, ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
|
||||
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
|
||||
|
||||
@@ -21,10 +21,12 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -51,7 +53,9 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.testing.TestableContext;
|
||||
import android.view.Display;
|
||||
@@ -61,6 +65,7 @@ import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityWindowAttributes;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.compatibility.common.util.TestUtils;
|
||||
@@ -76,12 +81,12 @@ import com.android.server.pm.UserManagerInternal;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
@@ -92,13 +97,16 @@ import java.util.ArrayList;
|
||||
* APCT tests for {@link AccessibilityManagerService}.
|
||||
*/
|
||||
public class AccessibilityManagerServiceTest {
|
||||
private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
|
||||
@Rule
|
||||
public final A11yTestableContext mTestableContext = new A11yTestableContext(
|
||||
ApplicationProvider.getApplicationContext());
|
||||
|
||||
private static final int ACTION_ID = 20;
|
||||
private static final String LABEL = "label";
|
||||
private static final String INTENT_ACTION = "TESTACTION";
|
||||
private static final String DESCRIPTION = "description";
|
||||
private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
|
||||
InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION),
|
||||
ApplicationProvider.getApplicationContext(), 0, new Intent(INTENT_ACTION),
|
||||
PendingIntent.FLAG_MUTABLE_UNAUDITED);
|
||||
private static final RemoteAction TEST_ACTION = new RemoteAction(
|
||||
Icon.createWithContentUri("content://test"),
|
||||
@@ -130,11 +138,7 @@ public class AccessibilityManagerServiceTest {
|
||||
@Mock private MagnificationController mMockMagnificationController;
|
||||
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
|
||||
@Mock private ProxyManager mProxyManager;
|
||||
|
||||
@Rule
|
||||
public final TestableContext mTestableContext = new TestableContext(
|
||||
getInstrumentation().getTargetContext(), null);
|
||||
|
||||
@Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
|
||||
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
|
||||
private AccessibilityServiceConnection mAccessibilityServiceConnection;
|
||||
private AccessibilityInputFilter mInputFilter;
|
||||
@@ -228,11 +232,8 @@ public class AccessibilityManagerServiceTest {
|
||||
doThrow(SecurityException.class).when(mMockSecurityPolicy)
|
||||
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
|
||||
|
||||
try {
|
||||
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
|
||||
Assert.fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID));
|
||||
verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
|
||||
}
|
||||
|
||||
@@ -248,11 +249,8 @@ public class AccessibilityManagerServiceTest {
|
||||
doThrow(SecurityException.class).when(mMockSecurityPolicy)
|
||||
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
|
||||
|
||||
try {
|
||||
mA11yms.unregisterSystemAction(ACTION_ID);
|
||||
Assert.fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mA11yms.unregisterSystemAction(ACTION_ID));
|
||||
verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
|
||||
}
|
||||
|
||||
@@ -290,11 +288,9 @@ public class AccessibilityManagerServiceTest {
|
||||
public void testRegisterProxyWithoutPermission() throws Exception {
|
||||
doThrow(SecurityException.class).when(mMockSecurityPolicy)
|
||||
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
|
||||
try {
|
||||
mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
|
||||
Assert.fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
any(), any(), any(), any());
|
||||
}
|
||||
@@ -302,11 +298,8 @@ public class AccessibilityManagerServiceTest {
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testRegisterProxyForDefaultDisplay() throws Exception {
|
||||
try {
|
||||
mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY);
|
||||
Assert.fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
any(), any(), any(), any());
|
||||
}
|
||||
@@ -314,11 +307,8 @@ public class AccessibilityManagerServiceTest {
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testRegisterProxyForInvalidDisplay() throws Exception {
|
||||
try {
|
||||
mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY);
|
||||
Assert.fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
|
||||
verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
|
||||
any(), any(), any(), any());
|
||||
}
|
||||
@@ -337,11 +327,9 @@ public class AccessibilityManagerServiceTest {
|
||||
public void testUnRegisterProxyWithoutPermission() throws Exception {
|
||||
doThrow(SecurityException.class).when(mMockSecurityPolicy)
|
||||
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
|
||||
try {
|
||||
mA11yms.unregisterProxyForDisplay(TEST_DISPLAY);
|
||||
Assert.fail();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY));
|
||||
verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);
|
||||
}
|
||||
|
||||
@@ -357,8 +345,9 @@ public class AccessibilityManagerServiceTest {
|
||||
|
||||
mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, false);
|
||||
|
||||
Assert.assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
|
||||
userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY));
|
||||
assertThat(userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
|
||||
ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
|
||||
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@@ -376,7 +365,7 @@ public class AccessibilityManagerServiceTest {
|
||||
|
||||
ArgumentCaptor<Display> displayCaptor = ArgumentCaptor.forClass(Display.class);
|
||||
verify(mInputFilter, timeout(100)).refreshMagnificationMode(displayCaptor.capture());
|
||||
Assert.assertEquals(Display.DEFAULT_DISPLAY, displayCaptor.getValue().getDisplayId());
|
||||
assertThat(displayCaptor.getValue().getDisplayId()).isEqualTo(Display.DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@@ -506,10 +495,59 @@ public class AccessibilityManagerServiceTest {
|
||||
attributes);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
|
||||
final AccessibilityUserState userState = mA11yms.mUserStates.get(
|
||||
mA11yms.getCurrentUserIdLocked());
|
||||
mockManageAccessibilityGranted(mTestableContext);
|
||||
userState.mAccessibilityShortcutKeyTargets.add(
|
||||
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
|
||||
|
||||
mA11yms.performAccessibilityShortcut(
|
||||
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
|
||||
mHandler.sendAllMessages();
|
||||
|
||||
assertStartActivityWithExpectedComponentName(mTestableContext.getMockContext(),
|
||||
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
|
||||
}
|
||||
|
||||
private void mockManageAccessibilityGranted(TestableContext context) {
|
||||
context.getTestablePermissions().setPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
|
||||
PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
private void assertStartActivityWithExpectedComponentName(Context mockContext,
|
||||
String componentName) {
|
||||
verify(mockContext).startActivityAsUser(mIntentArgumentCaptor.capture(),
|
||||
any(Bundle.class), any(UserHandle.class));
|
||||
assertThat(mIntentArgumentCaptor.getValue().getStringExtra(
|
||||
Intent.EXTRA_COMPONENT_NAME)).isEqualTo(componentName);
|
||||
}
|
||||
|
||||
public static class FakeInputFilter extends AccessibilityInputFilter {
|
||||
FakeInputFilter(Context context,
|
||||
AccessibilityManagerService service) {
|
||||
super(context, service);
|
||||
}
|
||||
}
|
||||
|
||||
private static class A11yTestableContext extends TestableContext {
|
||||
|
||||
private final Context mMockContext;
|
||||
|
||||
A11yTestableContext(Context base) {
|
||||
super(base);
|
||||
mMockContext = Mockito.mock(Context.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
|
||||
mMockContext.startActivityAsUser(intent, options, user);
|
||||
}
|
||||
|
||||
Context getMockContext() {
|
||||
return mMockContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user