Migrates android semanitcs integration to integration test (#127128)

I think the flake is due to setclipboard or semantics update race condition. I migrated the test to use integration test package which relies less on timing

fixes https://github.com/flutter/flutter/issues/124636
This commit is contained in:
chunhtai 2023-05-23 15:33:19 -07:00 committed by GitHub
parent 8c086d0171
commit c687dcd56f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 979 additions and 766 deletions

View File

@ -0,0 +1,79 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' as io;
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
String adbPath() {
final String? androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT'];
if (androidHome == null) {
return 'adb';
} else {
return path.join(androidHome, 'platform-tools', 'adb');
}
}
Future<Version> getTalkbackVersion() async {
final io.ProcessResult result = await io.Process.run(adbPath(), const <String>[
'shell',
'dumpsys',
'package',
'com.google.android.marvin.talkback',
]);
if (result.exitCode != 0) {
throw Exception('Failed to get TalkBack version: ${result.stdout as String}\n${result.stderr as String}');
}
final List<String> lines = (result.stdout as String).split('\n');
String? version;
for (final String line in lines) {
if (line.contains('versionName')) {
version = line.replaceAll(RegExp(r'\s*versionName='), '');
break;
}
}
if (version == null) {
throw Exception('Unable to determine TalkBack version.');
}
// Android doesn't quite use semver, so convert the version string to semver form.
final RegExp startVersion = RegExp(r'(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(\.(?<build>\d+))?');
final RegExpMatch? match = startVersion.firstMatch(version);
if (match == null) {
return Version(0, 0, 0);
}
return Version(
int.parse(match.namedGroup('major')!),
int.parse(match.namedGroup('minor')!),
int.parse(match.namedGroup('patch')!),
build: match.namedGroup('build'),
);
}
Future<void> enableTalkBack() async {
final io.Process run = await io.Process.start(adbPath(), const <String>[
'shell',
'settings',
'put',
'secure',
'enabled_accessibility_services',
'com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService',
]);
await run.exitCode;
print('TalkBack version is ${await getTalkbackVersion()}');
}
Future<void> disableTalkBack() async {
final io.Process run = await io.Process.start(adbPath(), const <String>[
'shell',
'settings',
'put',
'secure',
'enabled_accessibility_services',
'null',
]);
await run.exitCode;
}

View File

@ -4,6 +4,7 @@
import '../framework/devices.dart';
import '../framework/framework.dart';
import '../framework/talkback.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
@ -74,9 +75,10 @@ TaskFunction createHybridAndroidViewsIntegrationTest() {
}
TaskFunction createAndroidSemanticsIntegrationTest() {
return DriverTest(
return IntegrationTest(
'${flutterDirectory.path}/dev/integration_tests/android_semantics_testing',
'lib/main.dart',
'integration_test/main_test.dart',
withTalkBack: true,
).call;
}
@ -213,6 +215,7 @@ class IntegrationTest {
this.testTarget, {
this.extraOptions = const <String>[],
this.createPlatforms = const <String>[],
this.withTalkBack = false,
}
);
@ -220,6 +223,7 @@ class IntegrationTest {
final String testTarget;
final List<String> extraOptions;
final List<String> createPlatforms;
final bool withTalkBack;
Future<TaskResult> call() {
return inDirectory<TaskResult>(testDirectory, () async {
@ -237,6 +241,13 @@ class IntegrationTest {
]);
}
if (withTalkBack) {
if (device is! AndroidDevice) {
return TaskResult.failure('A test that enables TalkBack can only be run on Android devices');
}
await enableTalkBack();
}
final List<String> options = <String>[
'-v',
'-d',
@ -246,6 +257,10 @@ class IntegrationTest {
];
await flutter('test', options: options);
if (withTalkBack) {
await disableTalkBack();
}
return TaskResult.success(null);
});
}

View File

@ -20,7 +20,6 @@ import android.content.ClipData;
import android.content.Context;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.android.FlutterView;
@ -123,11 +122,60 @@ public class MainActivity extends FlutterActivity {
if (actionList.size() > 0) {
ArrayList<Integer> actions = new ArrayList<>();
for (AccessibilityNodeInfo.AccessibilityAction action : actionList) {
actions.add(action.getId());
if (kIdByAction.containsKey(action)) {
actions.add(kIdByAction.get(action).intValue());
}
}
result.put("actions", actions);
}
return result;
}
}
// These indices need to be in sync with android_semantics_testing/lib/src/constants.dart
static final int kFocusIndex = 1 << 0;
static final int kClearFocusIndex = 1 << 1;
static final int kSelectIndex = 1 << 2;
static final int kClearSelectionIndex = 1 << 3;
static final int kClickIndex = 1 << 4;
static final int kLongClickIndex = 1 << 5;
static final int kAccessibilityFocusIndex = 1 << 6;
static final int kClearAccessibilityFocusIndex = 1 << 7;
static final int kNextAtMovementGranularityIndex = 1 << 8;
static final int kPreviousAtMovementGranularityIndex = 1 << 9;
static final int kNextHtmlElementIndex = 1 << 10;
static final int kPreviousHtmlElementIndex = 1 << 11;
static final int kScrollForwardIndex = 1 << 12;
static final int kScrollBackwardIndex = 1 << 13;
static final int kCutIndex = 1 << 14;
static final int kCopyIndex = 1 << 15;
static final int kPasteIndex = 1 << 16;
static final int kSetSelectionIndex = 1 << 17;
static final int kExpandIndex = 1 << 18;
static final int kCollapseIndex = 1 << 19;
static final int kSetText = 1 << 21;
static final Map<AccessibilityNodeInfo.AccessibilityAction, Integer> kIdByAction = new HashMap<AccessibilityNodeInfo.AccessibilityAction, Integer>() {{
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_FOCUS, kFocusIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_FOCUS, kClearFocusIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SELECT, kSelectIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_SELECTION, kClearSelectionIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK, kClickIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK, kLongClickIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS, kAccessibilityFocusIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS, kClearAccessibilityFocusIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, kNextAtMovementGranularityIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, kPreviousAtMovementGranularityIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_HTML_ELEMENT, kNextHtmlElementIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT, kPreviousHtmlElementIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD, kScrollForwardIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD, kScrollBackwardIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_CUT, kCutIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_COPY, kCopyIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_PASTE, kPasteIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_SELECTION, kSetSelectionIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND, kExpandIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE, kCollapseIndex);
put(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT, kSetText);
}};
}

