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

Commit 0613773

Browse files
committed
Fixed tests
1 parent 9fe663d commit 0613773

File tree

8 files changed

+326
-203
lines changed

8 files changed

+326
-203
lines changed

shell/platform/android/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ action("robolectric_tests") {
429429
sources = [
430430
"test/io/flutter/FlutterTestSuite.java",
431431
"test/io/flutter/SmokeTest.java",
432+
"test/io/flutter/embedding/android/AndroidKeyProcessorTest.java",
432433
"test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java",
433434
"test/io/flutter/embedding/android/FlutterActivityTest.java",
434435
"test/io/flutter/embedding/android/FlutterAndroidComponentTest.java",
@@ -446,6 +447,7 @@ action("robolectric_tests") {
446447
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
447448
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
448449
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
450+
"test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java",
449451
"test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java",
450452
"test/io/flutter/external/FlutterLaunchTests.java",
451453
"test/io/flutter/plugin/common/StandardMessageCodecTest.java",

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

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525
*/
2626
public class AndroidKeyProcessor {
2727
private static final String TAG = "AndroidKeyProcessor";
28+
private static long eventIdSerial = 0;
2829

2930
@NonNull private final KeyEventChannel keyEventChannel;
3031
@NonNull private final TextInputPlugin textInputPlugin;
31-
@NonNull private int combiningCharacter;
32-
@NonNull public final EventResponder eventResponder;
32+
private int combiningCharacter;
33+
@NonNull EventResponder eventResponder;
3334

3435
public AndroidKeyProcessor(
3536
@NonNull Context context,
@@ -41,6 +42,17 @@ public AndroidKeyProcessor(
4142
this.keyEventChannel.setEventResponseHandler(eventResponder);
4243
}
4344

45+
/**
46+
* Set the event responder for this key processor.
47+
*
48+
* <p>Typically used by the testing framework to inject mocks.
49+
*
50+
* @param eventResponder the event responder to use instead of the default responder.
51+
*/
52+
public void setEventResponder(@NonNull EventResponder eventResponder) {
53+
this.eventResponder = eventResponder;
54+
}
55+
4456
/**
4557
* Called when a key up event is received by the {@link FlutterView}.
4658
*
@@ -55,7 +67,7 @@ public boolean onKeyUp(@NonNull KeyEvent keyEvent) {
5567

5668
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
5769
KeyEventChannel.FlutterKeyEvent flutterEvent =
58-
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter);
70+
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter, eventIdSerial++);
5971
keyEventChannel.keyUp(flutterEvent);
6072
eventResponder.addEvent(flutterEvent.eventId, keyEvent);
6173
return true;
@@ -85,7 +97,7 @@ public boolean onKeyDown(@NonNull KeyEvent keyEvent) {
8597

8698
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
8799
KeyEventChannel.FlutterKeyEvent flutterEvent =
88-
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter);
100+
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter, eventIdSerial++);
89101
keyEventChannel.keyDown(flutterEvent);
90102
eventResponder.addEvent(flutterEvent.eventId, keyEvent);
91103
return true;
@@ -124,7 +136,7 @@ private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoi
124136
return null;
125137
}
126138

127-
Character complexCharacter = (char) newCharacterCodePoint;
139+
char complexCharacter = (char) newCharacterCodePoint;
128140
boolean isNewCodePointACombiningCharacter =
129141
(newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0;
130142
if (isNewCodePointACombiningCharacter) {
@@ -154,10 +166,9 @@ public static class EventResponder implements KeyEventChannel.EventResponseHandl
154166
// The maximum number of pending events that are held before starting to
155167
// complain.
156168
private static final long MAX_PENDING_EVENTS = 1000;
157-
private final Deque<Entry<Long, KeyEvent>> pendingEvents =
158-
new ArrayDeque<Entry<Long, KeyEvent>>();
169+
final Deque<Entry<Long, KeyEvent>> pendingEvents = new ArrayDeque<Entry<Long, KeyEvent>>();
159170
@NonNull private final Context context;
160-
public boolean dispatchingKeyEvent = false;
171+
boolean dispatchingKeyEvent = false;
161172

162173
public EventResponder(@NonNull Context context) {
163174
this.context = context;
@@ -168,9 +179,13 @@ public EventResponder(@NonNull Context context) {
168179
*
169180
* @param id the id of the event to be removed.
170181
*/
171-
private KeyEvent removePendingEvent(@NonNull long id) {
182+
private KeyEvent removePendingEvent(long id) {
172183
if (pendingEvents.getFirst().getKey() != id) {
173-
throw new AssertionError("Event response received out of order");
184+
throw new AssertionError(
185+
"Event response received out of order. Should have seen event "
186+
+ pendingEvents.getFirst().getKey()
187+
+ " first. Instead, received "
188+
+ id);
174189
}
175190
return pendingEvents.removeFirst().getValue();
176191
}
@@ -182,7 +197,7 @@ private KeyEvent removePendingEvent(@NonNull long id) {
182197
* be null.
183198
*/
184199
@Override
185-
public void onKeyEventHandled(@NonNull long id) {
200+
public void onKeyEventHandled(long id) {
186201
removePendingEvent(id);
187202
}
188203

@@ -194,18 +209,8 @@ public void onKeyEventHandled(@NonNull long id) {
194209
* not be null.
195210
*/
196211
@Override
197-
public void onKeyEventNotHandled(@NonNull long id) {
198-
KeyEvent pendingEvent = removePendingEvent(id);
199-
200-
// Since the framework didn't handle it, dispatch the key again.
201-
Activity activity = getActivity(context);
202-
if (activity != null) {
203-
// Turn on dispatchingKeyEvent so that we don't dispatch to ourselves and
204-
// send it to the framework again.
205-
dispatchingKeyEvent = true;
206-
activity.dispatchKeyEvent(pendingEvent);
207-
dispatchingKeyEvent = false;
208-
}
212+
public void onKeyEventNotHandled(long id) {
213+
dispatchKeyEvent(removePendingEvent(id));
209214
}
210215

211216
/** Adds an Android key event with an id to the event responder to wait for a response. */
@@ -221,6 +226,23 @@ public void addEvent(long id, @NonNull KeyEvent event) {
221226
}
222227
}
223228

229+
/**
230+
* Dispatches the event to the activity associated with the context.
231+
*
232+
* @param event the event to be dispatched to the activity.
233+
*/
234+
public void dispatchKeyEvent(KeyEvent event) {
235+
// Since the framework didn't handle it, dispatch the key again.
236+
Activity activity = getActivity(context);
237+
if (activity != null) {
238+
// Turn on dispatchingKeyEvent so that we don't dispatch to ourselves and
239+
// send it to the framework again.
240+
dispatchingKeyEvent = true;
241+
activity.dispatchKeyEvent(event);
242+
dispatchingKeyEvent = false;
243+
}
244+
}
245+
224246
/**
225247
* Gets the nearest ancestor Activity for the given Context.
226248
*

shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
package io.flutter.embedding.engine.systemchannels;
66

77
import android.os.Build;
8-
import android.util.Log;
98
import android.view.InputDevice;
109
import android.view.KeyEvent;
1110
import androidx.annotation.NonNull;
1211
import androidx.annotation.Nullable;
13-
import io.flutter.embedding.engine.dart.DartExecutor;
12+
import io.flutter.Log;
1413
import io.flutter.plugin.common.BasicMessageChannel;
14+
import io.flutter.plugin.common.BinaryMessenger;
1515
import io.flutter.plugin.common.JSONMessageCodec;
1616
import java.util.HashMap;
1717
import java.util.Map;
@@ -26,7 +26,6 @@
2626
*/
2727
public class KeyEventChannel {
2828
private static final String TAG = "KeyEventChannel";
29-
private static long eventIdSerial = 0;
3029

3130
/**
3231
* Sets the event response handler to be used to receive key event response messages from the
@@ -47,7 +46,7 @@ public interface EventResponseHandler {
4746
* @param id the event id of the event to be marked as being handled by the framework. Must not
4847
* be null.
4948
*/
50-
public void onKeyEventHandled(@NonNull long id);
49+
public void onKeyEventHandled(long id);
5150

5251
/**
5352
* Called whenever the framework responds that a given key event wasn't handled by the
@@ -56,56 +55,58 @@ public interface EventResponseHandler {
5655
* @param id the event id of the event to be marked as not being handled by the framework. Must
5756
* not be null.
5857
*/
59-
public void onKeyEventNotHandled(@NonNull long id);
58+
public void onKeyEventNotHandled(long id);
6059
}
6160

62-
private final BasicMessageChannel.MessageHandler<Object> messageHandler =
63-
new BasicMessageChannel.MessageHandler<Object>() {
64-
@Override
65-
public void onMessage(
66-
@Nullable Object message, @NonNull BasicMessageChannel.Reply<Object> reply) {
67-
// If there is no handler to respond to this message then we don't
68-
// need to parse it.
69-
if (eventResponseHandler == null) {
70-
return;
71-
}
72-
73-
try {
74-
final JSONObject annotatedEvent = (JSONObject) message;
75-
final String type = annotatedEvent.getString("type");
76-
final JSONObject data = annotatedEvent.getJSONObject("data");
77-
78-
switch (type) {
79-
case "keyHandled":
80-
eventResponseHandler.onKeyEventHandled(data.getLong("eventId"));
81-
break;
82-
case "keyNotHandled":
83-
eventResponseHandler.onKeyEventNotHandled(data.getLong("eventId"));
84-
break;
85-
}
86-
} catch (JSONException e) {
87-
Log.e(TAG, "Unable to unpack JSON message: " + e);
88-
} finally {
89-
reply.reply(null);
90-
}
91-
}
92-
};
61+
/**
62+
* A constructor that creates a KeyEventChannel with the default message handler.
63+
*
64+
* @param binaryMessenger the binary messenger used to send messages on this channel.
65+
*/
66+
public KeyEventChannel(@NonNull BinaryMessenger binaryMessenger) {
67+
this.channel =
68+
new BasicMessageChannel<>(binaryMessenger, "flutter/keyevent", JSONMessageCodec.INSTANCE);
69+
}
9370

94-
@NonNull public final BasicMessageChannel<Object> channel;
71+
/**
72+
* Creates a reply handler for this an event with the given eventId.
73+
*
74+
* @param eventId the event ID to create a reply for.
75+
*/
76+
BasicMessageChannel.Reply<Object> createReplyHandler(long eventId) {
77+
return message -> {
78+
if (eventResponseHandler == null) {
79+
return;
80+
}
9581

96-
public KeyEventChannel(@NonNull DartExecutor dartExecutor) {
97-
this.channel =
98-
new BasicMessageChannel<>(dartExecutor, "flutter/keyevent", JSONMessageCodec.INSTANCE);
99-
this.channel.setMessageHandler(messageHandler);
82+
try {
83+
if (message == null) {
84+
eventResponseHandler.onKeyEventNotHandled(eventId);
85+
return;
86+
}
87+
final JSONObject annotatedEvent = (JSONObject) message;
88+
final boolean handled = annotatedEvent.getBoolean("handled");
89+
if (handled) {
90+
eventResponseHandler.onKeyEventHandled(eventId);
91+
} else {
92+
eventResponseHandler.onKeyEventNotHandled(eventId);
93+
}
94+
} catch (JSONException e) {
95+
Log.e(TAG, "Unable to unpack JSON message: " + e);
96+
eventResponseHandler.onKeyEventNotHandled(eventId);
97+
}
98+
};
10099
}
101100

101+
@NonNull public final BasicMessageChannel<Object> channel;
102+
102103
public void keyUp(@NonNull FlutterKeyEvent keyEvent) {
103104
Map<String, Object> message = new HashMap<>();
104105
message.put("type", "keyup");
105106
message.put("keymap", "android");
106107
encodeKeyEvent(keyEvent, message);
107108

108-
channel.send(message);
109+
channel.send(message, createReplyHandler(keyEvent.eventId));
109110
}
110111

111112
public void keyDown(@NonNull FlutterKeyEvent keyEvent) {
@@ -114,7 +115,7 @@ public void keyDown(@NonNull FlutterKeyEvent keyEvent) {
114115
message.put("keymap", "android");
115116
encodeKeyEvent(keyEvent, message);
116117

117-
channel.send(message);
118+
channel.send(message, createReplyHandler(keyEvent.eventId));
118119
}
119120

120121
private void encodeKeyEvent(
@@ -136,7 +137,10 @@ private void encodeKeyEvent(
136137
message.put("eventId", event.eventId);
137138
}
138139

139-
/** Key event as defined by Flutter. */
140+
/**
141+
* A key event as defined by Flutter that includes an id for the specific event to be used when
142+
* responding to the event.
143+
*/
140144
public static class FlutterKeyEvent {
141145
public final int deviceId;
142146
public final int flags;
@@ -152,12 +156,12 @@ public static class FlutterKeyEvent {
152156
public final int repeatCount;
153157
public final long eventId;
154158

155-
public FlutterKeyEvent(@NonNull KeyEvent androidKeyEvent) {
156-
this(androidKeyEvent, null);
159+
public FlutterKeyEvent(@NonNull KeyEvent androidKeyEvent, long eventId) {
160+
this(androidKeyEvent, null, eventId);
157161
}
158162

159163
public FlutterKeyEvent(
160-
@NonNull KeyEvent androidKeyEvent, @Nullable Character complexCharacter) {
164+
@NonNull KeyEvent androidKeyEvent, @Nullable Character complexCharacter, long eventId) {
161165
this(
162166
androidKeyEvent.getDeviceId(),
163167
androidKeyEvent.getFlags(),
@@ -168,7 +172,8 @@ public FlutterKeyEvent(
168172
androidKeyEvent.getScanCode(),
169173
androidKeyEvent.getMetaState(),
170174
androidKeyEvent.getSource(),
171-
androidKeyEvent.getRepeatCount());
175+
androidKeyEvent.getRepeatCount(),
176+
eventId);
172177
}
173178

174179
public FlutterKeyEvent(
@@ -181,7 +186,8 @@ public FlutterKeyEvent(
181186
int scanCode,
182187
int metaState,
183188
int source,
184-
int repeatCount) {
189+
int repeatCount,
190+
long eventId) {
185191
this.deviceId = deviceId;
186192
this.flags = flags;
187193
this.plainCodePoint = plainCodePoint;
@@ -192,7 +198,7 @@ public FlutterKeyEvent(
192198
this.metaState = metaState;
193199
this.source = source;
194200
this.repeatCount = repeatCount;
195-
this.eventId = eventIdSerial++;
201+
this.eventId = eventId;
196202
InputDevice device = InputDevice.getDevice(deviceId);
197203
if (device != null) {
198204
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

0 commit comments

Comments
 (0)