Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 64fae02

Browse files
split AndroidKeyProcessor into different classes
1 parent d4b9518 commit 64fae02

File tree

3 files changed

+48
-57
lines changed

3 files changed

+48
-57
lines changed

shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,11 @@
55
package io.flutter.embedding.android;
66

77
import android.view.KeyCharacterMap;
8-
import androidx.annotation.Nullable;
98

10-
/**
11-
* A class to process key events from Android, passing them to the framework as messages using
12-
* {@link KeyEventChannel}.
13-
*
14-
* <p>A class that sends Android key events to the framework, and re-dispatches those not handled by
15-
* the framework.
16-
*
17-
* <p>Flutter uses asynchronous event handling to avoid blocking the UI thread, but Android requires
18-
* that events are handled synchronously. So, when a key event is received by Flutter, it tells
19-
* Android synchronously that the key has been handled so that it won't propagate to other
20-
* components. Flutter then uses "delayed event synthesis", where it sends the event to the
21-
* framework, and if the framework responds that it has not handled the event, then this class
22-
* synthesizes a new event to send to Android, without handling it this time.
23-
*/
9+
/** A class for handling combining characters in a {@link KeyEvent} stream. */
2410
class AndroidKeyProcessor {
2511
private int combiningCharacter;
2612

27-
/**
28-
* Constructor for AndroidKeyProcessor.
29-
*
30-
* <p>The view is used as the destination to send the synthesized key to. This means that the the
31-
* next thing in the focus chain will get the event when the framework returns false from
32-
* onKeyDown/onKeyUp
33-
*
34-
* <p>It is possible that that in the middle of the async round trip, the focus chain could
35-
* change, and instead of the native widget that was "next" when the event was fired getting the
36-
* event, it may be the next widget when the event is synthesized that gets it. In practice, this
37-
* shouldn't be a huge problem, as this is an unlikely occurrence to happen without user input,
38-
* and it may actually be desired behavior, but it is possible.
39-
*
40-
* @param view takes the activity to use for re-dispatching of events that were not handled by the
41-
* framework.
42-
* @param keyEventChannel the event channel to listen to for new key events.
43-
* @param textInputPlugin a plugin, which, if set, is given key events before the framework is,
44-
* and if it has a valid input connection and is accepting text, then it will handle the event
45-
* and the framework will not receive it.
46-
*/
47-
4813
/**
4914
* Applies the given Unicode character in {@code newCharacterCodePoint} to a previously entered
5015
* Unicode combining character and returns the combination of these characters if a combination
@@ -72,7 +37,6 @@ class AndroidKeyProcessor {
7237
* <p>The following reference explains the concept of a "combining character":
7338
* https://en.wikipedia.org/wiki/Combining_character
7439
*/
75-
@Nullable
7640
Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint) {
7741
char complexCharacter = (char) newCharacterCodePoint;
7842
boolean isNewCodePointACombiningCharacter =

shell/platform/android/io/flutter/embedding/android/KeyChannelResponder.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
import androidx.annotation.NonNull;
55
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
66

7-
/**
8-
* A light wrapper around a {@link KeyEventChannel} that turns it into a {@link PrimaryResponder}.
9-
*/
7+
/** A light wrapper around a {@link KeyEventChannel}, turning it into a {@link PrimaryResponder}. */
108
class KeyChannelResponder implements KeyboardManager.PrimaryResponder {
119
private static final String TAG = "KeyChannelResponder";
1210

@@ -39,6 +37,5 @@ public void handleEvent(
3937
flutterEvent,
4038
isKeyUp,
4139
(isEventHandled) -> onKeyEventHandledCallback.onKeyEventHandled(isEventHandled));
42-
return;
4340
}
4441
}

shell/platform/android/io/flutter/embedding/android/KeyboardManager.java

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
import java.util.HashSet;
1515