View File

@ -18,11 +18,16 @@ androidx.lifecycle:lifecycle-runtime:2.2.0=debugAndroidTestCompileClasspath,debu
androidx.lifecycle:lifecycle-viewmodel:2.1.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.savedstate:savedstate:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test.espresso:espresso-core:3.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test.espresso:espresso-idling-resource:3.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test:monitor:1.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test:rules:1.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test:runner:1.2.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.tracing:tracing:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.window:window-java:1.0.0-beta03=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.window:window:1.0.0-beta03=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.window:window-java:1.0.0-beta04=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.window:window:1.0.0-beta04=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.android.tools.analytics-library:protos:27.1.3=lintClassPath
com.android.tools.analytics-library:shared:27.1.3=lintClassPath
com.android.tools.analytics-library:tracker:27.1.3=lintClassPath
@ -54,16 +59,19 @@ com.android.tools:sdk-common:27.1.3=lintClassPath
com.android.tools:sdklib:27.1.3=lintClassPath
com.android:signflinger:4.1.3=lintClassPath
com.android:zipflinger:4.1.3=lintClassPath
com.google.code.findbugs:jsr305:3.0.2=lintClassPath
com.google.code.findbugs:jsr305:2.0.1=debugAndroidTestCompileClasspath
com.google.code.findbugs:jsr305:3.0.2=debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.code.gson:gson:2.8.5=lintClassPath
com.google.errorprone:error_prone_annotations:2.3.2=lintClassPath
com.google.guava:failureaccess:1.0.1=lintClassPath
com.google.errorprone:error_prone_annotations:2.3.2=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
com.google.guava:failureaccess:1.0.1=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
com.google.guava:guava:28.1-android=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
com.google.guava:guava:28.1-jre=lintClassPath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=lintClassPath
com.google.j2objc:j2objc-annotations:1.3=lintClassPath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
com.google.j2objc:j2objc-annotations:1.3=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
com.google.jimfs:jimfs:1.1=lintClassPath
com.google.protobuf:protobuf-java:3.10.0=lintClassPath
com.googlecode.json-simple:json-simple:1.1=lintClassPath
com.squareup:javawriter:2.1.1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.squareup:javawriter:2.5.0=lintClassPath
com.sun.activation:javax.activation:1.2.0=lintClassPath
com.sun.istack:istack-commons-runtime:3.0.7=lintClassPath
@ -72,21 +80,26 @@ commons-codec:commons-codec:1.10=lintClassPath
commons-logging:commons-logging:1.2=lintClassPath
it.unimi.dsi:fastutil:7.2.0=lintClassPath
javax.activation:javax.activation-api:1.2.0=lintClassPath
javax.inject:javax.inject:1=lintClassPath
javax.inject:javax.inject:1=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
javax.xml.bind:jaxb-api:2.3.1=lintClassPath
junit:junit:4.12=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
net.sf.jopt-simple:jopt-simple:4.9=lintClassPath
net.sf.kxml:kxml2:2.3.0=lintClassPath
net.sf.kxml:kxml2:2.3.0=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.apache.commons:commons-compress:1.12=lintClassPath
org.apache.httpcomponents:httpclient:4.5.6=lintClassPath
org.apache.httpcomponents:httpcore:4.4.10=lintClassPath
org.apache.httpcomponents:httpmime:4.5.6=lintClassPath
org.bouncycastle:bcpkix-jdk15on:1.56=lintClassPath
org.bouncycastle:bcprov-jdk15on:1.56=lintClassPath
org.checkerframework:checker-compat-qual:2.5.5=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
org.checkerframework:checker-qual:2.8.1=lintClassPath
org.codehaus.groovy:groovy-all:2.4.15=lintClassPath
org.codehaus.mojo:animal-sniffer-annotations:1.18=lintClassPath
org.codehaus.mojo:animal-sniffer-annotations:1.18=debugRuntimeClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath
org.glassfish.jaxb:jaxb-runtime:2.3.1=lintClassPath
org.glassfish.jaxb:txw2:2.3.1=lintClassPath
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.hamcrest:hamcrest-integration:1.3=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-reflect:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=debugAndroidTestCompileClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath

View File

