Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
99810261
E
engine
项目概览
sxychenjing
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
99810261
编写于
10月 13, 2020
作者:
E
Emmanuel Garcia
提交者:
GitHub
10月 13, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Allow TalkBack navigation while a platform view is rendered (#21719)
上级
0b72b87f
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
118 addition
and
6 deletion
+118
-6
shell/platform/android/io/flutter/view/AccessibilityBridge.java
...platform/android/io/flutter/view/AccessibilityBridge.java
+34
-6
shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java
...android/test/io/flutter/view/AccessibilityBridgeTest.java
+84
-0
未找到文件。
shell/platform/android/io/flutter/view/AccessibilityBridge.java
浏览文件 @
99810261
...
...
@@ -28,6 +28,7 @@ import androidx.annotation.RequiresApi;
import
androidx.annotation.VisibleForTesting
;
import
io.flutter.BuildConfig
;
import
io.flutter.Log
;
import
io.flutter.embedding.android.FlutterActivity
;
import
io.flutter.embedding.engine.systemchannels.AccessibilityChannel
;
import
io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate
;
import
io.flutter.util.Predicate
;
...
...
@@ -541,12 +542,22 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
return
null
;
}
// Generate accessibility node for platform views using a virtual display.
//
// In this case, register the accessibility node in the view embedder,
// so the accessibility tree can be mirrored as a subtree of the Flutter accessibility tree.
// This is in constrast to hybrid composition where the embeded view is in the view hiearchy,
// so it doesn't need to be mirrored.
//
// See the case down below for how hybrid composition is handled.
if
(
semanticsNode
.
platformViewId
!=
-
1
)
{
// For platform views we delegate the node creation to the accessibility view embedder.
View
embeddedView
=
platformViewsAccessibilityDelegate
.
getPlatformViewById
(
semanticsNode
.
platformViewId
);
Rect
bounds
=
semanticsNode
.
getGlobalRect
();
return
accessibilityViewEmbedder
.
getRootNode
(
embeddedView
,
semanticsNode
.
id
,
bounds
);
boolean
childUsesVirtualDisplay
=
!(
embeddedView
.
getContext
()
instanceof
FlutterActivity
);
if
(
childUsesVirtualDisplay
)
{
Rect
bounds
=
semanticsNode
.
getGlobalRect
();
return
accessibilityViewEmbedder
.
getRootNode
(
embeddedView
,
semanticsNode
.
id
,
bounds
);
}
}
AccessibilityNodeInfo
result
=
...
...
@@ -823,11 +834,28 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
}
for
(
SemanticsNode
child
:
semanticsNode
.
childrenInTraversalOrder
)
{
if
(!
child
.
hasFlag
(
Flag
.
IS_HIDDEN
))
{
result
.
addChild
(
rootAccessibilityView
,
child
.
id
);
if
(
child
.
hasFlag
(
Flag
.
IS_HIDDEN
))
{
continue
;
}
if
(
child
.
platformViewId
!=
-
1
)
{
View
embeddedView
=
platformViewsAccessibilityDelegate
.
getPlatformViewById
(
child
.
platformViewId
);
// Add the embeded view as a child of the current accessibility node if it's using
// hybrid composition.
//
// In this case, the view is in the Activity's view hierarchy, so it doesn't need to be
// mirrored.
//
// See the case above for how virtual displays are handled.
boolean
childUsesHybridComposition
=
embeddedView
.
getContext
()
instanceof
FlutterActivity
;
if
(
childUsesHybridComposition
)
{
result
.
addChild
(
embeddedView
);
continue
;
}
}
result
.
addChild
(
rootAccessibilityView
,
child
.
id
);
}
return
result
;
}
...
...
shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java
浏览文件 @
99810261
...
...
@@ -5,6 +5,7 @@
package
io.flutter.view
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
mockito
.
Matchers
.
eq
;
import
static
org
.
mockito
.
Mockito
.
any
;
import
static
org
.
mockito
.
Mockito
.
anyInt
;
...
...
@@ -16,14 +17,17 @@ import static org.mockito.Mockito.verify;
import
static
org
.
mockito
.
Mockito
.
when
;
import
android.annotation.TargetApi
;
import
android.app.Activity
;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.graphics.Rect
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.ViewParent
;
import
android.view.accessibility.AccessibilityEvent
;
import
android.view.accessibility.AccessibilityManager
;
import
android.view.accessibility.AccessibilityNodeInfo
;
import
io.flutter.embedding.android.FlutterActivity
;
import
io.flutter.embedding.engine.systemchannels.AccessibilityChannel
;
import
io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate
;
import
java.nio.ByteBuffer
;
...
...
@@ -33,6 +37,7 @@ import org.junit.Test;
import
org.junit.runner.RunWith
;
import
org.mockito.ArgumentCaptor
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.RuntimeEnvironment
;
import
org.robolectric.annotation.Config
;
@Config
(
manifest
=
Config
.
NONE
)
...
...
@@ -198,6 +203,81 @@ public class AccessibilityBridgeTest {
accessibilityBridge
.
onAccessibilityHoverEvent
(
MotionEvent
.
obtain
(
1
,
1
,
1
,
-
10
,
-
10
,
0
));
}
@Test
public
void
itProducesPlatformViewNodeForHybridComposition
()
{
PlatformViewsAccessibilityDelegate
accessibilityDelegate
=
mock
(
PlatformViewsAccessibilityDelegate
.
class
);
Context
context
=
RuntimeEnvironment
.
application
.
getApplicationContext
();
View
rootAccessibilityView
=
new
View
(
context
);
AccessibilityViewEmbedder
accessibilityViewEmbedder
=
mock
(
AccessibilityViewEmbedder
.
class
);
AccessibilityBridge
accessibilityBridge
=
setUpBridge
(
rootAccessibilityView
,
/*accessibilityChannel=*/
null
,
/*accessibilityManager=*/
null
,
/*contentResolver=*/
null
,
accessibilityViewEmbedder
,
accessibilityDelegate
);
TestSemanticsNode
root
=
new
TestSemanticsNode
();
root
.
id
=
0
;
TestSemanticsNode
platformView
=
new
TestSemanticsNode
();
platformView
.
id
=
1
;
platformView
.
platformViewId
=
1
;
root
.
addChild
(
platformView
);
TestSemanticsUpdate
testSemanticsRootUpdate
=
root
.
toUpdate
();
accessibilityBridge
.
updateSemantics
(
testSemanticsRootUpdate
.
buffer
,
testSemanticsRootUpdate
.
strings
);
TestSemanticsUpdate
testSemanticsPlatformViewUpdate
=
platformView
.
toUpdate
();
accessibilityBridge
.
updateSemantics
(
testSemanticsPlatformViewUpdate
.
buffer
,
testSemanticsPlatformViewUpdate
.
strings
);
View
embeddedView
=
mock
(
View
.
class
);
when
(
accessibilityDelegate
.
getPlatformViewById
(
1
)).
thenReturn
(
embeddedView
);
when
(
embeddedView
.
getContext
()).
thenReturn
(
mock
(
FlutterActivity
.
class
));
AccessibilityNodeInfo
nodeInfo
=
mock
(
AccessibilityNodeInfo
.
class
);
when
(
embeddedView
.
createAccessibilityNodeInfo
()).
thenReturn
(
nodeInfo
);
AccessibilityNodeInfo
result
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertNotNull
(
result
);
assertEquals
(
result
.
getChildCount
(),
1
);
assertEquals
(
result
.
getClassName
(),
"android.view.View"
);
}
@Test
public
void
itProducesPlatformViewNodeForVirtualDisplay
()
{
PlatformViewsAccessibilityDelegate
accessibilityDelegate
=
mock
(
PlatformViewsAccessibilityDelegate
.
class
);
AccessibilityViewEmbedder
accessibilityViewEmbedder
=
mock
(
AccessibilityViewEmbedder
.
class
);
AccessibilityBridge
accessibilityBridge
=
setUpBridge
(
/*rootAccessibilityView=*/
null
,
/*accessibilityChannel=*/
null
,
/*accessibilityManager=*/
null
,
/*contentResolver=*/
null
,
accessibilityViewEmbedder
,
accessibilityDelegate
);
TestSemanticsNode
platformView
=
new
TestSemanticsNode
();
platformView
.
platformViewId
=
1
;
TestSemanticsUpdate
testSemanticsUpdate
=
platformView
.
toUpdate
();
accessibilityBridge
.
updateSemantics
(
testSemanticsUpdate
.
buffer
,
testSemanticsUpdate
.
strings
);
View
embeddedView
=
mock
(
View
.
class
);
when
(
accessibilityDelegate
.
getPlatformViewById
(
1
)).
thenReturn
(
embeddedView
);
when
(
embeddedView
.
getContext
()).
thenReturn
(
mock
(
Activity
.
class
));
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
verify
(
accessibilityViewEmbedder
).
getRootNode
(
eq
(
embeddedView
),
eq
(
0
),
any
(
Rect
.
class
));
}
@Test
public
void
releaseDropsChannelMessageHandler
()
{
AccessibilityChannel
mockChannel
=
mock
(
AccessibilityChannel
.
class
);
...
...
@@ -317,6 +397,10 @@ public class AccessibilityBridgeTest {
float
right
=
0.0f
;
float
bottom
=
0.0f
;
final
List
<
TestSemanticsNode
>
children
=
new
ArrayList
<
TestSemanticsNode
>();
public
void
addChild
(
TestSemanticsNode
child
)
{
children
.
add
(
child
);
}
// custom actions not supported.
TestSemanticsUpdate
toUpdate
()
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录