1616
/**
17-
* A class to process {@link KeyEvent}s dispatched to a {@link FlutterView}.
17+
* A class to process {@link KeyEvent}s dispatched to a {@link FlutterView}, either from a hardware
18+
* keyboard or an IME event.
1819
*
19-
* <p>A class that sends Android key events to the currently registered {@link PrimaryResponder}s,
20-
* and re-dispatches those not handled by the primary responders.
20+
* <p>A class that sends Android {@link KeyEvent} to the currently registered {@link
21+
* PrimaryResponder}s, and re-dispatches those not handled by the primary responders.
2122
*
2223
* <p>Flutter uses asynchronous event handling to avoid blocking the UI thread, but Android requires
2324
* that events are handled synchronously. So, when a key event is received by Flutter, it tells
@@ -26,19 +27,25 @@
2627
* framework, and if the framework responds that it has not handled the event, then this class
2728
* synthesizes a new event to send to Android, without handling it this time.
2829
*
29-
* <p>A new {@link KeyEvent} sent to a {@link KeyboardManager} may be processed by 3 different types
30-
* of "responder"s:
30+
* <p>A new {@link KeyEvent} sent to a {@link KeyboardManager} can be propagated to 3 different
31+
* types of "responder"s:
3132
*
3233
* <ul>
33-
* <li>{@link PrimaryResponder}s: the {@link KeyboardManager} calls the {@link
34-
* PrimaryResponder#handleEvent(KeyEvent, OnKeyEventHandledCallback)} method on the currently
35-
* registered {@link PrimaryResponder}s. When each {@link PrimaryResponder} has decided wether
36-
* to handle the key event, it must call the supplied {@link OnKeyEventHandledCallback}
34+
* <li>{@link PrimaryResponder}s: An immutable list of key responders in a {@link KeyboardManager}
35+
* that each implements the {@link PrimaryResponder} interface. a {@link PrimaryResponder} is
36+
* capable of handling {@link KeyEvent}s asynchronously.
37+
* <p>When a new {@link KeyEvent} is received, {@link KeyboardManager} calls the {@link
38+
* PrimaryResponder#handleEvent(KeyEvent, OnKeyEventHandledCallback)} method on its {@link
39+
* PrimaryResponder}s. Each {@link PrimaryResponder} must call the supplied {@link
40+
* OnKeyEventHandledCallback} exactly once, when it has decided wether to handle the key event
3741
* callback. More than one {@link PrimaryResponder} is allowed to reply true and handle the
3842
* same {@link KeyEvent}.
43+
* <p>Typically a {@link KeyboardManager} uses a {@link KeyChannelResponder} as its only
44+
* {@link PrimaryResponder}.
3945
* <li>{@link TextInputPlugin}: if every {@link PrimaryResponder} has replied false to a {@link
40-
* KeyEvent}, the {@link KeyEvent} will be sent to the currently focused editable text field
41-
* in {@link TextInputPlugin}, if any.
46+
* KeyEvent}, or if the {@link KeyboardManager} has zero {@link PrimaryResponder}s, the {@link
47+
* KeyEvent} will be sent to the currently focused editable text field in {@link
48+
* TextInputPlugin}, if any.
4249
* <li><b>"Redispatch"</b>: if there's no currently focused text field in {@link TextInputPlugin},
4350
* or the text field does not handle the {@link KeyEvent} either, the {@link KeyEvent} will be
4451
* sent back to the top of the activity's view hierachy, allowing the {@link KeyEvent} to be
@@ -56,6 +63,27 @@ public class KeyboardManager {
5663
this.primaryResponders = primaryResponders;
5764
}
5865

66+
/**
67+
* Constructor for {@link KeyboardManager} that uses a {@link KeyChannelResponder} as its only
68+
* {@link PrimaryResponder}.
69+
*
70+
* <p>The view is used as the destination to send the synthesized key to. This means that the the
71+
* next thing in the focus chain will get the event when the {@link KeyChannelResponder} returns
72+
* false from onKeyDown/onKeyUp.
73+
*
74+
* <p>It is possible that that in the middle of the async round trip, the focus chain could
75+
* change, and instead of the native widget that was "next" when the event was fired getting the
76+
* event, it may be the next widget when the event is synthesized that gets it. In practice, this
77+
* shouldn't be a huge problem, as this is an unlikely occurrence to happen without user input,
78+
* and it may actually be desired behavior, but it is possible.
79+
*
80+
* @param view takes the activity to use for re-dispatching of events that were not handled by the
81+
* framework.
82+
* @param textInputPlugin a plugin, which, if set, is given key events before the framework is,
83+
* and if it has a valid input connection and is accepting text, then it will handle the event
84+
* and the framework will not receive it.
85+
* @param keyEventChannel the event channel to listen to for new key events.
86+
*/
5987
public KeyboardManager(
6088
View view, @NonNull TextInputPlugin textInputPlugin, KeyEventChannel keyEventChannel) {
6189
this(
@@ -67,12 +95,14 @@ public KeyboardManager(
6795
/**
6896
* The interface for responding to a {@link KeyEvent} asynchronously.
6997
*
70-
* <p>Implementers of this interface should be added to a {@link KeyboardManager} using the {@link
71-
* KeyboardManager#addPrimaryResponder(PrimaryResponder)}, in order to receive key events.
98+
* <p>Implementers of this interface should be owned by a {@link KeyboardManager}, in order to
99+
* receive key events.
72100
*
73101
* <p>After receiving a {@link KeyEvent}, the {@link PrimaryResponder} must call the supplied
74-
* {@link OnKeyEventHandledCallback} to inform the {@link KeyboardManager} whether it is capable
75-
* of handling the {@link KeyEvent}.
102+
* {@link OnKeyEventHandledCallback} exactly once, to inform the {@link KeyboardManager} whether
103+
* it wishes to handle the {@link KeyEvent}. The {@link KeyEvent} will not be propagated to the
104+
* {@link TextInputPlugin} or be redispatched to the view hierachy if the key responder answered
105+
* yes.
76106
*
77107
* <p>If a {@link PrimaryResponder} fails to call the {@link OnKeyEventHandledCallback} callback,
78108
* the {@link KeyEvent} will never be sent to the {@link TextInputPlugin}, and the {@link
@@ -88,7 +118,7 @@ interface OnKeyEventHandledCallback {
88118
*
89119
* @param keyEvent the new {@link KeyEvent} this {@link PrimaryResponder} may be interested in.
90120
* @param onKeyEventHandledCallback the method to call when this {@link PrimaryResponder} has
91-
* decided whether to handle {@link keyEvent}.
121+
* decided whether to handle the {@link keyEvent}.
92122
*/
93123
void handleEvent(
94124
@NonNull KeyEvent keyEvent, @NonNull OnKeyEventHandledCallback onKeyEventHandledCallback);

0 commit comments

Comments
 (0)