@ -0,0 +1,122 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
androidx.activity:activity:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.annotation:annotation-experimental:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.annotation:annotation:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.arch.core:core-common:2.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.arch.core:core-runtime:2.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.collection:collection:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.core:core:1.6.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.customview:customview:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.fragment:fragment:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.lifecycle:lifecycle-common-java8:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.lifecycle:lifecycle-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core:2.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.lifecycle:lifecycle-livedata:2.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.lifecycle:lifecycle-runtime:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel:2.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.savedstate:savedstate:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test.espresso:espresso-core:3.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test.espresso:espresso-idling-resource:3.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test:monitor:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test:rules:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.test:runner:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.tracing:tracing:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.window:window-java:1.0.0-beta04=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
androidx.window:window:1.0.0-beta04=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.android.tools.analytics-library:protos:27.1.3=lintClassPath
com.android.tools.analytics-library:shared:27.1.3=lintClassPath
com.android.tools.analytics-library:tracker:27.1.3=lintClassPath
com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524=lintClassPath
com.android.tools.build:aapt2:4.1.3-6503028=_internal_aapt2_binary
com.android.tools.build:apksig:4.1.3=lintClassPath
com.android.tools.build:apkzlib:4.1.3=lintClassPath
com.android.tools.build:builder-model:4.1.3=lintClassPath
com.android.tools.build:builder-test-api:4.1.3=lintClassPath
com.android.tools.build:builder:4.1.3=lintClassPath
com.android.tools.build:gradle-api:4.1.3=lintClassPath
com.android.tools.build:manifest-merger:27.1.3=lintClassPath
com.android.tools.ddms:ddmlib:27.1.3=lintClassPath
com.android.tools.external.com-intellij:intellij-core:27.1.3=lintClassPath
com.android.tools.external.com-intellij:kotlin-compiler:27.1.3=lintClassPath
com.android.tools.external.org-jetbrains:uast:27.1.3=lintClassPath
com.android.tools.layoutlib:layoutlib-api:27.1.3=lintClassPath
com.android.tools.lint:lint-api:27.1.3=lintClassPath
com.android.tools.lint:lint-checks:27.1.3=lintClassPath
com.android.tools.lint:lint-gradle-api:27.1.3=lintClassPath
com.android.tools.lint:lint-gradle:27.1.3=lintClassPath
com.android.tools.lint:lint-model:27.1.3=lintClassPath
com.android.tools.lint:lint:27.1.3=lintClassPath
com.android.tools:annotations:27.1.3=lintClassPath
com.android.tools:common:27.1.3=lintClassPath
com.android.tools:dvlib:27.1.3=lintClassPath
com.android.tools:repository:27.1.3=lintClassPath
com.android.tools:sdk-common:27.1.3=lintClassPath
com.android.tools:sdklib:27.1.3=lintClassPath
com.android:signflinger:4.1.3=lintClassPath
com.android:zipflinger:4.1.3=lintClassPath
com.google.code.findbugs:jsr305:3.0.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.code.gson:gson:2.8.5=lintClassPath
com.google.errorprone:error_prone_annotations:2.3.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.guava:failureaccess:1.0.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.guava:guava:28.1-android=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.guava:guava:28.1-jre=lintClassPath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.j2objc:j2objc-annotations:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.google.jimfs:jimfs:1.1=lintClassPath
com.google.protobuf:protobuf-java:3.10.0=lintClassPath
com.googlecode.json-simple:json-simple:1.1=lintClassPath
com.squareup:javawriter:2.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
com.squareup:javawriter:2.5.0=lintClassPath
com.sun.activation:javax.activation:1.2.0=lintClassPath
com.sun.istack:istack-commons-runtime:3.0.7=lintClassPath
com.sun.xml.fastinfoset:FastInfoset:1.2.15=lintClassPath
commons-codec:commons-codec:1.10=lintClassPath
commons-logging:commons-logging:1.2=lintClassPath
it.unimi.dsi:fastutil:7.2.0=lintClassPath
javax.activation:javax.activation-api:1.2.0=lintClassPath
javax.inject:javax.inject:1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
javax.xml.bind:jaxb-api:2.3.1=lintClassPath
junit:junit:4.12=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
net.sf.jopt-simple:jopt-simple:4.9=lintClassPath
net.sf.kxml:kxml2:2.3.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.apache.commons:commons-compress:1.12=lintClassPath
org.apache.httpcomponents:httpclient:4.5.6=lintClassPath
org.apache.httpcomponents:httpcore:4.4.10=lintClassPath
org.apache.httpcomponents:httpmime:4.5.6=lintClassPath
org.bouncycastle:bcpkix-jdk15on:1.56=lintClassPath
org.bouncycastle:bcprov-jdk15on:1.56=lintClassPath
org.checkerframework:checker-compat-qual:2.5.5=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.checkerframework:checker-qual:2.8.1=lintClassPath
org.codehaus.groovy:groovy-all:2.4.15=lintClassPath
org.codehaus.mojo:animal-sniffer-annotations:1.18=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.glassfish.jaxb:jaxb-runtime:2.3.1=lintClassPath
org.glassfish.jaxb:txw2:2.3.1=lintClassPath
org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.hamcrest:hamcrest-integration:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-reflect:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.30=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:1.3.72=lintClassPath
org.jetbrains.kotlin:kotlin-stdlib:1.5.31=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jetbrains.trove4j:trove4j:20160824=lintClassPath
org.jetbrains:annotations:13.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath
org.jvnet.staxex:stax-ex:1.8=lintClassPath
org.ow2.asm:asm-analysis:7.0=lintClassPath
org.ow2.asm:asm-commons:7.0=lintClassPath
org.ow2.asm:asm-tree:7.0=lintClassPath
org.ow2.asm:asm-util:7.0=lintClassPath
org.ow2.asm:asm:7.0=lintClassPath
empty=androidApis,androidTestUtil,compile,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAnnotationProcessorClasspath,debugUnitTestAnnotationProcessorClasspath,lintChecks,lintPublish,profileAnnotationProcessorClasspath,profileUnitTestAnnotationProcessorClasspath,releaseAnnotationProcessorClasspath,releaseUnitTestAnnotationProcessorClasspath,testCompile

View File

