Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
weixin_43355755
engine
提交
a7899f05
E
engine
项目概览
weixin_43355755
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
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,发现更多精彩内容 >>
未验证
提交
a7899f05
编写于
8月 16, 2021
作者:
C
chunhtai
提交者:
GitHub
8月 16, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Issues/79528 reland (#28117)
上级
90c9a915
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
183 addition
and
6 deletion
+183
-6
shell/platform/android/io/flutter/view/AccessibilityBridge.java
...platform/android/io/flutter/view/AccessibilityBridge.java
+17
-6
shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java
...android/test/io/flutter/view/AccessibilityBridgeTest.java
+166
-0
未找到文件。
shell/platform/android/io/flutter/view/AccessibilityBridge.java
浏览文件 @
a7899f05
...
...
@@ -1020,28 +1020,39 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
}
case
AccessibilityNodeInfo
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
:
{
// Focused semantics node must be reset before sending the
// TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED event. Otherwise,
// TalkBack may think the node is still focused.
if
(
accessibilityFocusedSemanticsNode
!=
null
&&
accessibilityFocusedSemanticsNode
.
id
==
virtualViewId
)
{
accessibilityFocusedSemanticsNode
=
null
;
}
if
(
embeddedAccessibilityFocusedNodeId
!=
null
&&
embeddedAccessibilityFocusedNodeId
==
virtualViewId
)
{
embeddedAccessibilityFocusedNodeId
=
null
;
}
accessibilityChannel
.
dispatchSemanticsAction
(
virtualViewId
,
Action
.
DID_LOSE_ACCESSIBILITY_FOCUS
);
sendAccessibilityEvent
(
virtualViewId
,
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
);
accessibilityFocusedSemanticsNode
=
null
;
embeddedAccessibilityFocusedNodeId
=
null
;
return
true
;
}
case
AccessibilityNodeInfo
.
ACTION_ACCESSIBILITY_FOCUS
:
{
accessibilityChannel
.
dispatchSemanticsAction
(
virtualViewId
,
Action
.
DID_GAIN_ACCESSIBILITY_FOCUS
);
sendAccessibilityEvent
(
virtualViewId
,
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUSED
);
if
(
accessibilityFocusedSemanticsNode
==
null
)
{
// When Android focuses a node, it doesn't invalidate the view.
// (It does when it sends ACTION_CLEAR_ACCESSIBILITY_FOCUS, so
// we only have to worry about this when the focused node is null.)
rootAccessibilityView
.
invalidate
();
}
// Focused semantics node must be set before sending the TYPE_VIEW_ACCESSIBILITY_FOCUSED
// event. Otherwise, TalkBack may think the node is not focused yet.
accessibilityFocusedSemanticsNode
=
semanticsNode
;
accessibilityChannel
.
dispatchSemanticsAction
(
virtualViewId
,
Action
.
DID_GAIN_ACCESSIBILITY_FOCUS
);
sendAccessibilityEvent
(
virtualViewId
,
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUSED
);
if
(
semanticsNode
.
hasAction
(
Action
.
INCREASE
)
||
semanticsNode
.
hasAction
(
Action
.
DECREASE
))
{
// SeekBars only announce themselves after this event.
...
...
shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java
浏览文件 @
a7899f05
...
...
@@ -5,6 +5,7 @@
package
io.flutter.view
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Matchers
.
eq
;
...
...
@@ -46,6 +47,7 @@ import java.util.List;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.mockito.ArgumentCaptor
;
import
org.mockito.invocation.InvocationOnMock
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.RuntimeEnvironment
;
import
org.robolectric.annotation.Config
;
...
...
@@ -798,6 +800,170 @@ public class AccessibilityBridgeTest {
assertEquals
(
nodeInfo
.
getTextSelectionEnd
(),
expectedEnd
);
}
@Test
public
void
itPerformsClearAccessibilityFocusCorrectly
()
{
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
=
"root"
;
TestSemanticsNode
node1
=
new
TestSemanticsNode
();
node1
.
id
=
1
;
node1
.
value
=
"some text"
;
root
.
children
.
add
(
node1
);
TestSemanticsUpdate
testSemanticsUpdate
=
root
.
toUpdate
();
testSemanticsUpdate
.
sendUpdateToBridge
(
accessibilityBridge
);
accessibilityBridge
.
performAction
(
0
,
AccessibilityNodeInfo
.
ACTION_ACCESSIBILITY_FOCUS
,
null
);
AccessibilityNodeInfo
nodeInfo
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertTrue
(
nodeInfo
.
isAccessibilityFocused
());
// Clear focus on non-focused node shouldn't do anything
accessibilityBridge
.
performAction
(
1
,
AccessibilityNodeInfo
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
,
null
);
nodeInfo
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertTrue
(
nodeInfo
.
isAccessibilityFocused
());
// Now, clear the focus for real.
accessibilityBridge
.
performAction
(
0
,
AccessibilityNodeInfo
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
,
null
);
nodeInfo
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertFalse
(
nodeInfo
.
isAccessibilityFocused
());
}
@Test
public
void
itSetsFocusedNodeBeforeSendingEvent
()
{
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
=
"root"
;
TestSemanticsUpdate
testSemanticsUpdate
=
root
.
toUpdate
();
testSemanticsUpdate
.
sendUpdateToBridge
(
accessibilityBridge
);
class
Verifier
{
public
Verifier
(
AccessibilityBridge
accessibilityBridge
)
{
this
.
accessibilityBridge
=
accessibilityBridge
;
}
public
AccessibilityBridge
accessibilityBridge
;
public
boolean
verified
=
false
;
public
boolean
verify
(
InvocationOnMock
invocation
)
{
AccessibilityEvent
event
=
(
AccessibilityEvent
)
invocation
.
getArguments
()[
1
];
assertEquals
(
event
.
getEventType
(),
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUSED
);
// The accessibility focus must be set before sending out
// the TYPE_VIEW_ACCESSIBILITY_FOCUSED event.
AccessibilityNodeInfo
nodeInfo
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertTrue
(
nodeInfo
.
isAccessibilityFocused
());
verified
=
true
;
return
true
;
}
};
Verifier
verifier
=
new
Verifier
(
accessibilityBridge
);
when
(
mockParent
.
requestSendAccessibilityEvent
(
eq
(
mockRootView
),
any
(
AccessibilityEvent
.
class
)))
.
thenAnswer
(
invocation
->
verifier
.
verify
(
invocation
));
accessibilityBridge
.
performAction
(
0
,
AccessibilityNodeInfo
.
ACTION_ACCESSIBILITY_FOCUS
,
null
);
assertTrue
(
verifier
.
verified
);
}
@Test
public
void
itClearsFocusedNodeBeforeSendingEvent
()
{
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
=
"root"
;
TestSemanticsUpdate
testSemanticsUpdate
=
root
.
toUpdate
();
testSemanticsUpdate
.
sendUpdateToBridge
(
accessibilityBridge
);
// Set the focus on root.
accessibilityBridge
.
performAction
(
0
,
AccessibilityNodeInfo
.
ACTION_ACCESSIBILITY_FOCUS
,
null
);
AccessibilityNodeInfo
nodeInfo
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertTrue
(
nodeInfo
.
isAccessibilityFocused
());
class
Verifier
{
public
Verifier
(
AccessibilityBridge
accessibilityBridge
)
{
this
.
accessibilityBridge
=
accessibilityBridge
;
}
public
AccessibilityBridge
accessibilityBridge
;
public
boolean
verified
=
false
;
public
boolean
verify
(
InvocationOnMock
invocation
)
{
AccessibilityEvent
event
=
(
AccessibilityEvent
)
invocation
.
getArguments
()[
1
];
assertEquals
(
event
.
getEventType
(),
AccessibilityEvent
.
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
);
// The accessibility focus must be cleared before sending out
// the TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED event.
AccessibilityNodeInfo
nodeInfo
=
accessibilityBridge
.
createAccessibilityNodeInfo
(
0
);
assertFalse
(
nodeInfo
.
isAccessibilityFocused
());
verified
=
true
;
return
true
;
}
};
Verifier
verifier
=
new
Verifier
(
accessibilityBridge
);
when
(
mockParent
.
requestSendAccessibilityEvent
(
eq
(
mockRootView
),
any
(
AccessibilityEvent
.
class
)))
.
thenAnswer
(
invocation
->
verifier
.
verify
(
invocation
));
accessibilityBridge
.
performAction
(
0
,
AccessibilityNodeInfo
.
ACTION_CLEAR_ACCESSIBILITY_FOCUS
,
null
);
assertTrue
(
verifier
.
verified
);
}
@Test
public
void
itCanPredictCursorMovementsWithGranularityWord
()
{
AccessibilityChannel
mockChannel
=
mock
(
AccessibilityChannel
.
class
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录