blob: ace4341ce16584437be8d0e9db015d860f821f4c [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.accessibility.cts.common;
import static android.accessibility.cts.common.InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE;
import static android.accessibility.cts.common.ServiceControlUtils.waitForConditionWithServiceStateChange;
import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import android.app.Instrumentation;
import android.app.Service;
import android.app.UiAutomation;
import android.content.ContentResolver;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityManager;
import androidx.test.InstrumentationRegistry;
import org.junit.rules.ExternalResource;
import java.util.List;
/**
* JUnit rule used to restore Accessibility Shortcut Settings after the test. It also provides
* utils to update shortcut targets to Accessibility Shortcut Settings.
*/
public final class AccessibilityShortcutSettingsRule extends ExternalResource {
public static final int ACCESSIBILITY_BUTTON = 0;
public static final int ACCESSIBILITY_SHORTCUT_KEY = 1;
private static final String ACCESSIBILITY_BUTTON_TARGETS =
"accessibility_button_targets";
private static final char DELIMITER = ':';
private final Instrumentation mInstrumentation;
private final ContentResolver mContentResolver;
private final AccessibilityManager mAccessibilityManager;
// These are the shortcut settings before the tests. Roll back them after the tests.
private String mA11yShortcutTargetSetting;
private String mA11yButtonTargetSetting;
public AccessibilityShortcutSettingsRule() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mContentResolver = mInstrumentation.getContext().getContentResolver();
mAccessibilityManager = (AccessibilityManager) mInstrumentation.getContext()
.getSystemService(Service.ACCESSIBILITY_SERVICE);
}
@Override
protected void before() {
// Read current settings
mA11yShortcutTargetSetting = Settings.Secure.getString(mContentResolver,
ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mA11yButtonTargetSetting = Settings.Secure.getString(mContentResolver,
ACCESSIBILITY_BUTTON_TARGETS);
}
@Override
protected void after() {
// Rollback all settings
final UiAutomation uiAutomation = mInstrumentation.getUiAutomation(
FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
updateShortcutSetting(uiAutomation, ACCESSIBILITY_SHORTCUT_KEY, mA11yShortcutTargetSetting);
updateShortcutSetting(uiAutomation, ACCESSIBILITY_BUTTON, mA11yButtonTargetSetting);
}
/**
* Updates shortcut targets to accessibility shortcut.
*
* @param uiAutomation The UiAutomation.
* @param newUseShortcutList The component names which use the shortcut.
* @return true if the new targets updated.
*/
public boolean configureAccessibilityShortcut(UiAutomation uiAutomation,
String ... newUseShortcutList) {
final String useShortcutList = getComponentIdString(newUseShortcutList);
return updateShortcutSetting(uiAutomation, ACCESSIBILITY_SHORTCUT_KEY, useShortcutList);
}
/**
* Updates shortcut targets to accessibility button.
*
* @param uiAutomation The UiAutomation.
* @param newUseShortcutList The component names which use the shortcut.
* @return true if the new targets updated.
*/
public boolean configureAccessibilityButton(UiAutomation uiAutomation,
String ... newUseShortcutList) {
final String useShortcutList = getComponentIdString(newUseShortcutList);
return updateShortcutSetting(uiAutomation, ACCESSIBILITY_BUTTON, useShortcutList);
}
/**
* Waits for the ACCESSIBILITY_SHORTCUT_KEY state changed, and gets current shortcut list which
* is the same with expected one.
*
* @param uiAutomation The UiAutomation.
* @param expectedList The expected shortcut targets returned from
* {@link AccessibilityManager#getAccessibilityShortcutTargets(int)}.
*/
public void waitForAccessibilityShortcutStateChange(UiAutomation uiAutomation,
List<String> expectedList) {
waitForShortcutStateChange(uiAutomation, ACCESSIBILITY_SHORTCUT_KEY, expectedList);
}
/**
* Waits for the ACCESSIBILITY_BUTTON state changed, and gets current shortcut list which
* is the same with expected one.
*
* @param uiAutomation The UiAutomation.
* @param expectedList The expected shortcut targets returned from
* {@link AccessibilityManager#getAccessibilityShortcutTargets(int)}.
*/
public void waitForAccessibilityButtonStateChange(UiAutomation uiAutomation,
List<String> expectedList) {
waitForShortcutStateChange(uiAutomation, ACCESSIBILITY_BUTTON, expectedList);
}
/**
* Returns a colon-separated component name string by given string array.
*
* @param componentIds The array of component names.
* @return A colon-separated component name string.
*/
private String getComponentIdString(String ... componentIds) {
final StringBuilder stringBuilder = new StringBuilder();
for (String componentId : componentIds) {
if (TextUtils.isEmpty(componentId)) {
continue;
}
if (stringBuilder.length() != 0) {
stringBuilder.append(DELIMITER);
}
stringBuilder.append(componentId);
}
return stringBuilder.toString();
}
/**
* Updates the setting key of the accessibility shortcut.
*
* @param uiAutomation The uiautomation instance.
* @param shortcutType The shortcut type.
* @param newUseShortcutList The value of ACCESSIBILITY_SHORTCUT_TARGET_SERVICE
* @return True if setting is updated.
*/
private boolean updateShortcutSetting(UiAutomation uiAutomation, int shortcutType,
String newUseShortcutList) {
final String settingName = shortcutTypeToSettingName(shortcutType);
if (TextUtils.isEmpty(settingName)) {
return false;
}
final ShellCommandBuilder command = ShellCommandBuilder.create(uiAutomation);
final String useShortcutList = Settings.Secure.getString(mContentResolver, settingName);
boolean changes = false;
if (!TextUtils.equals(useShortcutList, newUseShortcutList)) {
if (TextUtils.isEmpty(newUseShortcutList)) {
command.deleteSecureSetting(settingName);
} else {
command.putSecureSetting(settingName, newUseShortcutList);
}
changes = true;
}
if (changes) {
command.run();
}
return changes;
}
private String shortcutTypeToSettingName(int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON: return ACCESSIBILITY_BUTTON_TARGETS;
case ACCESSIBILITY_SHORTCUT_KEY: return ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
default: return "";
}
}
/**
* Waits for the shortcut state changed, and gets current shortcut list is the same with
* expected one.
*
* @param uiAutomation The UiAutomation.
* @param shortcutType The shortcut type.
* @param expectedList The expected shortcut targets returned from
* {@link AccessibilityManager#getAccessibilityShortcutTargets(int)}.
*/
private void waitForShortcutStateChange(UiAutomation uiAutomation, int shortcutType,
List<String> expectedList) {
final StringBuilder message = new StringBuilder();
message.append(shortcutTypeToString(shortcutType));
message.append(" expect:").append(expectedList);
runWithShellPermissionIdentity(uiAutomation, () ->
waitForConditionWithServiceStateChange(mInstrumentation.getContext(), () -> {
final List<String> currentShortcuts =
mAccessibilityManager.getAccessibilityShortcutTargets(shortcutType);
if (currentShortcuts.size() != expectedList.size()) {
return false;
}
for (String expect : expectedList) {
if (!currentShortcuts.contains(expect)) {
return false;
}
}
return true;
}, TIMEOUT_SERVICE_ENABLE, message.toString()));
}
private String shortcutTypeToString(int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON: return "ACCESSIBILITY_BUTTON";
case ACCESSIBILITY_SHORTCUT_KEY: return "ACCESSIBILITY_SHORTCUT_KEY";
default: return "";
}
}
}