@ -0,0 +1,677 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:android_semantics_testing/android_semantics_testing.dart';
import 'package:android_semantics_testing/main.dart' as app;
import 'package:android_semantics_testing/test_constants.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
// The accessibility focus actions are added when a semantics node receives or
// lose accessibility focus. This test ignores these actions since it is hard to
// predict which node has the accessibility focus after a screen changes.
const List<AndroidSemanticsAction> ignoredAccessibilityFocusActions = <AndroidSemanticsAction>[
AndroidSemanticsAction.accessibilityFocus,
AndroidSemanticsAction.clearAccessibilityFocus,
];
const MethodChannel kSemanticsChannel = MethodChannel('semantics');
Future<void> setClipboard(String message) async {
final Completer<void> completer = Completer<void>();
Future<void> completeSetClipboard([Object? _]) async {
await kSemanticsChannel.invokeMethod<dynamic>('setClipboard', <String, dynamic>{
'message': message,
});
completer.complete();
}
if (SchedulerBinding.instance.hasScheduledFrame) {
SchedulerBinding.instance.addPostFrameCallback(completeSetClipboard);
} else {
completeSetClipboard();
}
await completer.future;
}
Future<AndroidSemanticsNode> getSemantics(Finder finder, WidgetTester tester) async {
final int id = tester.getSemantics(finder).id;
final Completer<String> completer = Completer<String>();
Future<void> completeSemantics([Object? _]) async {
final dynamic result = await kSemanticsChannel.invokeMethod<dynamic>('getSemanticsNode', <String, dynamic>{
'id': id,
});
completer.complete(json.encode(result));
}
if (SchedulerBinding.instance.hasScheduledFrame) {
SchedulerBinding.instance.addPostFrameCallback(completeSemantics);
} else {
completeSemantics();
}
return AndroidSemanticsNode.deserialize(await completer.future);
}
Future<void> main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('AccessibilityBridge', () {
group('TextField', () {
Future<void> prepareTextField(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await tester.tap(find.text(textFieldRoute));
await tester.pumpAndSettle();
// The text selection menu and related semantics vary depending on if
// the clipboard contents are pasteable. Copy some text into the
// clipboard to make sure these tests always run with pasteable content
// in the clipboard.
// Ideally this should test the case where there is nothing on the
// clipboard as well, but there is no reliable way to clear the
// clipboard on Android devices.
await setClipboard('Hello World');
await tester.pumpAndSettle();
}
testWidgets('TextField has correct Android semantics', (WidgetTester tester) async {
final Finder normalTextField = find.descendant(
of: find.byKey(const ValueKey<String>(normalTextFieldKeyValue)),
matching: find.byType(EditableText),
);
await prepareTextField(tester);
expect(
await getSemantics(normalTextField, tester),
hasAndroidSemantics(
className: AndroidClassName.editText,
isEditable: true,
isFocusable: true,
isFocused: false,
isPassword: false,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await tester.tap(normalTextField);
await tester.pumpAndSettle();
expect(
await getSemantics(normalTextField, tester),
hasAndroidSemantics(
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: false,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.paste,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await tester.enterText(normalTextField, 'hello world');
await tester.pumpAndSettle();
expect(
await getSemantics(normalTextField, tester),
hasAndroidSemantics(
text: 'hello world',
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: false,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.paste,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
AndroidSemanticsAction.previousAtMovementGranularity,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
}, timeout: Timeout.none);
testWidgets('password TextField has correct Android semantics', (WidgetTester tester) async {
final Finder passwordTextField = find.descendant(
of: find.byKey(const ValueKey<String>(passwordTextFieldKeyValue)),
matching: find.byType(EditableText),
);
await prepareTextField(tester);
expect(
await getSemantics(passwordTextField, tester),
hasAndroidSemantics(
className: AndroidClassName.editText,
isEditable: true,
isFocusable: true,
isFocused: false,
isPassword: true,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await tester.tap(passwordTextField);
await tester.pumpAndSettle();
expect(
await getSemantics(passwordTextField, tester),
hasAndroidSemantics(
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: true,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.paste,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await tester.enterText(passwordTextField, 'hello world');
await tester.pumpAndSettle();
expect(
await getSemantics(passwordTextField, tester),
hasAndroidSemantics(
text: '\u{2022}' * ('hello world'.length),
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: true,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.paste,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
AndroidSemanticsAction.previousAtMovementGranularity,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
}, timeout: Timeout.none);
});
group('SelectionControls', () {
Future<void> prepareSelectionControls(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await tester.tap(find.text(selectionControlsRoute));
await tester.pumpAndSettle();
}
testWidgets('Checkbox has correct Android semantics', (WidgetTester tester) async {
final Finder checkbox = find.byKey(const ValueKey<String>(checkboxKeyValue));
final Finder disabledCheckbox = find.byKey(const ValueKey<String>(disabledCheckboxKeyValue));
await prepareSelectionControls(tester);
expect(
await getSemantics(checkbox, tester),
hasAndroidSemantics(
className: AndroidClassName.checkBox,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await tester.tap(checkbox);
await tester.pumpAndSettle();
expect(
await getSemantics(checkbox, tester),
hasAndroidSemantics(
className: AndroidClassName.checkBox,
isChecked: true,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
expect(
await getSemantics(disabledCheckbox, tester),
hasAndroidSemantics(
className: AndroidClassName.checkBox,
isCheckable: true,
isEnabled: false,
ignoredActions: ignoredAccessibilityFocusActions,
actions: const <AndroidSemanticsAction>[],
),
);
}, timeout: Timeout.none);
testWidgets('Radio has correct Android semantics', (WidgetTester tester) async {
final Finder radio = find.byKey(const ValueKey<String>(radio2KeyValue));
await prepareSelectionControls(tester);
expect(
await getSemantics(radio, tester),
hasAndroidSemantics(
className: AndroidClassName.radio,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await tester.tap(radio);
await tester.pumpAndSettle();
expect(
await getSemantics(radio, tester),
hasAndroidSemantics(
className: AndroidClassName.radio,
isChecked: true,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
}, timeout: Timeout.none);
testWidgets('Switch has correct Android semantics', (WidgetTester tester) async {
final Finder switchFinder = find.byKey(const ValueKey<String>(switchKeyValue));
await prepareSelectionControls(tester);
expect(
await getSemantics(switchFinder, tester),
hasAndroidSemantics(
className: AndroidClassName.toggleSwitch,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await tester.tap(switchFinder);
await tester.pumpAndSettle();
expect(
await getSemantics(switchFinder, tester),
hasAndroidSemantics(
className: AndroidClassName.toggleSwitch,
isChecked: true,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
}, timeout: Timeout.none);
// Regression test for https://github.com/flutter/flutter/issues/20820.
testWidgets('Switch can be labeled', (WidgetTester tester) async {
final Finder switchFinder = find.byKey(const ValueKey<String>(labeledSwitchKeyValue));
await prepareSelectionControls(tester);
expect(
await getSemantics(switchFinder, tester),
hasAndroidSemantics(
className: AndroidClassName.toggleSwitch,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
contentDescription: switchLabel,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
}, timeout: Timeout.none);
});
group('Popup Controls', () {
Future<void> preparePopupControls(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await tester.tap(find.text(popupControlsRoute));
await tester.pumpAndSettle();
}
testWidgets('Popup Menu has correct Android semantics', (WidgetTester tester) async {
final Finder popupButton = find.byKey(const ValueKey<String>(popupButtonKeyValue));
await preparePopupControls(tester);
expect(
await getSemantics(popupButton, tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await tester.tap(popupButton);
await tester.pumpAndSettle();
try {
for (final String item in popupItems) {
expect(
await getSemantics(find.byKey(ValueKey<String>('$popupKeyValue.$item')), tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Popup $item doesn't have the right semantics",
);
}
await tester.tap(find.byKey(ValueKey<String>('$popupKeyValue.${popupItems.first}')));
await tester.pumpAndSettle();
// Pop up the menu again, to verify that TalkBack gets the right answer
// more than just the first time.
await tester.tap(popupButton);
await tester.pumpAndSettle();
for (final String item in popupItems) {
expect(
await getSemantics(find.byKey(ValueKey<String>('$popupKeyValue.$item')), tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Popup $item doesn't have the right semantics the second time",
);
}
} finally {
await tester.tap(find.byKey(ValueKey<String>('$popupKeyValue.${popupItems.first}')));
}
}, timeout: Timeout.none);
testWidgets('Dropdown Menu has correct Android semantics', (WidgetTester tester) async {
final Finder dropdownButton = find.byKey(const ValueKey<String>(dropdownButtonKeyValue));
await preparePopupControls(tester);
expect(
await getSemantics(dropdownButton, tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await tester.tap(dropdownButton);
await tester.pumpAndSettle();
try {
for (final String item in popupItems) {
// There are two copies of each item, so we want to find the version
// that is in the overlay, not the one in the dropdown.
expect(
await getSemantics(
find.descendant(
of: find.byType(Scrollable),
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.$item')),
),
tester,
),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Dropdown $item doesn't have the right semantics",
);
}
await tester.tap(
find.descendant(
of: find.byType(Scrollable),
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.${popupItems.first}')),
),
);
await tester.pumpAndSettle();
// Pop up the dropdown again, to verify that TalkBack gets the right answer
// more than just the first time.
await tester.tap(dropdownButton);
await tester.pumpAndSettle();
for (final String item in popupItems) {
// There are two copies of each item, so we want to find the version
// that is in the overlay, not the one in the dropdown.
expect(
await getSemantics(
find.descendant(
of: find.byType(Scrollable),
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.$item')),
),
tester,
),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Dropdown $item doesn't have the right semantics the second time.",
);
}
} finally {
await tester.tap(
find.descendant(
of: find.byType(Scrollable),
matching: find.byKey(ValueKey<String>('$dropdownKeyValue.${popupItems.first}')),
),
);
}
}, timeout: Timeout.none);
testWidgets('Modal alert dialog has correct Android semantics', (WidgetTester tester) async {
final Finder alertButton = find.byKey(const ValueKey<String>(alertButtonKeyValue));
await preparePopupControls(tester);
expect(
await getSemantics(alertButton, tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await tester.tap(alertButton);
await tester.pumpAndSettle();
try {
expect(
await getSemantics(find.byKey(const ValueKey<String>('$alertKeyValue.OK')), tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Alert OK button doesn't have the right semantics",
);
for (final String item in <String>['Title', 'Body1', 'Body2']) {
expect(
await getSemantics(find.byKey(ValueKey<String>('$alertKeyValue.$item')), tester),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[],
),
reason: "Alert $item button doesn't have the right semantics",
);
}
await tester.tap(find.byKey(const ValueKey<String>('$alertKeyValue.OK')));
await tester.pumpAndSettle();
// Pop up the alert again, to verify that TalkBack gets the right answer
// more than just the first time.
await tester.tap(alertButton);
await tester.pumpAndSettle();
expect(
await getSemantics(find.byKey(const ValueKey<String>('$alertKeyValue.OK')), tester),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Alert OK button doesn't have the right semantics",
);
for (final String item in <String>['Title', 'Body1', 'Body2']) {
expect(
await getSemantics(find.byKey(ValueKey<String>('$alertKeyValue.$item')), tester),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[],
),
reason: "Alert $item button doesn't have the right semantics",
);
}
} finally {
await tester.tap(find.byKey(const ValueKey<String>('$alertKeyValue.OK')));
}
}, timeout: Timeout.none);
});
group('Headings', () {
Future<void> prepareHeading(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await tester.tap(find.text(headingsRoute));
await tester.pumpAndSettle();
}
testWidgets('AppBar title has correct Android heading semantics', (WidgetTester tester) async {
await prepareHeading(tester);
expect(
await getSemantics(find.byKey(const ValueKey<String>(appBarTitleKeyValue)), tester),
hasAndroidSemantics(isHeading: true),
);
}, timeout: Timeout.none);
testWidgets('body text does not have Android heading semantics', (WidgetTester tester) async {
await prepareHeading(tester);
expect(
await getSemantics(find.byKey(const ValueKey<String>(bodyTextKeyValue)), tester),
hasAndroidSemantics(isHeading: false),
);
}, timeout: Timeout.none);
});
});
}

View File

@ -2,13 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'src/tests/controls_page.dart';
import 'src/tests/headings_page.dart';
@ -16,49 +10,9 @@ import 'src/tests/popup_page.dart';
import 'src/tests/text_field_page.dart';
void main() {
timeDilation = 0.05; // remove animations.
enableFlutterDriverExtension(handler: dataHandler);
runApp(const TestApp());
}
const MethodChannel kSemanticsChannel = MethodChannel('semantics');
Future<String> dataHandler(String? message) async {
if (message != null && message.contains('getSemanticsNode')) {
final Completer<String> completer = Completer<String>();
final int id = int.tryParse(message.split('#')[1]) ?? 0;
Future<void> completeSemantics([Object? _]) async {
final dynamic result = await kSemanticsChannel.invokeMethod<dynamic>('getSemanticsNode', <String, dynamic>{
'id': id,
});
completer.complete(json.encode(result));
}
if (SchedulerBinding.instance.hasScheduledFrame) {
SchedulerBinding.instance.addPostFrameCallback(completeSemantics);
} else {
completeSemantics();
}
return completer.future;
}
if (message != null && message.contains('setClipboard')) {
final Completer<String> completer = Completer<String>();
final String str = message.split('#')[1];
Future<void> completeSetClipboard([Object? _]) async {
await kSemanticsChannel.invokeMethod<dynamic>('setClipboard', <String, dynamic>{
'message': str,
});
completer.complete('');
}
if (SchedulerBinding.instance.hasScheduledFrame) {
SchedulerBinding.instance.addPostFrameCallback(completeSetClipboard);
} else {
completeSetClipboard();
}
return completer.future;
}
throw UnimplementedError();
}
Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
selectionControlsRoute : (BuildContext context) => const SelectionControlsPage(),
popupControlsRoute : (BuildContext context) => const PopupControlsPage(),

View File

@ -98,6 +98,7 @@ enum AndroidSemanticsAction {
/// The Android id of the action.
final int id;
// These indices need to be in sync with android_semantics_testing/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java
static const int _kFocusIndex = 1 << 0;
static const int _kClearFocusIndex = 1 << 1;
static const int _kSelectIndex = 1 << 2;

View File

@ -77,7 +77,8 @@ class _AndroidSemanticsMatcher extends Matcher {
this.isHeading,
this.isPassword,
this.isLongClickable,
});
}) : assert(ignoredActions == null || actions != null, 'actions must not be null if ignoredActions is not null'),
assert(ignoredActions == null || !actions!.any(ignoredActions.contains));
final String? text;
final String? className;
@ -115,6 +116,9 @@ class _AndroidSemanticsMatcher extends Matcher {
if (actions != null) {
description.add(' with actions: $actions');
}
if (ignoredActions != null) {
description.add(' with ignoredActions: $ignoredActions');
}
if (rect != null) {
description.add(' with rect: $rect');
}
@ -170,13 +174,15 @@ class _AndroidSemanticsMatcher extends Matcher {
}
if (actions != null) {
final List<AndroidSemanticsAction> itemActions = item.getActions();
if (ignoredActions != null) {
itemActions.removeWhere(ignoredActions!.contains);
}
if (!unorderedEquals(actions!).matches(itemActions, matchState)) {
final List<String> actionsString = actions!.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
final List<String> itemActionsString = itemActions.map<String>((AndroidSemanticsAction action) => action.toString()).toList()..sort();
final Set<AndroidSemanticsAction> unexpected = itemActions.toSet().difference(actions!.toSet());
final Set<String> unexpectedInString = itemActionsString.toSet().difference(actionsString.toSet());
final Set<String> missingInString = actionsString.toSet().difference(itemActionsString.toSet());
if (missingInString.isEmpty && ignoredActions != null && unexpected.every(ignoredActions!.contains)) {
if (missingInString.isEmpty && unexpectedInString.isEmpty) {
return true;
}
return _failWithMessage('Expected actions: $actionsString\nActual actions: $itemActionsString\nUnexpected: $unexpectedInString\nMissing: $missingInString', matchState);

View File

@ -6,7 +6,7 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_driver:
integration_test:
sdk: flutter
flutter_test:
sdk: flutter

View File

@ -1,702 +0,0 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' as io;
import 'package:android_semantics_testing/android_semantics_testing.dart';
import 'package:android_semantics_testing/test_constants.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart' hide isInstanceOf;
// The accessibility focus actions are added when a semantics node receives or
// lose accessibility focus. This test ignores these actions since it is hard to
// predict which node has the accessibility focus after a screen changes.
const List<AndroidSemanticsAction> ignoredAccessibilityFocusActions = <AndroidSemanticsAction>[
AndroidSemanticsAction.accessibilityFocus,
AndroidSemanticsAction.clearAccessibilityFocus,
];
String adbPath() {
final String? androidHome = io.Platform.environment['ANDROID_HOME'] ?? io.Platform.environment['ANDROID_SDK_ROOT'];
if (androidHome == null) {
return 'adb';
} else {
return path.join(androidHome, 'platform-tools', 'adb');
}
}
void main() {
group('AccessibilityBridge', () {
late FlutterDriver driver;
Future<AndroidSemanticsNode> getSemantics(SerializableFinder finder) async {
final int id = await driver.getSemanticsId(finder);
final String data = await driver.requestData('getSemanticsNode#$id');
return AndroidSemanticsNode.deserialize(data);
}
// The version of TalkBack running on the device.
Version? talkbackVersion;
Future<Version> getTalkbackVersion() async {
final io.ProcessResult result = await io.Process.run(adbPath(), const <String>[
'shell',
'dumpsys',
'package',
'com.google.android.marvin.talkback',
]);
if (result.exitCode != 0) {
throw Exception('Failed to get TalkBack version: ${result.stdout as String}\n${result.stderr as String}');
}
final List<String> lines = (result.stdout as String).split('\n');
String? version;
for (final String line in lines) {
if (line.contains('versionName')) {
version = line.replaceAll(RegExp(r'\s*versionName='), '');
break;
}
}
if (version == null) {
throw Exception('Unable to determine TalkBack version.');
}
// Android doesn't quite use semver, so convert the version string to semver form.
final RegExp startVersion = RegExp(r'(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(\.(?<build>\d+))?');
final RegExpMatch? match = startVersion.firstMatch(version);
if (match == null) {
return Version(0, 0, 0);
}
return Version(
int.parse(match.namedGroup('major')!),
int.parse(match.namedGroup('minor')!),
int.parse(match.namedGroup('patch')!),
build: match.namedGroup('build'),
);
}
setUpAll(() async {
driver = await FlutterDriver.connect();
talkbackVersion ??= await getTalkbackVersion();
print('TalkBack version is $talkbackVersion');
// Say the magic words..
final io.Process run = await io.Process.start(adbPath(), const <String>[
'shell',
'settings',
'put',
'secure',
'enabled_accessibility_services',
'com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService',
]);
await run.exitCode;
});
tearDownAll(() async {
// ... And turn it off again
final io.Process run = await io.Process.start(adbPath(), const <String>[
'shell',
'settings',
'put',
'secure',
'enabled_accessibility_services',
'null',
]);
await run.exitCode;
driver.close();
});
group('TextField', () {
setUpAll(() async {
await driver.tap(find.text(textFieldRoute));
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
await Future<void>.delayed(const Duration(milliseconds: 500));
// The text selection menu and related semantics vary depending on if
// the clipboard contents are pasteable. Copy some text into the
// clipboard to make sure these tests always run with pasteable content
// in the clipboard.
// Ideally this should test the case where there is nothing on the
// clipboard as well, but there is no reliable way to clear the
// clipboard on Android devices.
await driver.requestData('setClipboard#Hello World');
await Future<void>.delayed(const Duration(milliseconds: 500));
});
test('TextField has correct Android semantics', () async {
final SerializableFinder normalTextField = find.descendant(
of: find.byValueKey(normalTextFieldKeyValue),
matching: find.byType('Semantics'),
firstMatchOnly: true,
);
expect(
await getSemantics(normalTextField),
hasAndroidSemantics(
className: AndroidClassName.editText,
isEditable: true,
isFocusable: true,
isFocused: false,
isPassword: false,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await driver.tap(normalTextField);
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
await Future<void>.delayed(const Duration(milliseconds: 500));
expect(
await getSemantics(normalTextField),
hasAndroidSemantics(
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: false,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.copy,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await driver.enterText('hello world');
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
await Future<void>.delayed(const Duration(milliseconds: 500));
expect(
await getSemantics(normalTextField),
hasAndroidSemantics(
text: 'hello world',
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: false,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.copy,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
AndroidSemanticsAction.previousAtMovementGranularity,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
}, timeout: Timeout.none);
test('password TextField has correct Android semantics', () async {
final SerializableFinder passwordTextField = find.descendant(
of: find.byValueKey(passwordTextFieldKeyValue),
matching: find.byType('Semantics'),
firstMatchOnly: true,
);
expect(
await getSemantics(passwordTextField),
hasAndroidSemantics(
className: AndroidClassName.editText,
isEditable: true,
isFocusable: true,
isFocused: false,
isPassword: true,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await driver.tap(passwordTextField);
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
await Future<void>.delayed(const Duration(milliseconds: 500));
expect(
await getSemantics(passwordTextField),
hasAndroidSemantics(
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: true,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.copy,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
await driver.enterText('hello world');
// Delay for TalkBack to update focus as of November 2019 with Pixel 3 and Android API 28
await Future<void>.delayed(const Duration(milliseconds: 500));
expect(
await getSemantics(passwordTextField),
hasAndroidSemantics(
text: '\u{2022}' * ('hello world'.length),
className: AndroidClassName.editText,
isFocusable: true,
isFocused: true,
isEditable: true,
isPassword: true,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
AndroidSemanticsAction.copy,
AndroidSemanticsAction.setSelection,
AndroidSemanticsAction.setText,
AndroidSemanticsAction.previousAtMovementGranularity,
],
// We can't predict the a11y focus when the screen changes.
ignoredActions: ignoredAccessibilityFocusActions,
),
);
}, timeout: Timeout.none);
tearDownAll(() async {
await driver.tap(find.byValueKey('back'));
});
});
group('SelectionControls', () {
setUpAll(() async {
await driver.tap(find.text(selectionControlsRoute));
});
test('Checkbox has correct Android semantics', () async {
Future<AndroidSemanticsNode> getCheckboxSemantics(String key) async {
return getSemantics(find.byValueKey(key));
}
expect(
await getCheckboxSemantics(checkboxKeyValue),
hasAndroidSemantics(
className: AndroidClassName.checkBox,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await driver.tap(find.byValueKey(checkboxKeyValue));
expect(
await getCheckboxSemantics(checkboxKeyValue),
hasAndroidSemantics(
className: AndroidClassName.checkBox,
isChecked: true,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
expect(
await getCheckboxSemantics(disabledCheckboxKeyValue),
hasAndroidSemantics(
className: AndroidClassName.checkBox,
isCheckable: true,
isEnabled: false,
ignoredActions: ignoredAccessibilityFocusActions,
actions: const <AndroidSemanticsAction>[],
),
);
}, timeout: Timeout.none);
test('Radio has correct Android semantics', () async {
Future<AndroidSemanticsNode> getRadioSemantics(String key) async {
return getSemantics(find.byValueKey(key));
}
expect(
await getRadioSemantics(radio2KeyValue),
hasAndroidSemantics(
className: AndroidClassName.radio,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await driver.tap(find.byValueKey(radio2KeyValue));
expect(
await getRadioSemantics(radio2KeyValue),
hasAndroidSemantics(
className: AndroidClassName.radio,
isChecked: true,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
}, timeout: Timeout.none);
test('Switch has correct Android semantics', () async {
Future<AndroidSemanticsNode> getSwitchSemantics(String key) async {
return getSemantics(find.byValueKey(key));
}
expect(
await getSwitchSemantics(switchKeyValue),
hasAndroidSemantics(
className: AndroidClassName.toggleSwitch,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await driver.tap(find.byValueKey(switchKeyValue));
expect(
await getSwitchSemantics(switchKeyValue),
hasAndroidSemantics(
className: AndroidClassName.toggleSwitch,
isChecked: true,
isCheckable: true,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
}, timeout: Timeout.none);
// Regression test for https://github.com/flutter/flutter/issues/20820.
test('Switch can be labeled', () async {
Future<AndroidSemanticsNode> getSwitchSemantics(String key) async {
return getSemantics(find.byValueKey(key));
}
expect(
await getSwitchSemantics(labeledSwitchKeyValue),
hasAndroidSemantics(
className: AndroidClassName.toggleSwitch,
isChecked: false,
isCheckable: true,
isEnabled: true,
isFocusable: true,
contentDescription: switchLabel,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
}, timeout: Timeout.none);
tearDownAll(() async {
await driver.tap(find.byValueKey('back'));
});
});
group('Popup Controls', () {
setUpAll(() async {
await driver.tap(find.text(popupControlsRoute));
});
test('Popup Menu has correct Android semantics', () async {
expect(
await getSemantics(find.byValueKey(popupButtonKeyValue)),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await driver.tap(find.byValueKey(popupButtonKeyValue));
try {
// We have to wait wall time here because we're waiting for TalkBack to
// catch up.
await Future<void>.delayed(const Duration(milliseconds: 1500));
for (final String item in popupItems) {
expect(
await getSemantics(find.byValueKey('$popupKeyValue.$item')),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Popup $item doesn't have the right semantics");
}
await driver.tap(find.byValueKey('$popupKeyValue.${popupItems.first}'));
// Pop up the menu again, to verify that TalkBack gets the right answer
// more than just the first time.
await driver.tap(find.byValueKey(popupButtonKeyValue));
await Future<void>.delayed(const Duration(milliseconds: 1500));
for (final String item in popupItems) {
expect(
await getSemantics(find.byValueKey('$popupKeyValue.$item')),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Popup $item doesn't have the right semantics the second time");
}
} finally {
await driver.tap(find.byValueKey('$popupKeyValue.${popupItems.first}'));
}
}, timeout: Timeout.none);
test('Dropdown Menu has correct Android semantics', () async {
expect(
await getSemantics(find.byValueKey(dropdownButtonKeyValue)),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await driver.tap(find.byValueKey(dropdownButtonKeyValue));
try {
await Future<void>.delayed(const Duration(milliseconds: 1500));
for (final String item in popupItems) {
// There are two copies of each item, so we want to find the version
// that is in the overlay, not the one in the dropdown.
expect(
await getSemantics(find.descendant(
of: find.byType('Scrollable'),
matching: find.byValueKey('$dropdownKeyValue.$item'),
)),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Dropdown $item doesn't have the right semantics");
}
await driver.tap(
find.descendant(
of: find.byType('Scrollable'),
matching: find.byValueKey('$dropdownKeyValue.${popupItems.first}'),
),
);
// Pop up the dropdown again, to verify that TalkBack gets the right answer
// more than just the first time.
await driver.tap(find.byValueKey(dropdownButtonKeyValue));
await Future<void>.delayed(const Duration(milliseconds: 1500));
for (final String item in popupItems) {
// There are two copies of each item, so we want to find the version
// that is in the overlay, not the one in the dropdown.
expect(
await getSemantics(find.descendant(
of: find.byType('Scrollable'),
matching: find.byValueKey('$dropdownKeyValue.$item'),
)),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Dropdown $item doesn't have the right semantics the second time.");
}
} finally {
await driver.tap(
find.descendant(
of: find.byType('Scrollable'),
matching: find.byValueKey('$dropdownKeyValue.${popupItems.first}'),
),
);
}
}, timeout: Timeout.none);
test('Modal alert dialog has correct Android semantics', () async {
expect(
await getSemantics(find.byValueKey(alertButtonKeyValue)),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
);
await driver.tap(find.byValueKey(alertButtonKeyValue));
try {
await Future<void>.delayed(const Duration(milliseconds: 1500));
expect(
await getSemantics(find.byValueKey('$alertKeyValue.OK')),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Alert OK button doesn't have the right semantics");
for (final String item in <String>['Title', 'Body1', 'Body2']) {
expect(
await getSemantics(find.byValueKey('$alertKeyValue.$item')),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[],
),
reason: "Alert $item button doesn't have the right semantics");
}
await driver.tap(find.byValueKey('$alertKeyValue.OK'));
// Pop up the alert again, to verify that TalkBack gets the right answer
// more than just the first time.
await driver.tap(find.byValueKey(alertButtonKeyValue));
await Future<void>.delayed(const Duration(milliseconds: 1500));
expect(
await getSemantics(find.byValueKey('$alertKeyValue.OK')),
hasAndroidSemantics(
className: AndroidClassName.button,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[
AndroidSemanticsAction.click,
],
),
reason: "Alert OK button doesn't have the right semantics");
for (final String item in <String>['Title', 'Body1', 'Body2']) {
expect(
await getSemantics(find.byValueKey('$alertKeyValue.$item')),
hasAndroidSemantics(
className: AndroidClassName.view,
isChecked: false,
isCheckable: false,
isEnabled: true,
isFocusable: true,
ignoredActions: ignoredAccessibilityFocusActions,
actions: <AndroidSemanticsAction>[],
),
reason: "Alert $item button doesn't have the right semantics");
}
} finally {
await driver.tap(find.byValueKey('$alertKeyValue.OK'));
}
}, timeout: Timeout.none);
tearDownAll(() async {
await Future<void>.delayed(const Duration(milliseconds: 500));
await driver.tap(find.byValueKey('back'));
});
});
group('Headings', () {
setUpAll(() async {
await driver.tap(find.text(headingsRoute));
});
test('AppBar title has correct Android heading semantics', () async {
expect(
await getSemantics(find.byValueKey(appBarTitleKeyValue)),
hasAndroidSemantics(isHeading: true),
);
}, timeout: Timeout.none);
test('body text does not have Android heading semantics', () async {
expect(
await getSemantics(find.byValueKey(bodyTextKeyValue)),
hasAndroidSemantics(isHeading: false),
);
}, timeout: Timeout.none);
tearDownAll(() async {
await driver.tap(find.byValueKey('back'));
});
});
});
}