Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b6ceff55
Unverified
Commit
b6ceff55
authored
May 26, 2018
by
Jonah Williams
Committed by
GitHub
May 26, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
re-add semantic service events for tap and longPress (#17918)
parent
7587f54b
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
289 additions
and
1 deletion
+289
-1
feedback.dart
packages/flutter/lib/src/material/feedback.dart
+4
-0
toggleable.dart
packages/flutter/lib/src/material/toggleable.dart
+1
-0
object.dart
packages/flutter/lib/src/rendering/object.dart
+19
-0
semantics_event.dart
packages/flutter/lib/src/semantics/semantics_event.dart
+26
-0
semantics_service.dart
packages/flutter/lib/src/semantics/semantics_service.dart
+2
-1
checkbox_test.dart
packages/flutter/test/material/checkbox_test.dart
+41
-0
feedback_test.dart
packages/flutter/test/material/feedback_test.dart
+65
-0
radio_test.dart
packages/flutter/test/material/radio_test.dart
+37
-0
switch_test.dart
packages/flutter/test/material/switch_test.dart
+46
-0
tooltip_test.dart
packages/flutter/test/material/tooltip_test.dart
+48
-0
No files found.
packages/flutter/lib/src/material/feedback.dart
View file @
b6ceff55
...
...
@@ -4,6 +4,8 @@
import
'dart:async'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/semantics.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -86,6 +88,7 @@ class Feedback {
/// * [wrapForTap] to trigger platform-specific feedback before executing a
/// [GestureTapCallback].
static
Future
<
Null
>
forTap
(
BuildContext
context
)
async
{
context
.
findRenderObject
().
sendSemanticsEvent
(
const
TapSemanticEvent
());
switch
(
_platform
(
context
))
{
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
...
...
@@ -124,6 +127,7 @@ class Feedback {
/// * [wrapForLongPress] to trigger platform-specific feedback before
/// executing a [GestureLongPressCallback].
static
Future
<
Null
>
forLongPress
(
BuildContext
context
)
{
context
.
findRenderObject
().
sendSemanticsEvent
(
const
LongPressSemanticsEvent
());
switch
(
_platform
(
context
))
{
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
...
...
packages/flutter/lib/src/material/toggleable.dart
View file @
b6ceff55
...
...
@@ -291,6 +291,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
onChanged
(
false
);
break
;
}
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
void
_handleTapUp
(
TapUpDetails
details
)
{
...
...
packages/flutter/lib/src/rendering/object.dart
View file @
b6ceff55
...
...
@@ -2186,6 +2186,25 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
// Nothing to do by default.
}
/// Sends a [SemanticsEvent] associated with this render object's [SemanticsNode].
///
/// If this render object has no semantics information, the first parent
/// render object with a non-null semantic node is used.
///
/// If semantics are disabled, no events are dispatched.
///
/// See [SemanticsNode.sendEvent] for a full description of the behavior.
void
sendSemanticsEvent
(
SemanticsEvent
semanticsEvent
)
{
if
(
owner
.
semanticsOwner
==
null
)
return
;
if
(
_semantics
!=
null
)
{
_semantics
.
sendEvent
(
semanticsEvent
);
}
else
if
(
parent
!=
null
)
{
final
RenderObject
renderParent
=
parent
;
renderParent
.
sendSemanticsEvent
(
semanticsEvent
);
}
}
// Use [_semanticsConfiguration] to access.
SemanticsConfiguration
_cachedSemanticsConfiguration
;
...
...
packages/flutter/lib/src/semantics/semantics_event.dart
View file @
b6ceff55
...
...
@@ -107,3 +107,29 @@ class TooltipSemanticsEvent extends SemanticsEvent {
};
}
}
/// An event which triggers long press semantic feedback.
///
/// Currently only honored on Android. Triggers a long-press specific sound
/// when TalkBack is enabled.
class
LongPressSemanticsEvent
extends
SemanticsEvent
{
/// Constructs an event that triggers a long-press semantic feedback by the platform.
const
LongPressSemanticsEvent
()
:
super
(
'longPress'
);
@override
Map
<
String
,
dynamic
>
getDataMap
()
=>
const
<
String
,
dynamic
>{};
}
/// An event which triggers tap semantic feedback.
///
/// Currently only honored on Android. Triggers a tap specific sound when
/// TalkBack is enabled.
class
TapSemanticEvent
extends
SemanticsEvent
{
/// Constructs an event that triggers a long-press semantic feedback by the platform.
const
TapSemanticEvent
()
:
super
(
'tap'
);
@override
Map
<
String
,
dynamic
>
getDataMap
()
=>
const
<
String
,
dynamic
>{};
}
packages/flutter/lib/src/semantics/semantics_service.dart
View file @
b6ceff55
...
...
@@ -34,7 +34,8 @@ class SemanticsService {
/// Sends a semantic announcement of a tooltip.
///
/// This is only used by Android.
/// Currently only honored on Android. The contents of [message] will be
/// read by TalkBack.
static
Future
<
Null
>
tooltip
(
String
message
)
async
{
final
TooltipSemanticsEvent
event
=
new
TooltipSemanticsEvent
(
message
);
await
SystemChannels
.
accessibility
.
send
(
event
.
toMap
());
...
...
packages/flutter/test/material/checkbox_test.dart
View file @
b6ceff55
...
...
@@ -5,6 +5,7 @@
import
'dart:ui'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
...
...
@@ -200,4 +201,44 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
null
);
});
testWidgets
(
'has semantic events'
,
(
WidgetTester
tester
)
async
{
dynamic
semanticEvent
;
bool
checkboxValue
=
false
;
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
semanticEvent
=
message
;
});
final
SemanticsTester
semanticsTester
=
new
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
new
Material
(
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Checkbox
(
value:
checkboxValue
,
onChanged:
(
bool
value
)
{
setState
(()
{
checkboxValue
=
value
;
});
},
);
},
),
),
);
await
tester
.
tap
(
find
.
byType
(
Checkbox
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Checkbox
));
expect
(
checkboxValue
,
true
);
expect
(
semanticEvent
,
<
String
,
dynamic
>{
'type'
:
'tap'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
tap
),
true
);
SystemChannels
.
accessibility
.
setMockMessageHandler
(
null
);
semanticsTester
.
dispose
();
});
}
packages/flutter/test/material/feedback_test.dart
View file @
b6ceff55
...
...
@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../widgets/semantics_tester.dart'
;
import
'feedback_tester.dart'
;
void
main
(
)
{
...
...
@@ -21,8 +25,23 @@ void main () {
});
group
(
'Feedback on Android'
,
()
{
List
<
Map
<
String
,
Object
>>
semanticEvents
;
setUp
(()
{
semanticEvents
=
<
Map
<
String
,
Object
>>[];
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
final
Map
<
dynamic
,
dynamic
>
typedMessage
=
message
;
semanticEvents
.
add
(
typedMessage
.
cast
<
String
,
Object
>());
});
});
tearDown
(()
{
SystemChannels
.
accessibility
.
setMockMessageHandler
(
null
);
});
testWidgets
(
'forTap'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semanticsTester
=
new
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
new
TestWidget
(
tapHandler:
(
BuildContext
context
)
{
return
()
=>
Feedback
.
forTap
(
context
);
...
...
@@ -31,14 +50,27 @@ void main () {
await
tester
.
pumpAndSettle
(
kWaitDuration
);
expect
(
feedback
.
hapticCount
,
0
);
expect
(
feedback
.
clickSoundCount
,
0
);
expect
(
semanticEvents
,
isEmpty
);
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pumpAndSettle
(
kWaitDuration
);
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
GestureDetector
));
expect
(
feedback
.
hapticCount
,
0
);
expect
(
feedback
.
clickSoundCount
,
1
);
expect
(
semanticEvents
.
single
,
<
String
,
dynamic
>{
'type'
:
'tap'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
tap
),
true
);
semanticsTester
.
dispose
();
});
testWidgets
(
'forTap Wrapper'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semanticsTester
=
new
SemanticsTester
(
tester
);
int
callbackCount
=
0
;
final
VoidCallback
callback
=
()
{
callbackCount
++;
...
...
@@ -56,12 +88,24 @@ void main () {
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pumpAndSettle
(
kWaitDuration
);
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
GestureDetector
));
expect
(
feedback
.
hapticCount
,
0
);
expect
(
feedback
.
clickSoundCount
,
1
);
expect
(
callbackCount
,
1
);
expect
(
semanticEvents
.
single
,
<
String
,
dynamic
>{
'type'
:
'tap'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
tap
),
true
);
semanticsTester
.
dispose
();
});
testWidgets
(
'forLongPress'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semanticsTester
=
new
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
new
TestWidget
(
longPressHandler:
(
BuildContext
context
)
{
return
()
=>
Feedback
.
forLongPress
(
context
);
...
...
@@ -73,11 +117,22 @@ void main () {
await
tester
.
longPress
(
find
.
text
(
'X'
));
await
tester
.
pumpAndSettle
(
kWaitDuration
);
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
GestureDetector
));
expect
(
feedback
.
hapticCount
,
1
);
expect
(
feedback
.
clickSoundCount
,
0
);
expect
(
semanticEvents
.
single
,
<
String
,
dynamic
>{
'type'
:
'longPress'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
longPress
),
true
);
semanticsTester
.
dispose
();
});
testWidgets
(
'forLongPress Wrapper'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semanticsTester
=
new
SemanticsTester
(
tester
);
int
callbackCount
=
0
;
final
VoidCallback
callback
=
()
{
callbackCount
++;
...
...
@@ -89,6 +144,8 @@ void main () {
},
));
await
tester
.
pumpAndSettle
(
kWaitDuration
);
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
GestureDetector
));
expect
(
feedback
.
hapticCount
,
0
);
expect
(
feedback
.
clickSoundCount
,
0
);
expect
(
callbackCount
,
0
);
...
...
@@ -98,6 +155,14 @@ void main () {
expect
(
feedback
.
hapticCount
,
1
);
expect
(
feedback
.
clickSoundCount
,
0
);
expect
(
callbackCount
,
1
);
expect
(
semanticEvents
.
single
,
<
String
,
dynamic
>{
'type'
:
'longPress'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
longPress
),
true
);
semanticsTester
.
dispose
();
});
});
...
...
packages/flutter/test/material/radio_test.dart
View file @
b6ceff55
...
...
@@ -5,6 +5,7 @@
import
'dart:ui'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
...
...
@@ -182,4 +183,40 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'has semantic events'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
new
SemanticsTester
(
tester
);
final
Key
key
=
new
UniqueKey
();
dynamic
semanticEvent
;
int
radioValue
=
2
;
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
semanticEvent
=
message
;
});
await
tester
.
pumpWidget
(
new
Material
(
child:
new
Radio
<
int
>(
key:
key
,
value:
1
,
groupValue:
radioValue
,
onChanged:
(
int
i
)
{
radioValue
=
i
;
},
),
));
await
tester
.
tap
(
find
.
byKey
(
key
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byKey
(
key
));
expect
(
radioValue
,
1
);
expect
(
semanticEvent
,
<
String
,
dynamic
>{
'type'
:
'tap'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
tap
),
true
);
semantics
.
dispose
();
SystemChannels
.
accessibility
.
setMockMessageHandler
(
null
);
});
}
packages/flutter/test/material/switch_test.dart
View file @
b6ceff55
...
...
@@ -4,9 +4,11 @@
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../rendering/mock_canvas.dart'
;
import
'../widgets/semantics_tester.dart'
;
void
main
(
)
{
testWidgets
(
'Switch can toggle on tap'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -228,4 +230,48 @@ void main() {
expect
(
value
,
isTrue
);
expect
(
tester
.
hasRunningAnimations
,
false
);
});
testWidgets
(
'switch has semantic events'
,
(
WidgetTester
tester
)
async
{
dynamic
semanticEvent
;
bool
value
=
false
;
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
semanticEvent
=
message
;
});
final
SemanticsTester
semanticsTester
=
new
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Material
(
child:
new
Center
(
child:
new
Switch
(
value:
value
,
onChanged:
(
bool
newValue
)
{
setState
(()
{
value
=
newValue
;
});
},
),
),
);
},
),
),
);
await
tester
.
tap
(
find
.
byType
(
Switch
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Switch
));
expect
(
value
,
true
);
expect
(
semanticEvent
,
<
String
,
dynamic
>{
'type'
:
'tap'
,
'nodeId'
:
object
.
debugSemantics
.
id
,
'data'
:
<
String
,
dynamic
>{},
});
expect
(
object
.
debugSemantics
.
getSemanticsData
().
hasAction
(
SemanticsAction
.
tap
),
true
);
semanticsTester
.
dispose
();
SystemChannels
.
accessibility
.
setMockMessageHandler
(
null
);
});
}
packages/flutter/test/material/tooltip_test.dart
View file @
b6ceff55
...
...
@@ -4,6 +4,7 @@ import 'dart:ui';
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
...
...
@@ -676,4 +677,51 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'has semantic events'
,
(
WidgetTester
tester
)
async
{
final
List
<
dynamic
>
semanticEvents
=
<
dynamic
>[];
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
semanticEvents
.
add
(
message
);
});
final
SemanticsTester
semantics
=
new
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
new
Center
(
child:
new
Tooltip
(
message:
'Foo'
,
child:
new
Container
(
width:
100.0
,
height:
100.0
,
color:
Colors
.
green
[
500
],
),
),
),
),
);
await
tester
.
longPress
(
find
.
byType
(
Tooltip
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Tooltip
));
expect
(
semanticEvents
,
unorderedEquals
(<
dynamic
>[
<
String
,
dynamic
>{
'type'
:
'longPress'
,
'nodeId'
:
findDebugSemantics
(
object
).
id
,
'data'
:
<
String
,
dynamic
>{},
},
<
String
,
dynamic
>{
'type'
:
'tooltip'
,
'data'
:
<
String
,
dynamic
>{
'message'
:
'Foo'
,
},
},
]));
semantics
.
dispose
();
SystemChannels
.
accessibility
.
setMockMessageHandler
(
null
);
});
}
SemanticsNode
findDebugSemantics
(
RenderObject
object
)
{
if
(
object
.
debugSemantics
!=
null
)
return
object
.
debugSemantics
;
return
findDebugSemantics
(
object
.
parent
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment