Merge "AccessibilityMenuService test class setup & initial test." into udc-dev

This commit is contained in:
Riley Jones
2023-03-06 20:13:03 +00:00
committed by Android (Google) Code Review
9 changed files with 356 additions and 7 deletions

View File

@@ -18,6 +18,15 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
// This filegroup is used by menu tests.
filegroup {
name: "AccessibilityMenuSource",
srcs: [
"src/**/AccessibilityMenuService.java",
"src/**/A11yMenuShortcut.java",
],
}
android_app {
name: "AccessibilityMenu",

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/shortcutItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/grid_item_padding"

View File

@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.accessibilitymenu;
import android.Manifest;
import android.accessibilityservice.AccessibilityButtonController;
import android.accessibilityservice.AccessibilityService;
import android.content.BroadcastReceiver;
@@ -51,8 +52,12 @@ import java.util.List;
/** @hide */
public class AccessibilityMenuService extends AccessibilityService
implements View.OnTouchListener {
private static final String TAG = "A11yMenuService";
public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();
public static final String INTENT_TOGGLE_MENU = ".toggle_menu";
public static final String INTENT_HIDE_MENU = ".hide_menu";
private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
@@ -74,7 +79,8 @@ public class AccessibilityMenuService extends AccessibilityService
// TODO(b/136716947): Support multi-display once a11y framework side is ready.
private DisplayManager mDisplayManager;
final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
int mRotation;
@Override
@@ -95,13 +101,20 @@ public class AccessibilityMenuService extends AccessibilityService
}
};
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
private final BroadcastReceiver mHideMenuReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mA11yMenuLayout.hideMenu();
}
};
private final BroadcastReceiver mToggleMenuReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mA11yMenuLayout.toggleVisibility();
}
};
/**
* Update a11y menu layout when large button setting is changed.
*/
@@ -172,7 +185,19 @@ public class AccessibilityMenuService extends AccessibilityService
protected void onServiceConnected() {
mA11yMenuLayout = new A11yMenuOverlayLayout(this);
registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
IntentFilter hideMenuFilter = new IntentFilter();
hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF);
hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU);
// Including WRITE_SECURE_SETTINGS enforces that we only listen to apps
// with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent.
registerReceiver(mHideMenuReceiver, hideMenuFilter,
Manifest.permission.WRITE_SECURE_SETTINGS, null,
Context.RECEIVER_EXPORTED);
registerReceiver(mToggleMenuReceiver,
new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU),
Manifest.permission.WRITE_SECURE_SETTINGS, null,
Context.RECEIVER_EXPORTED);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mPrefs.registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
@@ -260,7 +285,8 @@ public class AccessibilityMenuService extends AccessibilityService
* @param increment The increment amount in gamma-space
*/
private void adjustBrightness(int increment) {
BrightnessInfo info = getDisplay().getBrightnessInfo();
Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
BrightnessInfo info = display.getBrightnessInfo();
int gamma = BrightnessUtils.convertLinearToGammaFloat(
info.brightness,
info.brightnessMinimum,
@@ -275,7 +301,7 @@ public class AccessibilityMenuService extends AccessibilityService
info.brightnessMinimum,
info.brightnessMaximum
);
mDisplayManager.setBrightness(getDisplayId(), brightness);
mDisplayManager.setBrightness(display.getDisplayId(), brightness);
mA11yMenuLayout.showSnackbar(
getString(R.string.brightness_percentage_label,
(gamma / (BrightnessUtils.GAMMA_SPACE_MAX / 100))));
@@ -310,7 +336,8 @@ public class AccessibilityMenuService extends AccessibilityService
@Override
public boolean onUnbind(Intent intent) {
unregisterReceiver(mBroadcastReceiver);
unregisterReceiver(mHideMenuReceiver);
unregisterReceiver(mToggleMenuReceiver);
mPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
sInitialized = false;
return super.onUnbind(intent);

View File

@@ -21,6 +21,7 @@ import android.view.LayoutInflater;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.TextView;
@@ -146,6 +147,15 @@ public class A11yMenuAdapter extends BaseAdapter {
shortcutIconButton.setBackground(
mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor));
shortcutIconButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(
View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setUniqueId(host.getTag().toString());
}
});
}
}
}

View File

