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
c68758fa
Unverified
Commit
c68758fa
authored
Jun 25, 2020
by
Greg Spencer
Committed by
GitHub
Jun 25, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement delayed key event synthesis support for Android (#59358)
parent
91bdf158
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
108 additions
and
8 deletions
+108
-8
raw_keyboard.dart
packages/flutter/lib/src/services/raw_keyboard.dart
+61
-5
focus_manager.dart
packages/flutter/lib/src/widgets/focus_manager.dart
+4
-3
raw_keyboard_test.dart
packages/flutter/test/services/raw_keyboard_test.dart
+43
-0
No files found.
packages/flutter/lib/src/services/raw_keyboard.dart
View file @
c68758fa
...
@@ -467,6 +467,13 @@ class RawKeyUpEvent extends RawKeyEvent {
...
@@ -467,6 +467,13 @@ class RawKeyUpEvent extends RawKeyEvent {
})
:
super
(
data:
data
,
character:
character
);
})
:
super
(
data:
data
,
character:
character
);
}
}
/// A callback type used by [RawKeyboard.keyEventHandler] to send key events to
/// a handler that can determine if the key has been handled or not.
///
/// The handler should return true if the key has been handled, and false if the
/// key was not handled. It must not return null.
typedef
RawKeyEventHandler
=
bool
Function
(
RawKeyEvent
event
);
/// An interface for listening to raw key events.
/// An interface for listening to raw key events.
///
///
/// Raw key events pass through as much information as possible from the
/// Raw key events pass through as much information as possible from the
...
@@ -477,6 +484,9 @@ class RawKeyUpEvent extends RawKeyEvent {
...
@@ -477,6 +484,9 @@ class RawKeyUpEvent extends RawKeyEvent {
/// buttons that are represented as keys. Typically used by games and other apps
/// buttons that are represented as keys. Typically used by games and other apps
/// that use keyboards for purposes other than text entry.
/// that use keyboards for purposes other than text entry.
///
///
/// These key events are typically only key events generated by a hardware
/// keyboard, and not those from software keyboards or input method editors.
///
/// See also:
/// See also:
///
///
/// * [RawKeyDownEvent] and [RawKeyUpEvent], the classes used to describe
/// * [RawKeyDownEvent] and [RawKeyUpEvent], the classes used to describe
...
@@ -494,20 +504,61 @@ class RawKeyboard {
...
@@ -494,20 +504,61 @@ class RawKeyboard {
final
List
<
ValueChanged
<
RawKeyEvent
>>
_listeners
=
<
ValueChanged
<
RawKeyEvent
>>[];
final
List
<
ValueChanged
<
RawKeyEvent
>>
_listeners
=
<
ValueChanged
<
RawKeyEvent
>>[];
/// Calls the listener every time the user presses or releases a key.
/// Register a listener that is called every time the user presses or releases
/// a hardware keyboard key.
///
/// Since the listeners have no way to indicate what they did with the event,
/// listeners are assumed to not handle the key event. These events will also
/// be distributed to other listeners, and to the [keyEventHandler].
///
/// Most applications prefer to use the focus system (see [Focus] and
/// [FocusManager]) to receive key events to the focused control instead of
/// this kind of passive listener.
///
///
/// Listeners can be removed with [removeListener].
/// Listeners can be removed with [removeListener].
void
addListener
(
ValueChanged
<
RawKeyEvent
>
listener
)
{
void
addListener
(
ValueChanged
<
RawKeyEvent
>
listener
)
{
_listeners
.
add
(
listener
);
_listeners
.
add
(
listener
);
}
}
/// Stop calling the listener every time the user presses or releases a key.
/// Stop calling the given listener every time the user presses or releases a
/// hardware keyboard key.
///
///
/// Listeners can be added with [addListener].
/// Listeners can be added with [addListener].
void
removeListener
(
ValueChanged
<
RawKeyEvent
>
listener
)
{
void
removeListener
(
ValueChanged
<
RawKeyEvent
>
listener
)
{
_listeners
.
remove
(
listener
);
_listeners
.
remove
(
listener
);
}
}
/// A handler for hardware keyboard events that will stop propagation if the
/// handler returns true.
///
/// Key events on the platform are given to Flutter to be handled by the
/// engine. If they are not handled, then the platform will continue to
/// distribute the keys (i.e. propagate them) to other (possibly non-Flutter)
/// components in the application. The return value from this handler tells
/// the platform to either stop propagation (by returning true: "event
/// handled"), or pass the event on to other controls (false: "event not
/// handled").
///
/// This handler is normally set by the [FocusManager] so that it can control
/// the key event propagation to focused widgets.
///
/// Most applications can use the focus system (see [Focus] and
/// [FocusManager]) to receive key events. If you are not using the
/// [FocusManager] to manage focus, then to be able to stop propagation of the
/// event by indicating that the event was handled, set this attribute to a
/// [RawKeyEventHandler]. Otherwise, key events will be assumed to not have
/// been handled by Flutter, and will also be sent to other (possibly
/// non-Flutter) controls in the application.
///
/// See also:
///
/// * [Focus.onKey], a [Focus] callback attribute that will be given key
/// events distributed by the [FocusManager] based on the current primary
/// focus.
/// * [addListener], to add passive key event listeners that do not stop event
/// propagation.
RawKeyEventHandler
keyEventHandler
;
Future
<
dynamic
>
_handleKeyEvent
(
dynamic
message
)
async
{
Future
<
dynamic
>
_handleKeyEvent
(
dynamic
message
)
async
{
final
RawKeyEvent
event
=
RawKeyEvent
.
fromMessage
(
message
as
Map
<
String
,
dynamic
>);
final
RawKeyEvent
event
=
RawKeyEvent
.
fromMessage
(
message
as
Map
<
String
,
dynamic
>);
if
(
event
==
null
)
{
if
(
event
==
null
)
{
...
@@ -534,14 +585,19 @@ class RawKeyboard {
...
@@ -534,14 +585,19 @@ class RawKeyboard {
// Make sure that the modifiers reflect reality, in case a modifier key was
// Make sure that the modifiers reflect reality, in case a modifier key was
// pressed/released while the app didn't have focus.
// pressed/released while the app didn't have focus.
_synchronizeModifiers
(
event
);
_synchronizeModifiers
(
event
);
if
(
_listeners
.
isEmpty
)
{
// Send the event to passive listeners.
return
;
}
for
(
final
ValueChanged
<
RawKeyEvent
>
listener
in
List
<
ValueChanged
<
RawKeyEvent
>>.
from
(
_listeners
))
{
for
(
final
ValueChanged
<
RawKeyEvent
>
listener
in
List
<
ValueChanged
<
RawKeyEvent
>>.
from
(
_listeners
))
{
if
(
_listeners
.
contains
(
listener
))
{
if
(
_listeners
.
contains
(
listener
))
{
listener
(
event
);
listener
(
event
);
}
}
}
}
// Send the key event to the keyEventHandler, then send the appropriate
// response to the platform so that it can resolve the event's handling.
// Defaults to false if keyEventHandler is null.
final
bool
handled
=
keyEventHandler
!=
null
&&
keyEventHandler
(
event
);
assert
(
handled
!=
null
,
'keyEventHandler returned null, which is not allowed'
);
return
<
String
,
dynamic
>{
'handled'
:
handled
};
}
}
static
final
Map
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>
_modifierKeyMap
=
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>{
static
final
Map
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>
_modifierKeyMap
=
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>{
...
...
packages/flutter/lib/src/widgets/focus_manager.dart
View file @
c68758fa
...
@@ -1416,7 +1416,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1416,7 +1416,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// from the [WidgetsBinding] singleton).
/// from the [WidgetsBinding] singleton).
FocusManager
()
{
FocusManager
()
{
rootScope
.
_manager
=
this
;
rootScope
.
_manager
=
this
;
RawKeyboard
.
instance
.
addListener
(
_handleRawKeyEvent
)
;
RawKeyboard
.
instance
.
keyEventHandler
=
_handleRawKeyEvent
;
GestureBinding
.
instance
.
pointerRouter
.
addGlobalRoute
(
_handlePointerEvent
);
GestureBinding
.
instance
.
pointerRouter
.
addGlobalRoute
(
_handlePointerEvent
);
}
}
...
@@ -1605,7 +1605,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1605,7 +1605,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
}
}
}
}
void
_handleRawKeyEvent
(
RawKeyEvent
event
)
{
bool
_handleRawKeyEvent
(
RawKeyEvent
event
)
{
// Update highlightMode first, since things responding to the keys might
// Update highlightMode first, since things responding to the keys might
// look at the highlight mode, and it should be accurate.
// look at the highlight mode, and it should be accurate.
_lastInteractionWasTouch
=
false
;
_lastInteractionWasTouch
=
false
;
...
@@ -1616,7 +1616,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1616,7 +1616,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
// onKey on the way up, and if one responds that they handled it, stop.
// onKey on the way up, and if one responds that they handled it, stop.
if
(
_primaryFocus
==
null
)
{
if
(
_primaryFocus
==
null
)
{
assert
(
_focusDebug
(
'No primary focus for key event, ignored:
$event
'
));
assert
(
_focusDebug
(
'No primary focus for key event, ignored:
$event
'
));
return
;
return
false
;
}
}
bool
handled
=
false
;
bool
handled
=
false
;
for
(
final
FocusNode
node
in
<
FocusNode
>[
_primaryFocus
,
...
_primaryFocus
.
ancestors
])
{
for
(
final
FocusNode
node
in
<
FocusNode
>[
_primaryFocus
,
...
_primaryFocus
.
ancestors
])
{
...
@@ -1629,6 +1629,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1629,6 +1629,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
if
(!
handled
)
{
if
(!
handled
)
{
assert
(
_focusDebug
(
'Key event not handled by anyone:
$event
.'
));
assert
(
_focusDebug
(
'Key event not handled by anyone:
$event
.'
));
}
}
return
handled
;
}
}
/// The node that currently has the primary focus.
/// The node that currently has the primary focus.
...
...
packages/flutter/test/services/raw_keyboard_test.dart
View file @
c68758fa
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
class
_ModifierCheck
{
class
_ModifierCheck
{
...
@@ -508,6 +509,48 @@ void main() {
...
@@ -508,6 +509,48 @@ void main() {
final
RawKeyEventDataAndroid
data
=
repeatCountEvent
.
data
as
RawKeyEventDataAndroid
;
final
RawKeyEventDataAndroid
data
=
repeatCountEvent
.
data
as
RawKeyEventDataAndroid
;
expect
(
data
.
repeatCount
,
equals
(
42
));
expect
(
data
.
repeatCount
,
equals
(
42
));
});
});
testWidgets
(
'Key events are responded to correctly.'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
LogicalKeyboardKey
.
keyA
,
platform:
'android'
,
isDown:
true
,
);
Map
<
String
,
dynamic
>
message
;
await
ServicesBinding
.
instance
.
defaultBinaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
keyEvent
.
name
,
SystemChannels
.
keyEvent
.
codec
.
encodeMessage
(
data
),
(
ByteData
data
)
{
message
=
SystemChannels
.
keyEvent
.
codec
.
decodeMessage
(
data
)
as
Map
<
String
,
dynamic
>;
},
);
expect
(
message
,
equals
(<
String
,
dynamic
>{
'handled'
:
false
}));
// Set up a widget that will receive focused text events.
final
FocusNode
focusNode
=
FocusNode
(
debugLabel:
'Test Node'
);
await
tester
.
pumpWidget
(
Focus
(
focusNode:
focusNode
,
onKey:
(
FocusNode
node
,
RawKeyEvent
event
)
{
return
true
;
// handle all events.
},
child:
const
SizedBox
(),
),
);
focusNode
.
requestFocus
();
await
tester
.
pump
();
await
ServicesBinding
.
instance
.
defaultBinaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
keyEvent
.
name
,
SystemChannels
.
keyEvent
.
codec
.
encodeMessage
(
data
),
(
ByteData
data
)
{
message
=
SystemChannels
.
keyEvent
.
codec
.
decodeMessage
(
data
)
as
Map
<
String
,
dynamic
>;
},
);
expect
(
message
,
equals
(<
String
,
dynamic
>{
'handled'
:
true
}));
ServicesBinding
.
instance
.
defaultBinaryMessenger
.
setMockMessageHandler
(
SystemChannels
.
keyEvent
.
name
,
null
);
});
});
});
group
(
'RawKeyEventDataFuchsia'
,
()
{
group
(
'RawKeyEventDataFuchsia'
,
()
{
const
Map
<
int
,
_ModifierCheck
>
modifierTests
=
<
int
,
_ModifierCheck
>{
const
Map
<
int
,
_ModifierCheck
>
modifierTests
=
<
int
,
_ModifierCheck
>{
...
...
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