mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Wires up Android API to set section locale (#173364)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> related https://github.com/flutter/flutter/issues/99600 what still missing: 1. iOS equivalence 2. set application level locale internal only doc: [go/flutter-semantics-language](http://goto.google.com/flutter-semantics-language) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
3a18c9449e
commit
f5ce9117ee
@ -293,6 +293,60 @@ void sendSemanticsUpdateWithRole() {
|
||||
_semanticsUpdate(builder.build());
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void sendSemanticsUpdateWithLocale() {
|
||||
final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder();
|
||||
|
||||
final Float64List transform = Float64List(16);
|
||||
final Int32List childrenInTraversalOrder = Int32List(0);
|
||||
final Int32List childrenInHitTestOrder = Int32List(0);
|
||||
final Int32List additionalActions = Int32List(0);
|
||||
// Identity matrix 4x4.
|
||||
transform[0] = 1;
|
||||
transform[5] = 1;
|
||||
transform[10] = 1;
|
||||
builder.updateNode(
|
||||
id: 0,
|
||||
flags: SemanticsFlags.none,
|
||||
actions: 0,
|
||||
maxValueLength: 0,
|
||||
currentValueLength: 0,
|
||||
textSelectionBase: -1,
|
||||
textSelectionExtent: -1,
|
||||
platformViewId: -1,
|
||||
scrollChildren: 0,
|
||||
scrollIndex: 0,
|
||||
scrollPosition: 0,
|
||||
scrollExtentMax: 0,
|
||||
scrollExtentMin: 0,
|
||||
rect: Rect.fromLTRB(0, 0, 10, 10),
|
||||
identifier: "identifier",
|
||||
label: "label",
|
||||
labelAttributes: const <StringAttribute>[],
|
||||
value: "value",
|
||||
valueAttributes: const <StringAttribute>[],
|
||||
increasedValue: "increasedValue",
|
||||
increasedValueAttributes: const <StringAttribute>[],
|
||||
decreasedValue: "decreasedValue",
|
||||
decreasedValueAttributes: const <StringAttribute>[],
|
||||
hint: "hint",
|
||||
hintAttributes: const <StringAttribute>[],
|
||||
tooltip: "tooltip",
|
||||
textDirection: TextDirection.ltr,
|
||||
transform: transform,
|
||||
childrenInTraversalOrder: childrenInTraversalOrder,
|
||||
childrenInHitTestOrder: childrenInHitTestOrder,
|
||||
additionalActions: additionalActions,
|
||||
headingLevel: 0,
|
||||
linkUrl: '',
|
||||
role: SemanticsRole.none,
|
||||
controlsNodes: null,
|
||||
inputType: SemanticsInputType.none,
|
||||
locale: Locale('es', 'MX'),
|
||||
);
|
||||
_semanticsUpdate(builder.build());
|
||||
}
|
||||
|
||||
@pragma('vm:external-name', 'SemanticsUpdate')
|
||||
external void _semanticsUpdate(SemanticsUpdate update);
|
||||
|
||||
|
||||
@ -1948,7 +1948,7 @@ base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1
|
||||
controlsNodes,
|
||||
validationResult.index,
|
||||
inputType.index,
|
||||
locale?.toString() ?? '',
|
||||
locale?.toLanguageTag() ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -166,6 +166,7 @@ struct SemanticsNode {
|
||||
std::string linkUrl;
|
||||
SemanticsRole role;
|
||||
SemanticsValidationResult validationResult = SemanticsValidationResult::kNone;
|
||||
std::string locale;
|
||||
};
|
||||
|
||||
// Contains semantic nodes that need to be updated.
|
||||
|
||||
@ -69,7 +69,9 @@ void SemanticsUpdateBuilder::updateNode(
|
||||
std::string linkUrl,
|
||||
int role,
|
||||
const std::vector<std::string>& controlsNodes,
|
||||
int validationResult) {
|
||||
int validationResult,
|
||||
int inputType,
|
||||
std::string locale) {
|
||||
FML_CHECK(scrollChildren == 0 ||
|
||||
(scrollChildren > 0 && childrenInHitTestOrder.data()))
|
||||
<< "Semantics update contained scrollChildren but did not have "
|
||||
@ -125,6 +127,7 @@ void SemanticsUpdateBuilder::updateNode(
|
||||
node.role = static_cast<SemanticsRole>(role);
|
||||
node.validationResult =
|
||||
static_cast<SemanticsValidationResult>(validationResult);
|
||||
node.locale = std::move(locale);
|
||||
|
||||
nodes_[id] = node;
|
||||
}
|
||||
|
||||
@ -68,7 +68,9 @@ class SemanticsUpdateBuilder
|
||||
std::string linkUrl,
|
||||
int role,
|
||||
const std::vector<std::string>& controlsNodes,
|
||||
int validationResult);
|
||||
int validationResult,
|
||||
int inputType,
|
||||
std::string locale);
|
||||
|
||||
void updateCustomAction(int id,
|
||||
std::string label,
|
||||
|
||||
@ -23,23 +23,25 @@ TEST_F(SemanticsUpdateBuilderTest, CanHandleAttributedStrings) {
|
||||
ASSERT_FALSE(Dart_IsError(result));
|
||||
SemanticsUpdate* update = reinterpret_cast<SemanticsUpdate*>(peer);
|
||||
SemanticsNodeUpdates nodes = update->takeNodes();
|
||||
ASSERT_EQ(nodes.size(), (size_t)1);
|
||||
auto node = nodes.find(0)->second;
|
||||
ASSERT_EQ(nodes.size(), static_cast<size_t>(1));
|
||||
auto found = nodes.find(0);
|
||||
ASSERT_NE(found, nodes.end());
|
||||
SemanticsNode node = found->second;
|
||||
// Should match the updateNode in ui_test.dart.
|
||||
ASSERT_EQ(node.label, "label");
|
||||
ASSERT_EQ(node.labelAttributes.size(), (size_t)1);
|
||||
ASSERT_EQ(node.labelAttributes.size(), static_cast<size_t>(1));
|
||||
ASSERT_EQ(node.labelAttributes[0]->start, 1);
|
||||
ASSERT_EQ(node.labelAttributes[0]->end, 2);
|
||||
ASSERT_EQ(node.labelAttributes[0]->type, StringAttributeType::kSpellOut);
|
||||
|
||||
ASSERT_EQ(node.value, "value");
|
||||
ASSERT_EQ(node.valueAttributes.size(), (size_t)1);
|
||||
ASSERT_EQ(node.valueAttributes.size(), static_cast<size_t>(1));
|
||||
ASSERT_EQ(node.valueAttributes[0]->start, 2);
|
||||
ASSERT_EQ(node.valueAttributes[0]->end, 3);
|
||||
ASSERT_EQ(node.valueAttributes[0]->type, StringAttributeType::kSpellOut);
|
||||
|
||||
ASSERT_EQ(node.hint, "hint");
|
||||
ASSERT_EQ(node.hintAttributes.size(), (size_t)1);
|
||||
ASSERT_EQ(node.hintAttributes.size(), static_cast<size_t>(1));
|
||||
ASSERT_EQ(node.hintAttributes[0]->start, 0);
|
||||
ASSERT_EQ(node.hintAttributes[0]->end, 1);
|
||||
ASSERT_EQ(node.hintAttributes[0]->type, StringAttributeType::kLocale);
|
||||
@ -48,14 +50,14 @@ TEST_F(SemanticsUpdateBuilderTest, CanHandleAttributedStrings) {
|
||||
ASSERT_EQ(local_attribute->locale, "en-MX");
|
||||
|
||||
ASSERT_EQ(node.increasedValue, "increasedValue");
|
||||
ASSERT_EQ(node.increasedValueAttributes.size(), (size_t)1);
|
||||
ASSERT_EQ(node.increasedValueAttributes.size(), static_cast<size_t>(1));
|
||||
ASSERT_EQ(node.increasedValueAttributes[0]->start, 4);
|
||||
ASSERT_EQ(node.increasedValueAttributes[0]->end, 5);
|
||||
ASSERT_EQ(node.increasedValueAttributes[0]->type,
|
||||
StringAttributeType::kSpellOut);
|
||||
|
||||
ASSERT_EQ(node.decreasedValue, "decreasedValue");
|
||||
ASSERT_EQ(node.decreasedValueAttributes.size(), (size_t)1);
|
||||
ASSERT_EQ(node.decreasedValueAttributes.size(), static_cast<size_t>(1));
|
||||
ASSERT_EQ(node.decreasedValueAttributes[0]->start, 5);
|
||||
ASSERT_EQ(node.decreasedValueAttributes[0]->end, 6);
|
||||
ASSERT_EQ(node.decreasedValueAttributes[0]->type,
|
||||
@ -99,8 +101,9 @@ TEST_F(SemanticsUpdateBuilderTest, CanHandleSemanticsRole) {
|
||||
ASSERT_FALSE(Dart_IsError(result));
|
||||
SemanticsUpdate* update = reinterpret_cast<SemanticsUpdate*>(peer);
|
||||
SemanticsNodeUpdates nodes = update->takeNodes();
|
||||
ASSERT_EQ(nodes.size(), (size_t)1);
|
||||
auto node = nodes.find(0)->second;
|
||||
auto found = nodes.find(0);
|
||||
ASSERT_NE(found, nodes.end());
|
||||
SemanticsNode node = found->second;
|
||||
// Should match the updateNode in ui_test.dart.
|
||||
ASSERT_EQ(node.role, SemanticsRole::kTab);
|
||||
message_latch->Signal();
|
||||
@ -123,7 +126,52 @@ TEST_F(SemanticsUpdateBuilderTest, CanHandleSemanticsRole) {
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
configuration.SetEntrypoint("sendSemanticsUpdateWithRole");
|
||||
|
||||
shell->RunEngine(std::move(configuration), [](auto result) {
|
||||
shell->RunEngine(std::move(configuration), [](Engine::RunStatus result) {
|
||||
ASSERT_EQ(result, Engine::RunStatus::Success);
|
||||
});
|
||||
|
||||
message_latch->Wait();
|
||||
DestroyShell(std::move(shell), task_runners);
|
||||
}
|
||||
|
||||
TEST_F(SemanticsUpdateBuilderTest, CanHandleSemanticsLocale) {
|
||||
auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
|
||||
|
||||
auto nativeSemanticsUpdate = [message_latch](Dart_NativeArguments args) {
|
||||
auto handle = Dart_GetNativeArgument(args, 0);
|
||||
intptr_t peer = 0;
|
||||
Dart_Handle result = Dart_GetNativeInstanceField(
|
||||
handle, tonic::DartWrappable::kPeerIndex, &peer);
|
||||
ASSERT_FALSE(Dart_IsError(result));
|
||||
SemanticsUpdate* update = reinterpret_cast<SemanticsUpdate*>(peer);
|
||||
SemanticsNodeUpdates nodes = update->takeNodes();
|
||||
ASSERT_EQ(nodes.size(), static_cast<size_t>(1));
|
||||
auto found = nodes.find(0);
|
||||
ASSERT_NE(found, nodes.end());
|
||||
SemanticsNode node = found->second;
|
||||
// Should match the updateNode in ui_test.dart.
|
||||
ASSERT_EQ(node.locale, "es-MX");
|
||||
message_latch->Signal();
|
||||
};
|
||||
|
||||
Settings settings = CreateSettingsForFixture();
|
||||
TaskRunners task_runners("test", // label
|
||||
GetCurrentTaskRunner(), // platform
|
||||
CreateNewThread(), // raster
|
||||
CreateNewThread(), // ui
|
||||
CreateNewThread() // io
|
||||
);
|
||||
|
||||
AddNativeCallback("SemanticsUpdate",
|
||||
CREATE_NATIVE_ENTRY(nativeSemanticsUpdate));
|
||||
|
||||
std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
|
||||
|
||||
ASSERT_TRUE(shell->IsSetup());
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
configuration.SetEntrypoint("sendSemanticsUpdateWithLocale");
|
||||
|
||||
shell->RunEngine(std::move(configuration), [](Engine::RunStatus result) {
|
||||
ASSERT_EQ(result, Engine::RunStatus::Success);
|
||||
});
|
||||
|
||||
|
||||
@ -366,6 +366,7 @@ android_java_sources = [
|
||||
"io/flutter/util/TraceSection.java",
|
||||
"io/flutter/util/ViewUtils.java",
|
||||
"io/flutter/view/AccessibilityBridge.java",
|
||||
"io/flutter/view/AccessibilityStringBuilder.java",
|
||||
"io/flutter/view/AccessibilityViewEmbedder.java",
|
||||
"io/flutter/view/FlutterCallbackInformation.java",
|
||||
"io/flutter/view/FlutterRunArguments.java",
|
||||
|
||||
@ -19,11 +19,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.LocaleSpan;
|
||||
import android.text.style.TtsSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
@ -41,6 +37,10 @@ import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
|
||||
import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate;
|
||||
import io.flutter.util.Predicate;
|
||||
import io.flutter.util.ViewUtils;
|
||||
import io.flutter.view.AccessibilityStringBuilder.LocaleStringAttribute;
|
||||
import io.flutter.view.AccessibilityStringBuilder.SpellOutStringAttribute;
|
||||
import io.flutter.view.AccessibilityStringBuilder.StringAttribute;
|
||||
import io.flutter.view.AccessibilityStringBuilder.StringAttributeType;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
@ -134,6 +134,9 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
/// Value is derived from ACTION_TYPE_MASK in AccessibilityNodeInfo.java
|
||||
private static int FIRST_RESOURCE_ID = 267386881;
|
||||
|
||||
/// The index value that indicates no string is specified.
|
||||
private static int EMPTY_STRING_INDEX = -1;
|
||||
|
||||
// Real Android View, which internally holds a Flutter UI.
|
||||
@NonNull private final View rootAccessibilityView;
|
||||
|
||||
@ -506,6 +509,54 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
platformViewsAccessibilityDelegate.attachAccessibilityBridge(this);
|
||||
}
|
||||
|
||||
private static List<StringAttribute> getStringAttributesFromBuffer(
|
||||
@NonNull ByteBuffer buffer, @NonNull ByteBuffer[] stringAttributeArgs) {
|
||||
final int attributesCount = buffer.getInt();
|
||||
if (attributesCount == -1) {
|
||||
return null;
|
||||
}
|
||||
final List<StringAttribute> result = new ArrayList<>(attributesCount);
|
||||
for (int i = 0; i < attributesCount; ++i) {
|
||||
final int start = buffer.getInt();
|
||||
final int end = buffer.getInt();
|
||||
final StringAttributeType type = StringAttributeType.values()[buffer.getInt()];
|
||||
switch (type) {
|
||||
case SPELLOUT:
|
||||
{
|
||||
// Pops the -1 size.
|
||||
buffer.getInt();
|
||||
SpellOutStringAttribute attribute = new SpellOutStringAttribute();
|
||||
attribute.start = start;
|
||||
attribute.end = end;
|
||||
attribute.type = type;
|
||||
result.add(attribute);
|
||||
break;
|
||||
}
|
||||
case LOCALE:
|
||||
{
|
||||
final int argsIndex = buffer.getInt();
|
||||
final ByteBuffer args = stringAttributeArgs[argsIndex];
|
||||
LocaleStringAttribute attribute = new LocaleStringAttribute();
|
||||
attribute.start = start;
|
||||
attribute.end = end;
|
||||
attribute.type = type;
|
||||
attribute.locale = Charset.forName("UTF-8").decode(args).toString();
|
||||
result.add(attribute);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String getStringFromBuffer(@NonNull ByteBuffer buffer, @NonNull String[] strings) {
|
||||
int stringIndex = buffer.getInt();
|
||||
|
||||
return stringIndex == EMPTY_STRING_INDEX ? null : strings[stringIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects any listeners and/or delegates that were initialized in {@code
|
||||
* AccessibilityBridge}'s constructor, or added after.
|
||||
@ -1615,10 +1666,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
int id = buffer.getInt();
|
||||
CustomAccessibilityAction action = getOrCreateAccessibilityAction(id);
|
||||
action.overrideId = buffer.getInt();
|
||||
int stringIndex = buffer.getInt();
|
||||
action.label = stringIndex == -1 ? null : strings[stringIndex];
|
||||
stringIndex = buffer.getInt();
|
||||
action.hint = stringIndex == -1 ? null : strings[stringIndex];
|
||||
action.label = getStringFromBuffer(buffer, strings);
|
||||
action.hint = getStringFromBuffer(buffer, strings);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2253,34 +2302,6 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
private String hint;
|
||||
}
|
||||
|
||||
// When adding a new StringAttributeType, the classes in these file must be
|
||||
// updated as well.
|
||||
// * engine/src/flutter/lib/ui/semantics.dart
|
||||
// * engine/src/flutter/lib/web_ui/lib/semantics.dart
|
||||
// * engine/src/flutter/lib/ui/semantics/string_attribute.h
|
||||
|
||||
private enum StringAttributeType {
|
||||
SPELLOUT,
|
||||
LOCALE,
|
||||
URL
|
||||
}
|
||||
|
||||
private static class StringAttribute {
|
||||
int start;
|
||||
int end;
|
||||
StringAttributeType type;
|
||||
}
|
||||
|
||||
private static class SpellOutStringAttribute extends StringAttribute {}
|
||||
|
||||
private static class LocaleStringAttribute extends StringAttribute {
|
||||
String locale;
|
||||
}
|
||||
|
||||
private static class UrlStringAttribute extends StringAttribute {
|
||||
String url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flutter {@code SemanticsNode} represented in Java/Android.
|
||||
*
|
||||
@ -2335,9 +2356,12 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
// API level >= 28; otherwise, this is attached to the end of content description.
|
||||
@Nullable private String tooltip;
|
||||
|
||||
// The Url the widget's points to.
|
||||
// The Url this node points to.
|
||||
@Nullable private String linkUrl;
|
||||
|
||||
// The locale of the content of this node.
|
||||
@Nullable private String locale;
|
||||
|
||||
// The id of the sibling node that is before this node in traversal
|
||||
// order.
|
||||
//
|
||||
@ -2517,40 +2541,26 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
scrollExtentMax = buffer.getFloat();
|
||||
scrollExtentMin = buffer.getFloat();
|
||||
|
||||
int stringIndex = buffer.getInt();
|
||||
|
||||
identifier = stringIndex == -1 ? null : strings[stringIndex];
|
||||
stringIndex = buffer.getInt();
|
||||
|
||||
label = stringIndex == -1 ? null : strings[stringIndex];
|
||||
identifier = getStringFromBuffer(buffer, strings);
|
||||
|
||||
label = getStringFromBuffer(buffer, strings);
|
||||
labelAttributes = getStringAttributesFromBuffer(buffer, stringAttributeArgs);
|
||||
|
||||
stringIndex = buffer.getInt();
|
||||
value = stringIndex == -1 ? null : strings[stringIndex];
|
||||
|
||||
value = getStringFromBuffer(buffer, strings);
|
||||
valueAttributes = getStringAttributesFromBuffer(buffer, stringAttributeArgs);
|
||||
|
||||
stringIndex = buffer.getInt();
|
||||
increasedValue = stringIndex == -1 ? null : strings[stringIndex];
|
||||
|
||||
increasedValue = getStringFromBuffer(buffer, strings);
|
||||
increasedValueAttributes = getStringAttributesFromBuffer(buffer, stringAttributeArgs);
|
||||
|
||||
stringIndex = buffer.getInt();
|
||||
decreasedValue = stringIndex == -1 ? null : strings[stringIndex];
|
||||
|
||||
decreasedValue = getStringFromBuffer(buffer, strings);
|
||||
decreasedValueAttributes = getStringAttributesFromBuffer(buffer, stringAttributeArgs);
|
||||
|
||||
stringIndex = buffer.getInt();
|
||||
hint = stringIndex == -1 ? null : strings[stringIndex];
|
||||
|
||||
hint = getStringFromBuffer(buffer, strings);
|
||||
hintAttributes = getStringAttributesFromBuffer(buffer, stringAttributeArgs);
|
||||
|
||||
stringIndex = buffer.getInt();
|
||||
tooltip = stringIndex == -1 ? null : strings[stringIndex];
|
||||
|
||||
stringIndex = buffer.getInt();
|
||||
linkUrl = stringIndex == -1 ? null : strings[stringIndex];
|
||||
tooltip = getStringFromBuffer(buffer, strings);
|
||||
linkUrl = getStringFromBuffer(buffer, strings);
|
||||
locale = getStringFromBuffer(buffer, strings);
|
||||
|
||||
textDirection = TextDirection.fromInt(buffer.getInt());
|
||||
|
||||
@ -2840,29 +2850,28 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
}
|
||||
|
||||
private CharSequence getValue() {
|
||||
return createSpannableString(value, valueAttributes);
|
||||
return new AccessibilityStringBuilder()
|
||||
.addString(value)
|
||||
.addAttributes(valueAttributes)
|
||||
.addLocale(locale)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CharSequence getLabel() {
|
||||
List<StringAttribute> attributes = labelAttributes;
|
||||
if (linkUrl != null && linkUrl.length() > 0) {
|
||||
if (attributes == null) {
|
||||
attributes = new ArrayList<StringAttribute>();
|
||||
} else {
|
||||
attributes = new ArrayList<StringAttribute>(attributes);
|
||||
}
|
||||
UrlStringAttribute uriStringAttribute = new UrlStringAttribute();
|
||||
uriStringAttribute.start = 0;
|
||||
uriStringAttribute.end = label.length();
|
||||
uriStringAttribute.url = linkUrl;
|
||||
uriStringAttribute.type = StringAttributeType.URL;
|
||||
attributes.add(uriStringAttribute);
|
||||
}
|
||||
return createSpannableString(label, attributes);
|
||||
return new AccessibilityStringBuilder()
|
||||
.addString(label)
|
||||
.addAttributes(labelAttributes)
|
||||
.addUrl(linkUrl)
|
||||
.addLocale(locale)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CharSequence getHint() {
|
||||
return createSpannableString(hint, hintAttributes);
|
||||
return new AccessibilityStringBuilder()
|
||||
.addString(hint)
|
||||
.addAttributes(hintAttributes)
|
||||
.addLocale(locale)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CharSequence getValueLabelHint() {
|
||||
@ -2894,41 +2903,6 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SpannableString createSpannableString(String string, List<StringAttribute> attributes) {
|
||||
if (string == null) {
|
||||
return null;
|
||||
}
|
||||
final SpannableString spannableString = new SpannableString(string);
|
||||
if (attributes != null) {
|
||||
for (StringAttribute attribute : attributes) {
|
||||
switch (attribute.type) {
|
||||
case SPELLOUT:
|
||||
{
|
||||
final TtsSpan ttsSpan = new TtsSpan.Builder<>(TtsSpan.TYPE_VERBATIM).build();
|
||||
spannableString.setSpan(ttsSpan, attribute.start, attribute.end, 0);
|
||||
break;
|
||||
}
|
||||
case LOCALE:
|
||||
{
|
||||
LocaleStringAttribute localeAttribute = (LocaleStringAttribute) attribute;
|
||||
Locale locale = Locale.forLanguageTag(localeAttribute.locale);
|
||||
final LocaleSpan localeSpan = new LocaleSpan(locale);
|
||||
spannableString.setSpan(localeSpan, attribute.start, attribute.end, 0);
|
||||
break;
|
||||
}
|
||||
case URL:
|
||||
{
|
||||
UrlStringAttribute uriAttribute = (UrlStringAttribute) attribute;
|
||||
final URLSpan urlSpan = new URLSpan(uriAttribute.url);
|
||||
spannableString.setSpan(urlSpan, attribute.start, attribute.end, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return spannableString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
package io.flutter.view;
|
||||
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.LocaleSpan;
|
||||
import android.text.style.TtsSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Builds a string with accessibility related string spans.
|
||||
*
|
||||
* <p>Use {@code addLocale} to set the locale and/or {@code addUrl} to set the url for the entire
|
||||
* string. Uses {@code addAttributes} to add any additional {@code StringAttribute} to the string
|
||||
*/
|
||||
public class AccessibilityStringBuilder {
|
||||
// When adding a new StringAttributeType, the classes in these file must be
|
||||
// updated as well.
|
||||
// * engine/src/flutter/lib/ui/semantics.dart
|
||||
// * engine/src/flutter/lib/web_ui/lib/semantics.dart
|
||||
// * engine/src/flutter/lib/ui/semantics/string_attribute.h
|
||||
|
||||
public enum StringAttributeType {
|
||||
SPELLOUT,
|
||||
LOCALE,
|
||||
}
|
||||
|
||||
public static class StringAttribute {
|
||||
int start;
|
||||
int end;
|
||||
StringAttributeType type;
|
||||
}
|
||||
|
||||
public static class SpellOutStringAttribute extends StringAttribute {}
|
||||
|
||||
public static class LocaleStringAttribute extends StringAttribute {
|
||||
String locale;
|
||||
}
|
||||
|
||||
private static class UrlStringAttribute extends StringAttribute {
|
||||
String url;
|
||||
}
|
||||
|
||||
AccessibilityStringBuilder() {}
|
||||
|
||||
private String string;
|
||||
private List<StringAttribute> attributes;
|
||||
private String locale;
|
||||
private String url;
|
||||
|
||||
AccessibilityStringBuilder addString(String string) {
|
||||
this.string = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
AccessibilityStringBuilder addAttributes(List<StringAttribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
return this;
|
||||
}
|
||||
|
||||
AccessibilityStringBuilder addLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
|
||||
AccessibilityStringBuilder addUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
CharSequence build() {
|
||||
if (string == null) {
|
||||
return null;
|
||||
}
|
||||
final SpannableString spannableString = new SpannableString(string);
|
||||
if (attributes != null) {
|
||||
for (StringAttribute attribute : attributes) {
|
||||
switch (attribute.type) {
|
||||
case SPELLOUT:
|
||||
{
|
||||
final TtsSpan ttsSpan = new TtsSpan.Builder<>(TtsSpan.TYPE_VERBATIM).build();
|
||||
spannableString.setSpan(ttsSpan, attribute.start, attribute.end, 0);
|
||||
break;
|
||||
}
|
||||
case LOCALE:
|
||||
{
|
||||
LocaleStringAttribute localeAttribute = (LocaleStringAttribute) attribute;
|
||||
Locale locale = Locale.forLanguageTag(localeAttribute.locale);
|
||||
final LocaleSpan localeSpan = new LocaleSpan(locale);
|
||||
spannableString.setSpan(localeSpan, attribute.start, attribute.end, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (url != null && !url.isEmpty()) {
|
||||
final URLSpan urlSpan = new URLSpan(url);
|
||||
spannableString.setSpan(urlSpan, 0, string.length(), 0);
|
||||
}
|
||||
|
||||
if (locale != null && !locale.isEmpty()) {
|
||||
Locale localeObject = Locale.forLanguageTag(locale);
|
||||
final LocaleSpan localeSpan = new LocaleSpan(localeObject);
|
||||
spannableString.setSpan(localeSpan, 0, string.length(), 0);
|
||||
}
|
||||
|
||||
return spannableString;
|
||||
}
|
||||
}
|
||||
@ -7,27 +7,27 @@
|
||||
#include <utility>
|
||||
|
||||
namespace flutter {
|
||||
|
||||
namespace {
|
||||
void putStringAttributesIntoBuffer(
|
||||
const StringAttributes& attributes,
|
||||
int32_t* buffer_int32,
|
||||
size_t& position,
|
||||
int32_t* buffer,
|
||||
size_t* position,
|
||||
std::vector<std::vector<uint8_t>>& string_attribute_args) {
|
||||
if (attributes.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
buffer[(*position)++] = PlatformViewAndroidDelegate::kEmptyStringIndex;
|
||||
return;
|
||||
}
|
||||
buffer_int32[position++] = attributes.size();
|
||||
buffer[(*position)++] = attributes.size();
|
||||
for (const auto& attribute : attributes) {
|
||||
buffer_int32[position++] = attribute->start;
|
||||
buffer_int32[position++] = attribute->end;
|
||||
buffer_int32[position++] = static_cast<int32_t>(attribute->type);
|
||||
buffer[(*position)++] = attribute->start;
|
||||
buffer[(*position)++] = attribute->end;
|
||||
buffer[(*position)++] = static_cast<int32_t>(attribute->type);
|
||||
switch (attribute->type) {
|
||||
case StringAttributeType::kSpellOut:
|
||||
buffer_int32[position++] = -1;
|
||||
buffer[(*position)++] = PlatformViewAndroidDelegate::kEmptyStringIndex;
|
||||
break;
|
||||
case StringAttributeType::kLocale:
|
||||
buffer_int32[position++] = string_attribute_args.size();
|
||||
buffer[(*position)++] = string_attribute_args.size();
|
||||
std::shared_ptr<LocaleStringAttribute> locale_attribute =
|
||||
std::static_pointer_cast<LocaleStringAttribute>(attribute);
|
||||
string_attribute_args.push_back(
|
||||
@ -37,6 +37,18 @@ void putStringAttributesIntoBuffer(
|
||||
}
|
||||
}
|
||||
|
||||
void putStringIntoBuffer(const std::string& string,
|
||||
int32_t* buffer,
|
||||
size_t* position,
|
||||
std::vector<std::string>& strings) {
|
||||
if (string.empty()) {
|
||||
buffer[(*position)++] = PlatformViewAndroidDelegate::kEmptyStringIndex;
|
||||
} else {
|
||||
buffer[(*position)++] = strings.size();
|
||||
strings.push_back(string);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t flagsToInt64(flutter::SemanticsFlags flags) {
|
||||
int64_t result = 0;
|
||||
if (flags.isChecked != flutter::SemanticsCheckState::kNone) {
|
||||
@ -134,6 +146,7 @@ int64_t flagsToInt64(flutter::SemanticsFlags flags) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PlatformViewAndroidDelegate::PlatformViewAndroidDelegate(
|
||||
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
|
||||
@ -198,73 +211,33 @@ void PlatformViewAndroidDelegate::UpdateSemantics(
|
||||
buffer_float32[position++] = static_cast<float>(node.scrollExtentMax);
|
||||
buffer_float32[position++] = static_cast<float>(node.scrollExtentMin);
|
||||
|
||||
if (node.identifier.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.identifier);
|
||||
}
|
||||
|
||||
if (node.label.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.label);
|
||||
}
|
||||
putStringIntoBuffer(node.identifier, buffer_int32, &position, strings);
|
||||
|
||||
putStringIntoBuffer(node.label, buffer_int32, &position, strings);
|
||||
putStringAttributesIntoBuffer(node.labelAttributes, buffer_int32,
|
||||
position, string_attribute_args);
|
||||
if (node.value.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.value);
|
||||
}
|
||||
&position, string_attribute_args);
|
||||
|
||||
putStringIntoBuffer(node.value, buffer_int32, &position, strings);
|
||||
putStringAttributesIntoBuffer(node.valueAttributes, buffer_int32,
|
||||
position, string_attribute_args);
|
||||
if (node.increasedValue.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.increasedValue);
|
||||
}
|
||||
&position, string_attribute_args);
|
||||
|
||||
putStringIntoBuffer(node.increasedValue, buffer_int32, &position,
|
||||
strings);
|
||||
putStringAttributesIntoBuffer(node.increasedValueAttributes, buffer_int32,
|
||||
position, string_attribute_args);
|
||||
if (node.decreasedValue.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.decreasedValue);
|
||||
}
|
||||
&position, string_attribute_args);
|
||||
|
||||
putStringIntoBuffer(node.decreasedValue, buffer_int32, &position,
|
||||
strings);
|
||||
putStringAttributesIntoBuffer(node.decreasedValueAttributes, buffer_int32,
|
||||
position, string_attribute_args);
|
||||
&position, string_attribute_args);
|
||||
|
||||
if (node.hint.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.hint);
|
||||
}
|
||||
putStringIntoBuffer(node.hint, buffer_int32, &position, strings);
|
||||
putStringAttributesIntoBuffer(node.hintAttributes, buffer_int32,
|
||||
&position, string_attribute_args);
|
||||
|
||||
putStringAttributesIntoBuffer(node.hintAttributes, buffer_int32, position,
|
||||
string_attribute_args);
|
||||
|
||||
if (node.tooltip.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.tooltip);
|
||||
}
|
||||
|
||||
if (node.linkUrl.empty()) {
|
||||
buffer_int32[position++] = -1;
|
||||
} else {
|
||||
buffer_int32[position++] = strings.size();
|
||||
strings.push_back(node.linkUrl);
|
||||
}
|
||||
putStringIntoBuffer(node.tooltip, buffer_int32, &position, strings);
|
||||
putStringIntoBuffer(node.linkUrl, buffer_int32, &position, strings);
|
||||
putStringIntoBuffer(node.locale, buffer_int32, &position, strings);
|
||||
|
||||
buffer_int32[position++] = node.textDirection;
|
||||
buffer_float32[position++] = node.rect.left();
|
||||
@ -304,18 +277,10 @@ void PlatformViewAndroidDelegate::UpdateSemantics(
|
||||
const flutter::CustomAccessibilityAction& action = value.second;
|
||||
actions_buffer_int32[actions_position++] = action.id;
|
||||
actions_buffer_int32[actions_position++] = action.overrideId;
|
||||
if (action.label.empty()) {
|
||||
actions_buffer_int32[actions_position++] = -1;
|
||||
} else {
|
||||
actions_buffer_int32[actions_position++] = action_strings.size();
|
||||
action_strings.push_back(action.label);
|
||||
}
|
||||
if (action.hint.empty()) {
|
||||
actions_buffer_int32[actions_position++] = -1;
|
||||
} else {
|
||||
actions_buffer_int32[actions_position++] = action_strings.size();
|
||||
action_strings.push_back(action.hint);
|
||||
}
|
||||
putStringIntoBuffer(action.label, actions_buffer_int32, &actions_position,
|
||||
action_strings);
|
||||
putStringIntoBuffer(action.hint, actions_buffer_int32, &actions_position,
|
||||
action_strings);
|
||||
}
|
||||
|
||||
// Calling NewDirectByteBuffer in API level 22 and below with a size of zero
|
||||
|
||||
@ -16,11 +16,13 @@ namespace flutter {
|
||||
|
||||
class PlatformViewAndroidDelegate {
|
||||
public:
|
||||
static constexpr size_t kBytesPerNode = 50 * sizeof(int32_t);
|
||||
static constexpr size_t kBytesPerNode =
|
||||
51 * sizeof(int32_t); // The # fields in SemanticsNode
|
||||
static constexpr size_t kBytesPerChild = sizeof(int32_t);
|
||||
static constexpr size_t kBytesPerCustomAction = sizeof(int32_t);
|
||||
static constexpr size_t kBytesPerAction = 4 * sizeof(int32_t);
|
||||
static constexpr size_t kBytesPerStringAttribute = 4 * sizeof(int32_t);
|
||||
static constexpr int kEmptyStringIndex = -1;
|
||||
explicit PlatformViewAndroidDelegate(
|
||||
std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
|
||||
void UpdateSemantics(
|
||||
|
||||
@ -60,6 +60,7 @@ TEST(PlatformViewShell, UpdateSemanticsDoesFlutterViewUpdateSemantics) {
|
||||
buffer_int32[position++] = expected_strings.size(); // node0.tooltip
|
||||
expected_strings.push_back(node0.tooltip);
|
||||
buffer_int32[position++] = -1; // node0.linkUrl
|
||||
buffer_int32[position++] = -1; // node0.locale
|
||||
buffer_int32[position++] = node0.textDirection;
|
||||
buffer_float32[position++] = node0.rect.left();
|
||||
buffer_float32[position++] = node0.rect.top();
|
||||
@ -77,7 +78,7 @@ TEST(PlatformViewShell, UpdateSemanticsDoesFlutterViewUpdateSemantics) {
|
||||
delegate->UpdateSemantics(update, actions);
|
||||
}
|
||||
|
||||
TEST(PlatformViewShell, UpdateSemanticsDoesUpdatelinkUrl) {
|
||||
TEST(PlatformViewShell, UpdateSemanticsDoesUpdateLinkUrl) {
|
||||
auto jni_mock = std::make_shared<JNIMock>();
|
||||
auto delegate = std::make_unique<PlatformViewAndroidDelegate>(jni_mock);
|
||||
|
||||
@ -124,8 +125,76 @@ TEST(PlatformViewShell, UpdateSemanticsDoesUpdatelinkUrl) {
|
||||
buffer_int32[position++] = -1; // node0.hint
|
||||
buffer_int32[position++] = -1; // node0.hintAttributes
|
||||
buffer_int32[position++] = -1; // node0.tooltip
|
||||
buffer_int32[position++] = expected_strings.size(); // node0.tooltip
|
||||
buffer_int32[position++] = expected_strings.size(); // node0.linkUrl
|
||||
expected_strings.push_back(node0.linkUrl);
|
||||
buffer_int32[position++] = -1; // node0.locale
|
||||
buffer_int32[position++] = node0.textDirection;
|
||||
buffer_float32[position++] = node0.rect.left();
|
||||
buffer_float32[position++] = node0.rect.top();
|
||||
buffer_float32[position++] = node0.rect.right();
|
||||
buffer_float32[position++] = node0.rect.bottom();
|
||||
node0.transform.getColMajor(&buffer_float32[position]);
|
||||
position += 16;
|
||||
buffer_int32[position++] = 0; // node0.childrenInTraversalOrder.size();
|
||||
buffer_int32[position++] = 0; // node0.customAccessibilityActions.size();
|
||||
EXPECT_CALL(*jni_mock,
|
||||
FlutterViewUpdateSemantics(expected_buffer, expected_strings,
|
||||
expected_string_attribute_args));
|
||||
// Creates empty custom actions.
|
||||
flutter::CustomAccessibilityActionUpdates actions;
|
||||
delegate->UpdateSemantics(update, actions);
|
||||
}
|
||||
|
||||
TEST(PlatformViewShell, UpdateSemanticsDoesUpdateLocale) {
|
||||
auto jni_mock = std::make_shared<JNIMock>();
|
||||
auto delegate = std::make_unique<PlatformViewAndroidDelegate>(jni_mock);
|
||||
|
||||
flutter::SemanticsNodeUpdates update;
|
||||
flutter::SemanticsNode node0;
|
||||
node0.id = 0;
|
||||
node0.identifier = "identifier";
|
||||
node0.label = "label";
|
||||
node0.locale = "es-MX";
|
||||
update.insert(std::make_pair(0, node0));
|
||||
|
||||
std::vector<uint8_t> expected_buffer(
|
||||
PlatformViewAndroidDelegate::kBytesPerNode);
|
||||
std::vector<std::vector<uint8_t>> expected_string_attribute_args(0);
|
||||
size_t position = 0;
|
||||
int32_t* buffer_int32 = reinterpret_cast<int32_t*>(&expected_buffer[0]);
|
||||
float* buffer_float32 = reinterpret_cast<float*>(&expected_buffer[0]);
|
||||
std::vector<std::string> expected_strings;
|
||||
buffer_int32[position++] = node0.id;
|
||||
std::memcpy(&buffer_int32[position], &node0.flags, 2);
|
||||
position += 2;
|
||||
buffer_int32[position++] = node0.actions;
|
||||
buffer_int32[position++] = node0.maxValueLength;
|
||||
buffer_int32[position++] = node0.currentValueLength;
|
||||
buffer_int32[position++] = node0.textSelectionBase;
|
||||
buffer_int32[position++] = node0.textSelectionExtent;
|
||||
buffer_int32[position++] = node0.platformViewId;
|
||||
buffer_int32[position++] = node0.scrollChildren;
|
||||
buffer_int32[position++] = node0.scrollIndex;
|
||||
buffer_float32[position++] = static_cast<float>(node0.scrollPosition);
|
||||
buffer_float32[position++] = static_cast<float>(node0.scrollExtentMax);
|
||||
buffer_float32[position++] = static_cast<float>(node0.scrollExtentMin);
|
||||
buffer_int32[position++] = expected_strings.size(); // node0.identifier
|
||||
expected_strings.push_back(node0.identifier);
|
||||
buffer_int32[position++] = expected_strings.size(); // node0.label
|
||||
expected_strings.push_back(node0.label);
|
||||
buffer_int32[position++] = -1; // node0.labelAttributes
|
||||
buffer_int32[position++] = -1; // node0.value
|
||||
buffer_int32[position++] = -1; // node0.valueAttributes
|
||||
buffer_int32[position++] = -1; // node0.increasedValue
|
||||
buffer_int32[position++] = -1; // node0.increasedValueAttributes
|
||||
buffer_int32[position++] = -1; // node0.decreasedValue
|
||||
buffer_int32[position++] = -1; // node0.decreasedValueAttributes
|
||||
buffer_int32[position++] = -1; // node0.hint
|
||||
buffer_int32[position++] = -1; // node0.hintAttributes
|
||||
buffer_int32[position++] = -1; // node0.tooltip
|
||||
buffer_int32[position++] = -1; // node0.linkUrl
|
||||
buffer_int32[position++] = expected_strings.size();
|
||||
expected_strings.push_back(node0.locale); // node0.locale
|
||||
buffer_int32[position++] = node0.textDirection;
|
||||
buffer_float32[position++] = node0.rect.left();
|
||||
buffer_float32[position++] = node0.rect.top();
|
||||
@ -219,6 +288,7 @@ TEST(PlatformViewShell,
|
||||
{locale_attribute->locale.begin(), locale_attribute->locale.end()});
|
||||
buffer_int32[position++] = -1; // node0.tooltip
|
||||
buffer_int32[position++] = -1; // node0.linkUrl
|
||||
buffer_int32[position++] = -1; // node0.locale
|
||||
buffer_int32[position++] = node0.textDirection;
|
||||
buffer_float32[position++] = node0.rect.left();
|
||||
buffer_float32[position++] = node0.rect.top();
|
||||
|
||||
@ -114,7 +114,8 @@ public class AccessibilityBridgeTest {
|
||||
Context context = mock(Context.class);
|
||||
when(mockRootView.getContext()).thenReturn(context);
|
||||
final int position = 88;
|
||||
// The getBoundsInScreen() in createAccessibilityNodeInfo() needs View.getLocationOnScreen()
|
||||
// The getBoundsInScreen() in createAccessibilityNodeInfo() needs
|
||||
// View.getLocationOnScreen()
|
||||
doAnswer(
|
||||
invocation -> {
|
||||
int[] outLocation = (int[]) invocation.getArguments()[0];
|
||||
@ -152,12 +153,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
when(mockManager.isTouchExplorationEnabled()).thenReturn(false);
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
}
|
||||
|
||||
@ -173,12 +174,12 @@ public class AccessibilityBridgeTest {
|
||||
when(mockManager.isTouchExplorationEnabled()).thenReturn(false);
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
ArgumentCaptor<AccessibilityManager.TouchExplorationStateChangeListener> listenerCaptor =
|
||||
ArgumentCaptor.forClass(AccessibilityManager.TouchExplorationStateChangeListener.class);
|
||||
verify(mockManager).addTouchExplorationStateChangeListener(listenerCaptor.capture());
|
||||
@ -710,12 +711,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -749,12 +750,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -788,12 +789,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -842,6 +843,47 @@ public class AccessibilityBridgeTest {
|
||||
assertEquals(actual.getSpanEnd(spellOutSpan), 9);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itBuildsAttributedStringWithLocale() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
AccessibilityManager mockManager = mock(AccessibilityManager.class);
|
||||
View mockRootView = mock(View.class);
|
||||
Context context = mock(Context.class);
|
||||
when(mockRootView.getContext()).thenReturn(context);
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
when(mockManager.isEnabled()).thenReturn(true);
|
||||
|
||||
TestSemanticsNode root = new TestSemanticsNode();
|
||||
root.id = 0;
|
||||
root.label = "label";
|
||||
root.locale = "es-MX";
|
||||
|
||||
TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
|
||||
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
|
||||
AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
|
||||
SpannableString actual = (SpannableString) nodeInfo.getContentDescription();
|
||||
assertEquals(actual.toString(), "label");
|
||||
Object[] objectSpans = actual.getSpans(0, actual.length(), Object.class);
|
||||
assertEquals(objectSpans.length, 1);
|
||||
LocaleSpan localeSpan = (LocaleSpan) objectSpans[0];
|
||||
assertEquals(localeSpan.getLocale().toLanguageTag(), "es-MX");
|
||||
assertEquals(actual.getSpanStart(localeSpan), 0);
|
||||
assertEquals(actual.getSpanEnd(localeSpan), actual.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(minSdk = API_LEVELS.FLUTTER_MIN)
|
||||
public void itSetsTextCorrectly() {
|
||||
@ -854,12 +896,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -914,12 +956,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -953,12 +995,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -998,12 +1040,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1036,12 +1078,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1072,12 +1114,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1118,12 +1160,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ accessibilityChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ accessibilityChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1171,12 +1213,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1225,19 +1267,20 @@ public class AccessibilityBridgeTest {
|
||||
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
verify(mockChannel)
|
||||
.setAccessibilityFeatures(
|
||||
ACCESSIBILITY_FEATURE_BOLD_TEXT | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
reset(mockChannel);
|
||||
|
||||
// Now verify that clearing the BOLD_TEXT flag doesn't touch any of the other flags.
|
||||
// Now verify that clearing the BOLD_TEXT flag doesn't touch any of the other
|
||||
// flags.
|
||||
// Ensure the DISABLE_ANIMATION flag will be set
|
||||
Settings.Global.putFloat(null, "transition_animation_scale", 0.0f);
|
||||
// Ensure the BOLD_TEXT flag will be cleared
|
||||
@ -1245,14 +1288,15 @@ public class AccessibilityBridgeTest {
|
||||
|
||||
accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
// setAccessibilityFeatures() will be called multiple times from AccessibilityBridge's
|
||||
// setAccessibilityFeatures() will be called multiple times from
|
||||
// AccessibilityBridge's
|
||||
// constructor, verify that the latest argument is correct
|
||||
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(mockChannel, atLeastOnce()).setAccessibilityFeatures(captor.capture());
|
||||
@ -1278,12 +1322,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ accessibilityChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ accessibilityChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1340,12 +1384,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ accessibilityChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ accessibilityChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1405,12 +1449,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1463,12 +1507,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1515,12 +1559,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1564,12 +1608,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1622,12 +1666,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1681,12 +1725,12 @@ public class AccessibilityBridgeTest {
|
||||
when(context.getPackageName()).thenReturn("test");
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ mockRootView,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ mockManager,
|
||||
/*contentResolver=*/ null,
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ mockRootView,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ mockManager,
|
||||
/* contentResolver= */ null,
|
||||
/* accessibilityViewEmbedder= */ mockViewEmbedder,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
ViewParent mockParent = mock(ViewParent.class);
|
||||
when(mockRootView.getParent()).thenReturn(mockParent);
|
||||
@ -1790,9 +1834,9 @@ public class AccessibilityBridgeTest {
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
rootAccessibilityView,
|
||||
/*accessibilityChannel=*/ null,
|
||||
/*accessibilityManager=*/ null,
|
||||
/*contentResolver=*/ null,
|
||||
/* accessibilityChannel= */ null,
|
||||
/* accessibilityManager= */ null,
|
||||
/* contentResolver= */ null,
|
||||
accessibilityViewEmbedder,
|
||||
accessibilityDelegate);
|
||||
|
||||
@ -1835,9 +1879,9 @@ public class AccessibilityBridgeTest {
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
rootAccessibilityView,
|
||||
/*accessibilityChannel=*/ null,
|
||||
/*accessibilityManager=*/ null,
|
||||
/*contentResolver=*/ null,
|
||||
/* accessibilityChannel= */ null,
|
||||
/* accessibilityManager= */ null,
|
||||
/* contentResolver= */ null,
|
||||
accessibilityViewEmbedder,
|
||||
accessibilityDelegate);
|
||||
|
||||
@ -1870,9 +1914,9 @@ public class AccessibilityBridgeTest {
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
rootAccessibilityView,
|
||||
/*accessibilityChannel=*/ null,
|
||||
/*accessibilityManager=*/ null,
|
||||
/*contentResolver=*/ null,
|
||||
/* accessibilityChannel= */ null,
|
||||
/* accessibilityManager= */ null,
|
||||
/* contentResolver= */ null,
|
||||
accessibilityViewEmbedder,
|
||||
accessibilityDelegate);
|
||||
|
||||
@ -1908,10 +1952,10 @@ public class AccessibilityBridgeTest {
|
||||
AccessibilityViewEmbedder accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ null,
|
||||
/*accessibilityChannel=*/ null,
|
||||
/*accessibilityManager=*/ null,
|
||||
/*contentResolver=*/ null,
|
||||
/* rootAccessibilityView= */ null,
|
||||
/* accessibilityChannel= */ null,
|
||||
/* accessibilityManager= */ null,
|
||||
/* contentResolver= */ null,
|
||||
accessibilityViewEmbedder,
|
||||
accessibilityDelegate);
|
||||
|
||||
@ -1936,14 +1980,15 @@ public class AccessibilityBridgeTest {
|
||||
|
||||
AccessibilityBridge accessibilityBridge =
|
||||
setUpBridge(
|
||||
/*rootAccessibilityView=*/ null,
|
||||
/*accessibilityChannel=*/ mockChannel,
|
||||
/*accessibilityManager=*/ null,
|
||||
/*contentResolver=*/ mockContentResolver,
|
||||
/*accessibilityViewEmbedder=*/ null,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
/* rootAccessibilityView= */ null,
|
||||
/* accessibilityChannel= */ mockChannel,
|
||||
/* accessibilityManager= */ null,
|
||||
/* contentResolver= */ mockContentResolver,
|
||||
/* accessibilityViewEmbedder= */ null,
|
||||
/* platformViewsAccessibilityDelegate= */ null);
|
||||
|
||||
// Capture the observer registered for Settings.Global.TRANSITION_ANIMATION_SCALE
|
||||
// Capture the observer registered for
|
||||
// Settings.Global.TRANSITION_ANIMATION_SCALE
|
||||
ArgumentCaptor<ContentObserver> observerCaptor = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
verify(mockContentResolver)
|
||||
.registerContentObserver(
|
||||
@ -2203,6 +2248,7 @@ public class AccessibilityBridgeTest {
|
||||
List<TestStringAttribute> hintAttributes;
|
||||
String tooltip = null;
|
||||
String linkUrl = null;
|
||||
String locale = null;
|
||||
int textDirection = 0;
|
||||
float left = 0.0f;
|
||||
float top = 0.0f;
|
||||
@ -2272,6 +2318,12 @@ public class AccessibilityBridgeTest {
|
||||
strings.add(linkUrl);
|
||||
bytes.putInt(strings.size() - 1);
|
||||
}
|
||||
if (locale == null) {
|
||||
bytes.putInt(-1);
|
||||
} else {
|
||||
strings.add(locale);
|
||||
bytes.putInt(strings.size() - 1);
|
||||
}
|
||||
bytes.putInt(textDirection);
|
||||
bytes.putFloat(left);
|
||||
bytes.putFloat(top);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user