@@ -0,0 +1,43 @@
//
// Copyright (C) 2023 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 {
default_applicable_licenses: ["Android-Apache-2.0"],
}
android_test {
name: "AccessibilityMenuServiceTests",
certificate: "platform",
libs: [
"android.test.runner",
"android.test.base",
],
static_libs: [
"androidx.test.core",
"androidx.test.runner",
"androidx.test.ext.junit",
"compatibility-device-util-axt",
"platform-test-annotations",
"truth-prebuilt",
],
srcs: [
"src/**/*.java",
":AccessibilityMenuSource",
],
platform_apis: true,
test_suites: ["device-tests"],
instrumentation_for: "AccessibilityMenu",
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.systemui.accessibility.accessibilitymenu.tests">
<!-- Needed to write to Settings.Secure to enable and disable the service under test. -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.systemui.accessibility.accessibilitymenu.tests"
android:label="AccessibilityMenu Test Cases">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<configuration description="Runs AccessibilityMenu Test Cases.">
<option name="test-tag" value="AccessibilityMenuServiceTests" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="AccessibilityMenuServiceTests.apk" />
<option name="aapt-version" value="AAPT2" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.systemui.accessibility.accessibilitymenu.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
</configuration>

View File

@@ -0,0 +1,15 @@
{
"presubmit": [
{
"name": "AccessibilityMenuServiceTests",
"options": [
{
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
"exclude-annotation": "android.support.test.filters.FlakyTest"
}
]
}
]
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2023 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.accessibilitymenu.tests;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.TestUtils;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class AccessibilityMenuServiceTest {
private static final String TAG = "A11yMenuServiceTest";
private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
private static final int TIMEOUT_UI_CHANGE_S = 5;
private static Instrumentation sInstrumentation;
private static UiAutomation sUiAutomation;
private static AccessibilityManager sAccessibilityManager;
@BeforeClass
public static void classSetup() throws Throwable {
final String serviceName = PACKAGE_NAME + "/.AccessibilityMenuService";
sInstrumentation = InstrumentationRegistry.getInstrumentation();
sUiAutomation = sInstrumentation.getUiAutomation(
UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
final Context context = sInstrumentation.getContext();
sAccessibilityManager = context.getSystemService(AccessibilityManager.class);
// Disable all a11yServices if any are active.
if (!sAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
Settings.Secure.putString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
TestUtils.waitUntil("Failed to disable all services",
TIMEOUT_SERVICE_STATUS_CHANGE_S,
() -> sAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty());
}
// Enable a11yMenu service.
Settings.Secure.putString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, serviceName);
TestUtils.waitUntil("Failed to enable service",
TIMEOUT_SERVICE_STATUS_CHANGE_S,
() -> sAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
info -> info.getId().contains(serviceName)).count() == 1);
}
@AfterClass
public static void classTeardown() throws Throwable {
Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
}
private boolean isMenuVisible() {
return sUiAutomation.getRootInActiveWindow() != null
&& sUiAutomation.getRootInActiveWindow().getPackageName().toString().equals(
PACKAGE_NAME);
}
private void openMenu() throws Throwable {
if (isMenuVisible()) {
return;
}
Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);
sInstrumentation.getContext().sendBroadcast(intent);
TestUtils.waitUntil("Timed out before menu could appear.",
TIMEOUT_UI_CHANGE_S, () -> isMenuVisible());
}
private void closeMenu() throws Throwable {
if (!isMenuVisible()) {
return;
}
Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU);
sInstrumentation.getContext().sendBroadcast(intent);
TestUtils.waitUntil("Timed out before menu could close.",
TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
}
private List<AccessibilityNodeInfo> getGridButtonList() {
return sUiAutomation.getRootInActiveWindow()
.findAccessibilityNodeInfosByViewId(PACKAGE_NAME + ":id/shortcutIconBtn");
}
private AccessibilityNodeInfo findGridButtonInfo(
List<AccessibilityNodeInfo> buttons, String text) {
for (AccessibilityNodeInfo button: buttons) {
if (button.getUniqueId().equals(text)) {
return button;
}
}
return null;
}
@Test
public void testAdjustBrightness() throws Throwable {
openMenu();
Context context = sInstrumentation.getTargetContext();
DisplayManager displayManager = context.getSystemService(
DisplayManager.class);
float resetBrightness = displayManager.getBrightness(context.getDisplayId());
List<AccessibilityNodeInfo> buttons = getGridButtonList();
AccessibilityNodeInfo brightnessUpButton = findGridButtonInfo(buttons,
String.valueOf(ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()));
AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,
String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()));
int clickId = AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.getId();
BrightnessInfo brightnessInfo = displayManager.getDisplay(
context.getDisplayId()).getBrightnessInfo();
try {
displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMinimum);
TestUtils.waitUntil("Could not change to minimum brightness",
TIMEOUT_UI_CHANGE_S,
() -> displayManager.getBrightness(context.getDisplayId())
== brightnessInfo.brightnessMinimum);
brightnessUpButton.performAction(clickId);
TestUtils.waitUntil("Did not detect an increase in brightness.",
TIMEOUT_UI_CHANGE_S,
() -> displayManager.getBrightness(context.getDisplayId())
> brightnessInfo.brightnessMinimum);
displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMaximum);
TestUtils.waitUntil("Could not change to maximum brightness",
TIMEOUT_UI_CHANGE_S,
() -> displayManager.getBrightness(context.getDisplayId())
== brightnessInfo.brightnessMaximum);
brightnessDownButton.performAction(clickId);
TestUtils.waitUntil("Did not detect a decrease in brightness.",
TIMEOUT_UI_CHANGE_S,
() -> displayManager.getBrightness(context.getDisplayId())
< brightnessInfo.brightnessMaximum);
} finally {
displayManager.setBrightness(context.getDisplayId(), resetBrightness);
closeMenu();
}
}
}