Flutter Engine
The Flutter Engine
KeyboardManagerTest.java
Go to the documentation of this file.
1package io.flutter.embedding.android;
2
3import static android.view.KeyEvent.*;
4import static io.flutter.embedding.android.KeyData.Type;
5import static io.flutter.util.KeyCodes.*;
6import static org.junit.Assert.assertEquals;
7import static org.junit.Assert.assertNotNull;
8import static org.junit.Assert.assertNull;
9import static org.junit.Assert.assertTrue;
10import static org.mockito.Mockito.any;
11import static org.mockito.Mockito.doAnswer;
12import static org.mockito.Mockito.eq;
13import static org.mockito.Mockito.mock;
14import static org.mockito.Mockito.times;
15import static org.mockito.Mockito.verify;
16
17import android.view.KeyCharacterMap;
18import android.view.KeyEvent;
19import androidx.annotation.NonNull;
20import androidx.annotation.Nullable;
21import androidx.test.ext.junit.runners.AndroidJUnit4;
22import io.flutter.embedding.android.KeyData.DeviceType;
23import io.flutter.plugin.common.BinaryMessenger;
24import io.flutter.plugin.common.JSONMessageCodec;
25import io.flutter.util.FakeKeyEvent;
26import java.nio.ByteBuffer;
27import java.util.ArrayList;
28import java.util.List;
29import java.util.Map;
30import java.util.function.BiConsumer;
31import java.util.function.Consumer;
32import java.util.stream.Collectors;
33import org.json.JSONException;
34import org.json.JSONObject;
35import org.junit.Before;
36import org.junit.Test;
37import org.junit.runner.RunWith;
38import org.mockito.Mock;
39import org.mockito.MockitoAnnotations;
40import org.mockito.invocation.InvocationOnMock;
41import org.robolectric.annotation.Config;
42
43@Config(manifest = Config.NONE)
44@RunWith(AndroidJUnit4.class)
45public class KeyboardManagerTest {
46 public static final int SCAN_KEY_A = 0x1e;
47 public static final int SCAN_DIGIT1 = 0x2;
48 public static final int SCAN_SHIFT_LEFT = 0x2a;
49 public static final int SCAN_SHIFT_RIGHT = 0x36;
50 public static final int SCAN_CONTROL_LEFT = 0x1d;
51 public static final int SCAN_CONTROL_RIGHT = 0x61;
52 public static final int SCAN_ALT_LEFT = 0x38;
53 public static final int SCAN_ALT_RIGHT = 0x64;
54 public static final int SCAN_ARROW_LEFT = 0x69;
55 public static final int SCAN_ARROW_RIGHT = 0x6a;
56 public static final int SCAN_CAPS_LOCK = 0x3a;
57
58 public static final boolean DOWN_EVENT = true;
59 public static final boolean UP_EVENT = false;
60 public static final boolean SHIFT_LEFT_EVENT = true;
61 public static final boolean SHIFT_RIGHT_EVENT = false;
62
63 private static final int DEAD_KEY = '`' | KeyCharacterMap.COMBINING_ACCENT;
64
65 /**
66 * Records a message that {@link KeyboardManager} sends to outside.
67 *
68 * <p>A call record can originate from many sources, indicated by its {@link type}. Different
69 * types will have different fields filled, leaving others empty.
70 */
71 static class CallRecord {
72 enum Kind {
73 /**
74 * The channel responder sent a message through the key event channel.
75 *
76 * <p>This call record will have a non-null {@link channelObject}, with an optional {@link
77 * reply}.
78 */
80 /**
81 * The embedder responder sent a message through the key data channel.
82 *
83 * <p>This call record will have a non-null {@link keyData}, with an optional {@link reply}.
84 */
86 }
87
88 /**
89 * Construct an empty call record.
90 *
91 * <p>Use the static functions to constuct specific types instead.
92 */
93 private CallRecord() {}
94
96
97 /**
98 * The callback given by the keyboard manager.
99 *
100 * <p>It might be null, which probably means it is a synthesized event and requires no reply.
101 * Otherwise, invoke this callback with whether the event is handled for the keyboard manager to
102 * continue processing the key event.
103 */
104 public Consumer<Boolean> reply;
105 /** The data for a call record of kind {@link Kind.kChannel}. */
106 public JSONObject channelObject;
107 /** The data for a call record of kind {@link Kind.kEmbedder}. */
109
110 /** Construct a call record of kind {@link Kind.kChannel}. */
112 @NonNull JSONObject channelObject, @Nullable Consumer<Boolean> reply) {
113 final CallRecord record = new CallRecord();
114 record.kind = Kind.kChannel;
115 record.channelObject = channelObject;
116 record.reply = reply;
117 return record;
118 }
119
120 /** Construct a call record of kind {@link Kind.kEmbedder}. */
121 static CallRecord embedderCall(@NonNull KeyData keyData, @Nullable Consumer<Boolean> reply) {
122 final CallRecord record = new CallRecord();
123 record.kind = Kind.kEmbedder;
124 record.keyData = keyData;
125 record.reply = reply;
126 return record;
127 }
128 }
129
130 /**
131 * Build a response to a channel message sent by the channel responder.
132 *
133 * @param handled whether the event is handled.
134 */
135 static ByteBuffer buildJsonResponse(boolean handled) {
136 JSONObject body = new JSONObject();
137 try {
138 body.put("handled", handled);
139 } catch (JSONException e) {
140 assertNull(e);
141 }
142 ByteBuffer binaryReply = JSONMessageCodec.INSTANCE.encodeMessage(body);
143 binaryReply.rewind();
144 return binaryReply;
145 }
146
147 /**
148 * Build a response to an embedder message sent by the embedder responder.
149 *
150 * @param handled whether the event is handled.
151 */
152 static ByteBuffer buildBinaryResponse(boolean handled) {
153 byte[] body = new byte[1];
154 body[0] = (byte) (handled ? 1 : 0);
155 final ByteBuffer binaryReply = ByteBuffer.wrap(body);
156 binaryReply.rewind();
157 return binaryReply;
158 }
159
160 /**
161 * Used to configure how to process a channel message.
162 *
163 * <p>When the channel responder sends a channel message, this functional interface will be
164 * invoked. Its first argument will be the detailed data. The second argument will be a nullable
165 * reply callback, which should be called to mock the reply from the framework.
166 */
167 @FunctionalInterface
168 static interface ChannelCallHandler extends BiConsumer<JSONObject, Consumer<Boolean>> {}
169
170 /**
171 * Used to configure how to process an embedder message.
172 *
173 * <p>When the embedder responder sends a key data, this functional interface will be invoked. Its
174 * first argument will be the detailed data. The second argument will be a nullable reply
175 * callback, which should be called to mock the reply from the framework.
176 */
177 @FunctionalInterface
178 static interface EmbedderCallHandler extends BiConsumer<KeyData, Consumer<Boolean>> {}
179
180 static class KeyboardTester {
181 public KeyboardTester() {
182 respondToChannelCallsWith(false);
183 respondToEmbedderCallsWith(false);
184 respondToTextInputWith(false);
185
186 BinaryMessenger mockMessenger = mock(BinaryMessenger.class);
187 doAnswer(invocation -> onMessengerMessage(invocation))
188 .when(mockMessenger)
189 .send(any(String.class), any(ByteBuffer.class), eq(null));
190 doAnswer(invocation -> onMessengerMessage(invocation))
191 .when(mockMessenger)
192 .send(any(String.class), any(ByteBuffer.class), any(BinaryMessenger.BinaryReply.class));
193
194 mockView = mock(KeyboardManager.ViewDelegate.class);
195 doAnswer(invocation -> mockMessenger).when(mockView).getBinaryMessenger();
196 doAnswer(invocation -> textInputResult)
197 .when(mockView)
198 .onTextInputKeyEvent(any(KeyEvent.class));
199 doAnswer(
200 invocation -> {
201 KeyEvent event = invocation.getArgument(0);
202 boolean handled = keyboardManager.handleEvent(event);
203 assertEquals(handled, false);
204 return null;
205 })
206 .when(mockView)
207 .redispatch(any(KeyEvent.class));
208
209 keyboardManager = new KeyboardManager(mockView);
210 }
211
212 public @Mock KeyboardManager.ViewDelegate mockView;
214
215 /** Set channel calls to respond immediately with the given response. */
216 public void respondToChannelCallsWith(boolean handled) {
217 channelHandler =
218 (JSONObject data, Consumer<Boolean> reply) -> {
219 if (reply != null) {
220 reply.accept(handled);
221 }
222 };
223 }
224
225 /**
226 * Record channel calls to the given storage.
227 *
228 * <p>They are not responded to until the stored callbacks are manually called.
229 */
230 public void recordChannelCallsTo(@NonNull ArrayList<CallRecord> storage) {
231 channelHandler =
232 (JSONObject data, Consumer<Boolean> reply) -> {
233 storage.add(CallRecord.channelCall(data, reply));
234 };
235 }
236
237 /** Set embedder calls to respond immediately with the given response. */
238 public void respondToEmbedderCallsWith(boolean handled) {
239 embedderHandler =
240 (KeyData keyData, Consumer<Boolean> reply) -> {
241 if (reply != null) {
242 reply.accept(handled);
243 }
244 };
245 }
246
247 /**
248 * Record embedder calls to the given storage.
249 *
250 * <p>They are not responded to until the stored callbacks are manually called.
251 */
252 public void recordEmbedderCallsTo(@NonNull ArrayList<CallRecord> storage) {
253 embedderHandler =
254 (KeyData keyData, Consumer<Boolean> reply) ->
255 storage.add(CallRecord.embedderCall(keyData, reply));
256 }
257
258 /** Set text calls to respond with the given response. */
259 public void respondToTextInputWith(boolean response) {
260 textInputResult = response;
261 }
262
263 private ChannelCallHandler channelHandler;
264 private EmbedderCallHandler embedderHandler;
265 private Boolean textInputResult;
266
267 private Object onMessengerMessage(@NonNull InvocationOnMock invocation) {
268 final String channel = invocation.getArgument(0);
269 final ByteBuffer buffer = invocation.getArgument(1);
270 buffer.rewind();
271
272 final BinaryMessenger.BinaryReply reply = invocation.getArgument(2);
273 if (channel == "flutter/keyevent") {
274 // Parse a channel call.
275 final JSONObject jsonObject = (JSONObject) JSONMessageCodec.INSTANCE.decodeMessage(buffer);
276 final Consumer<Boolean> jsonReply =
277 reply == null ? null : handled -> reply.reply(buildJsonResponse(handled));
278 channelHandler.accept(jsonObject, jsonReply);
279 } else if (channel == "flutter/keydata") {
280 // Parse an embedder call.
281 final KeyData keyData = new KeyData(buffer);
282 final Consumer<Boolean> booleanReply =
283 reply == null ? null : handled -> reply.reply(buildBinaryResponse(handled));
284 embedderHandler.accept(keyData, booleanReply);
285 } else {
286 assertTrue(false);
287 }
288 return null;
289 }
290 }
291
292 /**
293 * Assert that the channel call is an event that matches the given data.
294 *
295 * <p>For now this function only validates key code, but not scancode or characters.
296 *
297 * @param data the target data to be tested.
298 * @param type the type of the data, usually "keydown" or "keyup".
299 * @param keyCode the key code.
300 */
302 @NonNull JSONObject message, @NonNull String type, @NonNull Integer keyCode) {
303 try {
304 assertEquals(type, message.get("type"));
305 assertEquals("android", message.get("keymap"));
306 assertEquals(keyCode, message.get("keyCode"));
307 } catch (JSONException e) {
308 assertNull(e);
309 }
310 }
311
312 /** Assert that the embedder call is an event that matches the given data. */
314 @NonNull KeyData data,
315 Type type,
316 long physicalKey,
317 long logicalKey,
318 String character,
319 boolean synthesized,
320 DeviceType deviceType) {
321 assertEquals(type, data.type);
322 assertEquals(physicalKey, data.physicalKey);
323 assertEquals(logicalKey, data.logicalKey);
324 assertEquals(character, data.character);
325 assertEquals(synthesized, data.synthesized);
326 assertEquals(deviceType, data.deviceType);
327 }
328
329 static void verifyEmbedderEvents(List<CallRecord> receivedCalls, KeyData[] expectedData) {
330 assertEquals(receivedCalls.size(), expectedData.length);
331 for (int idx = 0; idx < receivedCalls.size(); idx += 1) {
332 final KeyData data = expectedData[idx];
333 assertEmbedderEventEquals(
334 receivedCalls.get(idx).keyData,
335 data.type,
336 data.physicalKey,
337 data.logicalKey,
338 data.character,
339 data.synthesized,
340 data.deviceType);
341 }
342 }
343
345 Type type,
346 long physicalKey,
347 long logicalKey,
348 @Nullable String characters,
349 boolean synthesized,
350 DeviceType deviceType) {
351 final KeyData result = new KeyData();
352 result.physicalKey = physicalKey;
353 result.logicalKey = logicalKey;
354 result.timestamp = 0x0;
355 result.type = type;
356 result.character = characters;
357 result.synthesized = synthesized;
358 result.deviceType = deviceType;
359 return result;
360 }
361
362 /**
363 * Start a new tester, generate a ShiftRight event under the specified condition, and return the
364 * output events for that event.
365 *
366 * @param preEventLeftPressed Whether ShiftLeft was recorded as pressed before the event.
367 * @param preEventRightPressed Whether ShiftRight was recorded as pressed before the event.
368 * @param rightEventIsDown Whether the dispatched event is a key down of key up of ShiftRight.
369 * @param truePressed Whether Shift is pressed as shown in the metaState of the event.
370 * @return
371 */
373 boolean preEventLeftPressed,
374 boolean preEventRightPressed,
375 boolean rightEventIsDown,
376 boolean truePressed) {
377 final ArrayList<CallRecord> calls = new ArrayList<>();
378 // Even though the event is for ShiftRight, we still set SHIFT | SHIFT_LEFT here.
379 // See the comment in synchronizePressingKey for the reason.
380 final int SHIFT_LEFT_ON = META_SHIFT_LEFT_ON | META_SHIFT_ON;
381
383 tester.respondToTextInputWith(true); // Suppress redispatching
384 if (preEventLeftPressed) {
385 tester.keyboardManager.handleEvent(
386 new FakeKeyEvent(
387 ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', SHIFT_LEFT_ON));
388 }
389 if (preEventRightPressed) {
390 tester.keyboardManager.handleEvent(
391 new FakeKeyEvent(
392 ACTION_DOWN, SCAN_SHIFT_RIGHT, KEYCODE_SHIFT_RIGHT, 0, '\0', SHIFT_LEFT_ON));
393 }
394 tester.recordEmbedderCallsTo(calls);
395 tester.keyboardManager.handleEvent(
396 new FakeKeyEvent(
397 rightEventIsDown ? ACTION_DOWN : ACTION_UP,
398 SCAN_SHIFT_RIGHT,
399 KEYCODE_SHIFT_RIGHT,
400 0,
401 '\0',
402 truePressed ? SHIFT_LEFT_ON : 0));
403 return calls.stream()
404 .filter(data -> data.keyData.physicalKey != 0)
405 .collect(Collectors.toList());
406 }
407
408 public static KeyData buildShiftKeyData(boolean isLeft, boolean isDown, boolean isSynthesized) {
409 final KeyData data = new KeyData();
410 data.type = isDown ? Type.kDown : Type.kUp;
411 data.physicalKey = isLeft ? PHYSICAL_SHIFT_LEFT : PHYSICAL_SHIFT_RIGHT;
412 data.logicalKey = isLeft ? LOGICAL_SHIFT_LEFT : LOGICAL_SHIFT_RIGHT;
413 data.synthesized = isSynthesized;
414 data.deviceType = KeyData.DeviceType.kKeyboard;
415 return data;
416 }
417
418 /**
419 * Print each byte of the given buffer as a hex (such as "0a" for 0x0a), and return the
420 * concatenated string.
421 *
422 * <p>Used to compare binary content in byte buffers.
423 */
424 static String printBufferBytes(@NonNull ByteBuffer buffer) {
425 final String[] results = new String[buffer.capacity()];
426 for (int byteIdx = 0; byteIdx < buffer.capacity(); byteIdx += 1) {
427 results[byteIdx] = String.format("%02x", buffer.get(byteIdx));
428 }
429 return String.join("", results);
430 }
431
432 @Before
433 public void setUp() {
434 MockitoAnnotations.openMocks(this);
435 }
436
437 // Tests start
438
439 @Test
441 // Test data1: Non-empty character, synthesized.
442 final KeyData data1 = new KeyData();
443 data1.physicalKey = 0x0a;
444 data1.logicalKey = 0x0b;
445 data1.timestamp = 0x0c;
446 data1.type = Type.kRepeat;
447 data1.character = "A";
448 data1.synthesized = true;
449 data1.deviceType = DeviceType.kKeyboard;
450
451 final ByteBuffer data1Buffer = data1.toBytes();
452
453 assertEquals(
454 ""
455 + "0100000000000000"
456 + "0c00000000000000"
457 + "0200000000000000"
458 + "0a00000000000000"
459 + "0b00000000000000"
460 + "0100000000000000"
461 + "0000000000000000"
462 + "41",
463 printBufferBytes(data1Buffer));
464 // `position` is considered as the message size.
465 assertEquals(57, data1Buffer.position());
466
467 data1Buffer.rewind();
468 final KeyData data1Loaded = new KeyData(data1Buffer);
469 assertEquals(data1Loaded.timestamp, data1.timestamp);
470
471 // Test data2: Empty character, not synthesized.
472 final KeyData data2 = new KeyData();
473 data2.physicalKey = 0xaaaabbbbccccl;
474 data2.logicalKey = 0x666677778888l;
475 data2.timestamp = 0x333344445555l;
476 data2.type = Type.kUp;
477 data2.character = null;
478 data2.synthesized = false;
479 data2.deviceType = DeviceType.kDirectionalPad;
480
481 final ByteBuffer data2Buffer = data2.toBytes();
482
483 assertEquals(
484 ""
485 + "0000000000000000"
486 + "5555444433330000"
487 + "0100000000000000"
488 + "ccccbbbbaaaa0000"
489 + "8888777766660000"
490 + "0000000000000000"
491 + "0100000000000000",
492 printBufferBytes(data2Buffer));
493
494 data2Buffer.rewind();
495 final KeyData data2Loaded = new KeyData(data2Buffer);
496 assertEquals(data2Loaded.timestamp, data2.timestamp);
497 }
498
499 @Test
501 final KeyboardManager.CharacterCombiner combiner = new KeyboardManager.CharacterCombiner();
502 assertEquals(0, (int) combiner.applyCombiningCharacterToBaseCharacter(0));
503 assertEquals('B', (int) combiner.applyCombiningCharacterToBaseCharacter('B'));
504 assertEquals('B', (int) combiner.applyCombiningCharacterToBaseCharacter('B'));
505 assertEquals('A', (int) combiner.applyCombiningCharacterToBaseCharacter('A'));
506 assertEquals(0, (int) combiner.applyCombiningCharacterToBaseCharacter(0));
507 assertEquals(0, (int) combiner.applyCombiningCharacterToBaseCharacter(0));
508
509 assertEquals('`', (int) combiner.applyCombiningCharacterToBaseCharacter(DEAD_KEY));
510 assertEquals('`', (int) combiner.applyCombiningCharacterToBaseCharacter(DEAD_KEY));
511 assertEquals('À', (int) combiner.applyCombiningCharacterToBaseCharacter('A'));
512
513 assertEquals('`', (int) combiner.applyCombiningCharacterToBaseCharacter(DEAD_KEY));
514 assertEquals(0, (int) combiner.applyCombiningCharacterToBaseCharacter(0));
515 // The 0 input should remove the combining state.
516 assertEquals('A', (int) combiner.applyCombiningCharacterToBaseCharacter('A'));
517
518 assertEquals(0, (int) combiner.applyCombiningCharacterToBaseCharacter(0));
519 assertEquals('`', (int) combiner.applyCombiningCharacterToBaseCharacter(DEAD_KEY));
520 assertEquals('À', (int) combiner.applyCombiningCharacterToBaseCharacter('A'));
521 }
522
523 @Test
526 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, 65);
527 final ArrayList<CallRecord> calls = new ArrayList<>();
528
529 tester.recordChannelCallsTo(calls);
530
531 final boolean result = tester.keyboardManager.handleEvent(keyEvent);
532
533 assertEquals(true, result);
534 assertEquals(calls.size(), 1);
535 assertChannelEventEquals(calls.get(0).channelObject, "keydown", 65);
536
537 // Don't send the key event to the text plugin if the only primary responder
538 // hasn't responded.
539 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
540 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
541 }
542
543 @Test
546 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, 65);
547 final ArrayList<CallRecord> calls = new ArrayList<>();
548
549 tester.recordChannelCallsTo(calls);
550
551 final boolean result = tester.keyboardManager.handleEvent(keyEvent);
552
553 assertEquals(true, result);
554 assertEquals(calls.size(), 1);
555 assertChannelEventEquals(calls.get(0).channelObject, "keydown", 65);
556
557 // Don't send the key event to the text plugin if the only primary responder
558 // hasn't responded.
559 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
560 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
561
562 // If a primary responder handles the key event the propagation stops.
563 assertNotNull(calls.get(0).reply);
564 calls.get(0).reply.accept(true);
565 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
566 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
567 }
568
569 @Test
572 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0);
573 final ArrayList<CallRecord> calls = new ArrayList<>();
574
575 tester.recordEmbedderCallsTo(calls);
576
577 final boolean result = tester.keyboardManager.handleEvent(keyEvent);
578
579 assertEquals(true, result);
580 assertEquals(calls.size(), 1);
581 assertEmbedderEventEquals(
582 calls.get(0).keyData,
583 Type.kDown,
584 PHYSICAL_KEY_A,
585 LOGICAL_KEY_A,
586 "a",
587 false,
589
590 // Don't send the key event to the text plugin if the only primary responder
591 // hasn't responded.
592 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
593 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
594
595 // If a primary responder handles the key event the propagation stops.
596 assertNotNull(calls.get(0).reply);
597 calls.get(0).reply.accept(true);
598 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
599 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
600 }
601
602 @Test
604 // Regression test for https://github.com/flutter/flutter/issues/141662.
605 final BinaryMessenger mockMessenger = mock(BinaryMessenger.class);
606 doAnswer(
607 invocation -> {
608 final BinaryMessenger.BinaryReply reply = invocation.getArgument(2);
609 // Simulate a null reply.
610 // In release mode, a null reply might happen when the engine sends a message
611 // before the framework has started.
612 reply.reply(null);
613 return null;
614 })
615 .when(mockMessenger)
616 .send(any(String.class), any(ByteBuffer.class), any(BinaryMessenger.BinaryReply.class));
617
618 final KeyboardManager.ViewDelegate mockView = mock(KeyboardManager.ViewDelegate.class);
619 doAnswer(invocation -> mockMessenger).when(mockView).getBinaryMessenger();
620
621 final KeyboardManager keyboardManager = new KeyboardManager(mockView);
622 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0);
623
624 boolean exceptionThrown = false;
625 try {
626 final boolean result = keyboardManager.handleEvent(keyEvent);
627 } catch (Exception exception) {
628 exceptionThrown = true;
629 }
630
631 assertEquals(false, exceptionThrown);
632 }
633
634 @Test
637 final ArrayList<CallRecord> calls = new ArrayList<>();
638
639 tester.recordChannelCallsTo(calls);
640 tester.recordEmbedderCallsTo(calls);
641 tester.respondToTextInputWith(true);
642
643 final boolean result =
644 tester.keyboardManager.handleEvent(
645 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0));
646
647 assertEquals(true, result);
648 assertEquals(calls.size(), 2);
649 assertEmbedderEventEquals(
650 calls.get(0).keyData,
651 Type.kDown,
652 PHYSICAL_KEY_A,
653 LOGICAL_KEY_A,
654 "a",
655 false,
657 assertChannelEventEquals(calls.get(1).channelObject, "keydown", KEYCODE_A);
658
659 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
660 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
661
662 calls.get(0).reply.accept(true);
663 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
664 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
665
666 calls.get(1).reply.accept(true);
667 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
668 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
669 }
670
671 @Test
674 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, 65);
675 final ArrayList<CallRecord> calls = new ArrayList<>();
676
677 tester.recordChannelCallsTo(calls);
678
679 final boolean result = tester.keyboardManager.handleEvent(keyEvent);
680
681 assertEquals(true, result);
682 assertEquals(calls.size(), 1);
683 assertChannelEventEquals(calls.get(0).channelObject, "keydown", 65);
684
685 // Don't send the key event to the text plugin if the only primary responder
686 // hasn't responded.
687 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
688 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
689
690 // If no primary responder handles the key event the propagates to the text
691 // input plugin.
692 assertNotNull(calls.get(0).reply);
693 // Let text input plugin handle the key event.
694 tester.respondToTextInputWith(true);
695 calls.get(0).reply.accept(false);
696
697 verify(tester.mockView, times(1)).onTextInputKeyEvent(keyEvent);
698 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
699 }
700
701 @Test
704 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, 65);
705 final ArrayList<CallRecord> calls = new ArrayList<>();
706
707 tester.recordChannelCallsTo(calls);
708
709 final boolean result = tester.keyboardManager.handleEvent(keyEvent);
710
711 assertEquals(true, result);
712 assertEquals(calls.size(), 1);
713 assertChannelEventEquals(calls.get(0).channelObject, "keydown", 65);
714
715 // Don't send the key event to the text plugin if the only primary responder
716 // hasn't responded.
717 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
718 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
719
720 // Neither the primary responders nor text input plugin handles the event.
721 tester.respondToTextInputWith(false);
722 calls.get(0).reply.accept(false);
723
724 verify(tester.mockView, times(1)).onTextInputKeyEvent(keyEvent);
725 verify(tester.mockView, times(1)).redispatch(keyEvent);
726 }
727
728 @Test
731 final ArrayList<CallRecord> calls = new ArrayList<>();
732
733 tester.recordChannelCallsTo(calls);
734
735 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, 65);
736 final boolean result = tester.keyboardManager.handleEvent(keyEvent);
737
738 assertEquals(true, result);
739 assertEquals(calls.size(), 1);
740 assertChannelEventEquals(calls.get(0).channelObject, "keydown", 65);
741
742 // Don't send the key event to the text plugin if the only primary responder
743 // hasn't responded.
744 verify(tester.mockView, times(0)).onTextInputKeyEvent(any(KeyEvent.class));
745 verify(tester.mockView, times(0)).redispatch(any(KeyEvent.class));
746
747 // Neither the primary responders nor text input plugin handles the event.
748 tester.respondToTextInputWith(false);
749 calls.get(0).reply.accept(false);
750
751 verify(tester.mockView, times(1)).onTextInputKeyEvent(keyEvent);
752 verify(tester.mockView, times(1)).redispatch(keyEvent);
753
754 // It's redispatched to the keyboard manager, but no eventual key calls.
755 assertEquals(calls.size(), 1);
756 }
757
758 @Test
759 public void tapLowerA() {
761 final ArrayList<CallRecord> calls = new ArrayList<>();
762
763 tester.recordEmbedderCallsTo(calls);
764 tester.respondToTextInputWith(true); // Suppress redispatching
765
766 assertEquals(
767 true,
768 tester.keyboardManager.handleEvent(
769 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0)));
770
771 verifyEmbedderEvents(
772 calls,
773 new KeyData[] {
774 buildKeyData(Type.kDown, PHYSICAL_KEY_A, LOGICAL_KEY_A, "a", false, DeviceType.kKeyboard),
775 });
776 calls.clear();
777
778 assertEquals(
779 true,
780 tester.keyboardManager.handleEvent(
781 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 1, 'a', 0)));
782 verifyEmbedderEvents(
783 calls,
784 new KeyData[] {
785 buildKeyData(
786 Type.kRepeat, PHYSICAL_KEY_A, LOGICAL_KEY_A, "a", false, DeviceType.kKeyboard),
787 });
788 calls.clear();
789
790 assertEquals(
791 true,
792 tester.keyboardManager.handleEvent(
793 new FakeKeyEvent(ACTION_UP, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0)));
794 verifyEmbedderEvents(
795 calls,
796 new KeyData[] {
797 buildKeyData(Type.kUp, PHYSICAL_KEY_A, LOGICAL_KEY_A, null, false, DeviceType.kKeyboard),
798 });
799 calls.clear();
800 }
801
802 @Test
803 public void tapUpperA() {
805 final ArrayList<CallRecord> calls = new ArrayList<>();
806
807 tester.recordEmbedderCallsTo(calls);
808 tester.respondToTextInputWith(true); // Suppress redispatching
809
810 // ShiftLeft
811 assertEquals(
812 true,
813 tester.keyboardManager.handleEvent(
814 new FakeKeyEvent(ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0x41)));
815 verifyEmbedderEvents(
816 calls,
817 new KeyData[] {
818 buildKeyData(
819 Type.kDown,
820 PHYSICAL_SHIFT_LEFT,
821 LOGICAL_SHIFT_LEFT,
822 null,
823 false,
825 });
826 calls.clear();
827
828 assertEquals(
829 true,
830 tester.keyboardManager.handleEvent(
831 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'A', 0x41)));
832 verifyEmbedderEvents(
833 calls,
834 new KeyData[] {
835 buildKeyData(Type.kDown, PHYSICAL_KEY_A, LOGICAL_KEY_A, "A", false, DeviceType.kKeyboard),
836 });
837 calls.clear();
838
839 assertEquals(
840 true,
841 tester.keyboardManager.handleEvent(
842 new FakeKeyEvent(ACTION_UP, SCAN_KEY_A, KEYCODE_A, 0, 'A', 0x41)));
843 verifyEmbedderEvents(
844 calls,
845 new KeyData[] {
846 buildKeyData(Type.kUp, PHYSICAL_KEY_A, LOGICAL_KEY_A, null, false, DeviceType.kKeyboard),
847 });
848 calls.clear();
849
850 // ShiftLeft
851 assertEquals(
852 true,
853 tester.keyboardManager.handleEvent(
854 new FakeKeyEvent(ACTION_UP, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0)));
855 verifyEmbedderEvents(
856 calls,
857 new KeyData[] {
858 buildKeyData(
859 Type.kUp, PHYSICAL_SHIFT_LEFT, LOGICAL_SHIFT_LEFT, null, false, DeviceType.kKeyboard),
860 });
861 calls.clear();
862 }
863
864 @Test
865 public void eventsWithMintedCodes() {
867 final ArrayList<CallRecord> calls = new ArrayList<>();
868
869 tester.recordEmbedderCallsTo(calls);
870 tester.respondToTextInputWith(true); // Suppress redispatching
871
872 // Zero scan code.
873 assertEquals(
874 true,
875 tester.keyboardManager.handleEvent(
876 new FakeKeyEvent(ACTION_DOWN, 0, KEYCODE_ENTER, 0, '\n', 0)));
877 verifyEmbedderEvents(
878 calls,
879 new KeyData[] {
880 buildKeyData(Type.kDown, 0x1100000042L, LOGICAL_ENTER, "\n", false, DeviceType.kKeyboard),
881 });
882 calls.clear();
883
884 // Zero scan code and zero key code.
885 assertEquals(
886 true, tester.keyboardManager.handleEvent(new FakeKeyEvent(ACTION_DOWN, 0, 0, 0, '\0', 0)));
887 verifyEmbedderEvents(
888 calls,
889 new KeyData[] {
890 buildKeyData(Type.kDown, 0, 0, null, true, DeviceType.kKeyboard),
891 });
892 calls.clear();
893
894 // Unrecognized scan code. (Fictional test)
895 assertEquals(
896 true,
897 tester.keyboardManager.handleEvent(
898 new FakeKeyEvent(ACTION_DOWN, 0xDEADBEEF, 0, 0, '\0', 0)));
899 verifyEmbedderEvents(
900 calls,
901 new KeyData[] {
902 buildKeyData(Type.kDown, 0x11DEADBEEFL, 0x1100000000L, null, false, DeviceType.kKeyboard),
903 });
904 calls.clear();
905
906 // Zero key code. (Fictional test; I have yet to find a real case.)
907 assertEquals(
908 true,
909 tester.keyboardManager.handleEvent(
910 new FakeKeyEvent(ACTION_DOWN, SCAN_ARROW_LEFT, 0, 0, '\0', 0)));
911 verifyEmbedderEvents(
912 calls,
913 new KeyData[] {
914 buildKeyData(
915 Type.kDown, PHYSICAL_ARROW_LEFT, 0x1100000000L, null, false, DeviceType.kKeyboard),
916 });
917 calls.clear();
918
919 // Unrecognized key code. (Fictional test)
920 assertEquals(
921 true,
922 tester.keyboardManager.handleEvent(
923 new FakeKeyEvent(ACTION_DOWN, SCAN_ARROW_RIGHT, 0xDEADBEEF, 0, '\0', 0)));
924 verifyEmbedderEvents(
925 calls,
926 new KeyData[] {
927 buildKeyData(
928 Type.kDown, PHYSICAL_ARROW_RIGHT, 0x11DEADBEEFL, null, false, DeviceType.kKeyboard),
929 });
930 calls.clear();
931 }
932
933 @Test
936 final ArrayList<CallRecord> calls = new ArrayList<>();
937
938 tester.recordEmbedderCallsTo(calls);
939 tester.respondToTextInputWith(true); // Suppress redispatching
940
941 assertEquals(
942 true,
943 tester.keyboardManager.handleEvent(
944 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0)));
945 verifyEmbedderEvents(
946 calls,
947 new KeyData[] {
948 buildKeyData(Type.kDown, PHYSICAL_KEY_A, LOGICAL_KEY_A, "a", false, DeviceType.kKeyboard),
949 });
950 calls.clear();
951
952 assertEquals(
953 true,
954 tester.keyboardManager.handleEvent(
955 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0)));
956 assertEquals(calls.size(), 2);
957 assertEmbedderEventEquals(
958 calls.get(0).keyData,
959 Type.kUp,
960 PHYSICAL_KEY_A,
961 LOGICAL_KEY_A,
962 null,
963 true,
965 assertEmbedderEventEquals(
966 calls.get(1).keyData,
967 Type.kDown,
968 PHYSICAL_KEY_A,
969 LOGICAL_KEY_A,
970 "a",
971 false,
973 calls.clear();
974 }
975
976 @Test
979 final ArrayList<CallRecord> calls = new ArrayList<>();
980
981 tester.recordEmbedderCallsTo(calls);
982 tester.respondToTextInputWith(true); // Suppress redispatching
983
984 assertEquals(
985 true,
986 tester.keyboardManager.handleEvent(
987 new FakeKeyEvent(ACTION_UP, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0)));
988 verifyEmbedderEvents(
989 calls,
990 new KeyData[] {
991 buildKeyData(Type.kDown, 0l, 0l, null, true, DeviceType.kKeyboard),
992 });
993 calls.clear();
994 }
995
996 @Test
997 public void modifierKeys() {
999 final ArrayList<CallRecord> calls = new ArrayList<>();
1000
1001 tester.recordEmbedderCallsTo(calls);
1002 tester.respondToTextInputWith(true); // Suppress redispatching
1003
1004 // ShiftLeft
1005 tester.keyboardManager.handleEvent(
1006 new FakeKeyEvent(ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0x41));
1007 verifyEmbedderEvents(
1008 calls,
1009 new KeyData[] {
1010 buildKeyData(
1011 Type.kDown,
1012 PHYSICAL_SHIFT_LEFT,
1013 LOGICAL_SHIFT_LEFT,
1014 null,
1015 false,
1017 });
1018 calls.clear();
1019
1020 tester.keyboardManager.handleEvent(
1021 new FakeKeyEvent(ACTION_UP, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0));
1022 verifyEmbedderEvents(
1023 calls,
1024 new KeyData[] {
1025 buildKeyData(
1026 Type.kUp, PHYSICAL_SHIFT_LEFT, LOGICAL_SHIFT_LEFT, null, false, DeviceType.kKeyboard),
1027 });
1028 calls.clear();
1029
1030 // ShiftRight
1031 tester.keyboardManager.handleEvent(
1032 new FakeKeyEvent(ACTION_DOWN, SCAN_SHIFT_RIGHT, KEYCODE_SHIFT_RIGHT, 0, '\0', 0x41));
1033 verifyEmbedderEvents(
1034 calls,
1035 new KeyData[] {
1036 buildKeyData(
1037 Type.kDown,
1038 PHYSICAL_SHIFT_RIGHT,
1039 LOGICAL_SHIFT_RIGHT,
1040 null,
1041 false,
1043 });
1044 calls.clear();
1045
1046 tester.keyboardManager.handleEvent(
1047 new FakeKeyEvent(ACTION_UP, SCAN_SHIFT_RIGHT, KEYCODE_SHIFT_RIGHT, 0, '\0', 0));
1048 verifyEmbedderEvents(
1049 calls,
1050 new KeyData[] {
1051 buildKeyData(
1052 Type.kUp,
1053 PHYSICAL_SHIFT_RIGHT,
1054 LOGICAL_SHIFT_RIGHT,
1055 null,
1056 false,
1058 });
1059 calls.clear();
1060
1061 // ControlLeft
1062 tester.keyboardManager.handleEvent(
1063 new FakeKeyEvent(ACTION_DOWN, SCAN_CONTROL_LEFT, KEYCODE_CTRL_LEFT, 0, '\0', 0x3000));
1064 verifyEmbedderEvents(
1065 calls,
1066 new KeyData[] {
1067 buildKeyData(
1068 Type.kDown,
1069 PHYSICAL_CONTROL_LEFT,
1070 LOGICAL_CONTROL_LEFT,
1071 null,
1072 false,
1074 });
1075 calls.clear();
1076
1077 tester.keyboardManager.handleEvent(
1078 new FakeKeyEvent(ACTION_UP, SCAN_CONTROL_LEFT, KEYCODE_CTRL_LEFT, 0, '\0', 0));
1079 verifyEmbedderEvents(
1080 calls,
1081 new KeyData[] {
1082 buildKeyData(
1083 Type.kUp,
1084 PHYSICAL_CONTROL_LEFT,
1085 LOGICAL_CONTROL_LEFT,
1086 null,
1087 false,
1089 });
1090 calls.clear();
1091
1092 // ControlRight
1093 tester.keyboardManager.handleEvent(
1094 new FakeKeyEvent(ACTION_DOWN, SCAN_CONTROL_RIGHT, KEYCODE_CTRL_RIGHT, 0, '\0', 0x3000));
1095 verifyEmbedderEvents(
1096 calls,
1097 new KeyData[] {
1098 buildKeyData(
1099 Type.kDown,
1100 PHYSICAL_CONTROL_RIGHT,
1101 LOGICAL_CONTROL_RIGHT,
1102 null,
1103 false,
1105 });
1106 calls.clear();
1107
1108 tester.keyboardManager.handleEvent(
1109 new FakeKeyEvent(ACTION_UP, SCAN_CONTROL_RIGHT, KEYCODE_CTRL_RIGHT, 0, '\0', 0));
1110 verifyEmbedderEvents(
1111 calls,
1112 new KeyData[] {
1113 buildKeyData(
1114 Type.kUp,
1115 PHYSICAL_CONTROL_RIGHT,
1116 LOGICAL_CONTROL_RIGHT,
1117 null,
1118 false,
1120 });
1121 calls.clear();
1122
1123 // AltLeft
1124 tester.keyboardManager.handleEvent(
1125 new FakeKeyEvent(ACTION_DOWN, SCAN_ALT_LEFT, KEYCODE_ALT_LEFT, 0, '\0', 0x12));
1126 verifyEmbedderEvents(
1127 calls,
1128 new KeyData[] {
1129 buildKeyData(
1130 Type.kDown, PHYSICAL_ALT_LEFT, LOGICAL_ALT_LEFT, null, false, DeviceType.kKeyboard),
1131 });
1132 calls.clear();
1133
1134 tester.keyboardManager.handleEvent(
1135 new FakeKeyEvent(ACTION_UP, SCAN_ALT_LEFT, KEYCODE_ALT_LEFT, 0, '\0', 0));
1136 verifyEmbedderEvents(
1137 calls,
1138 new KeyData[] {
1139 buildKeyData(
1140 Type.kUp, PHYSICAL_ALT_LEFT, LOGICAL_ALT_LEFT, null, false, DeviceType.kKeyboard),
1141 });
1142 calls.clear();
1143
1144 // AltRight
1145 tester.keyboardManager.handleEvent(
1146 new FakeKeyEvent(ACTION_DOWN, SCAN_ALT_RIGHT, KEYCODE_ALT_RIGHT, 0, '\0', 0x12));
1147 verifyEmbedderEvents(
1148 calls,
1149 new KeyData[] {
1150 buildKeyData(
1151 Type.kDown, PHYSICAL_ALT_RIGHT, LOGICAL_ALT_RIGHT, null, false, DeviceType.kKeyboard),
1152 });
1153 calls.clear();
1154
1155 tester.keyboardManager.handleEvent(
1156 new FakeKeyEvent(ACTION_UP, SCAN_ALT_RIGHT, KEYCODE_ALT_RIGHT, 0, '\0', 0));
1157 verifyEmbedderEvents(
1158 calls,
1159 new KeyData[] {
1160 buildKeyData(
1161 Type.kUp, PHYSICAL_ALT_RIGHT, LOGICAL_ALT_RIGHT, null, false, DeviceType.kKeyboard),
1162 });
1163 calls.clear();
1164 }
1165
1166 @Test
1167 public void nonUsKeys() {
1168 final KeyboardTester tester = new KeyboardTester();
1169 final ArrayList<CallRecord> calls = new ArrayList<>();
1170
1171 tester.recordEmbedderCallsTo(calls);
1172 tester.respondToTextInputWith(true); // Suppress redispatching
1173
1174 // French 1
1175 tester.keyboardManager.handleEvent(
1176 new FakeKeyEvent(ACTION_DOWN, SCAN_DIGIT1, KEYCODE_1, 0, '1', 0));
1177 verifyEmbedderEvents(
1178 calls,
1179 new KeyData[] {
1180 buildKeyData(
1181 Type.kDown, PHYSICAL_DIGIT1, LOGICAL_DIGIT1, "1", false, DeviceType.kKeyboard),
1182 });
1183 calls.clear();
1184
1185 tester.keyboardManager.handleEvent(
1186 new FakeKeyEvent(ACTION_UP, SCAN_DIGIT1, KEYCODE_1, 0, '1', 0));
1187 verifyEmbedderEvents(
1188 calls,
1189 new KeyData[] {
1190 buildKeyData(
1191 Type.kUp, PHYSICAL_DIGIT1, LOGICAL_DIGIT1, null, false, DeviceType.kKeyboard),
1192 });
1193 calls.clear();
1194
1195 // French Shift-1
1196 tester.keyboardManager.handleEvent(
1197 new FakeKeyEvent(ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0x41));
1198 calls.clear();
1199
1200 tester.keyboardManager.handleEvent(
1201 new FakeKeyEvent(ACTION_DOWN, SCAN_DIGIT1, KEYCODE_1, 0, '&', 0x41));
1202 verifyEmbedderEvents(
1203 calls,
1204 new KeyData[] {
1205 buildKeyData(
1206 Type.kDown, PHYSICAL_DIGIT1, LOGICAL_DIGIT1, "&", false, DeviceType.kKeyboard),
1207 });
1208 calls.clear();
1209
1210 tester.keyboardManager.handleEvent(
1211 new FakeKeyEvent(ACTION_UP, SCAN_DIGIT1, KEYCODE_1, 0, '&', 0x41));
1212 verifyEmbedderEvents(
1213 calls,
1214 new KeyData[] {
1215 buildKeyData(
1216 Type.kUp, PHYSICAL_DIGIT1, LOGICAL_DIGIT1, null, false, DeviceType.kKeyboard),
1217 });
1218 calls.clear();
1219
1220 tester.keyboardManager.handleEvent(
1221 new FakeKeyEvent(ACTION_UP, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0));
1222 calls.clear();
1223
1224 // Russian lowerA
1225 tester.keyboardManager.handleEvent(
1226 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 0, '\u0444', 0));
1227 verifyEmbedderEvents(
1228 calls,
1229 new KeyData[] {
1230 buildKeyData(Type.kDown, PHYSICAL_KEY_A, LOGICAL_KEY_A, "Ñ„", false, DeviceType.kKeyboard),
1231 });
1232 calls.clear();
1233
1234 tester.keyboardManager.handleEvent(
1235 new FakeKeyEvent(ACTION_UP, SCAN_KEY_A, KEYCODE_A, 0, '\u0444', 0));
1236 verifyEmbedderEvents(
1237 calls,
1238 new KeyData[] {
1239 buildKeyData(Type.kUp, PHYSICAL_KEY_A, LOGICAL_KEY_A, null, false, DeviceType.kKeyboard),
1240 });
1241 calls.clear();
1242 }
1243
1244 @Test
1246 // Test if ShiftLeft can be synchronized during events of ArrowLeft.
1247 final KeyboardTester tester = new KeyboardTester();
1248 final ArrayList<CallRecord> calls = new ArrayList<>();
1249
1250 tester.recordEmbedderCallsTo(calls);
1251 tester.respondToTextInputWith(true); // Suppress redispatching
1252
1253 final int SHIFT_LEFT_ON = META_SHIFT_LEFT_ON | META_SHIFT_ON;
1254
1255 assertEquals(
1256 true,
1257 tester.keyboardManager.handleEvent(
1258 new FakeKeyEvent(
1259 ACTION_DOWN, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', SHIFT_LEFT_ON)));
1260 assertEquals(calls.size(), 2);
1261 assertEmbedderEventEquals(
1262 calls.get(0).keyData,
1263 Type.kDown,
1264 PHYSICAL_SHIFT_LEFT,
1265 LOGICAL_SHIFT_LEFT,
1266 null,
1267 true,
1269 calls.clear();
1270
1271 assertEquals(
1272 true,
1273 tester.keyboardManager.handleEvent(
1274 new FakeKeyEvent(ACTION_UP, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', 0)));
1275 assertEquals(calls.size(), 2);
1276 assertEmbedderEventEquals(
1277 calls.get(0).keyData,
1278 Type.kUp,
1279 PHYSICAL_SHIFT_LEFT,
1280 LOGICAL_SHIFT_LEFT,
1281 null,
1282 true,
1284 calls.clear();
1285 }
1286
1287 @Test
1289 // Test if ShiftLeft can be synchronized during events of ShiftLeft.
1290 final KeyboardTester tester = new KeyboardTester();
1291 final ArrayList<CallRecord> calls = new ArrayList<>();
1292
1293 tester.recordEmbedderCallsTo(calls);
1294 tester.respondToTextInputWith(true); // Suppress redispatching
1295
1296 final int SHIFT_LEFT_ON = META_SHIFT_LEFT_ON | META_SHIFT_ON;
1297 // All 6 cases (3 types x 2 states) are arranged in the following order so that the starting
1298 // states for each case are the desired states.
1299
1300 // Repeat event when current state is 0.
1301 assertEquals(
1302 true,
1303 tester.keyboardManager.handleEvent(
1304 new FakeKeyEvent(
1305 ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 1, '\0', SHIFT_LEFT_ON)));
1306 assertEquals(calls.size(), 1);
1307 assertEmbedderEventEquals(
1308 calls.get(0).keyData,
1309 Type.kDown,
1310 PHYSICAL_SHIFT_LEFT,
1311 LOGICAL_SHIFT_LEFT,
1312 null,
1313 false,
1315 calls.clear();
1316
1317 // Down event when the current state is 1.
1318 assertEquals(
1319 true,
1320 tester.keyboardManager.handleEvent(
1321 new FakeKeyEvent(
1322 ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', SHIFT_LEFT_ON)));
1323 assertEquals(calls.size(), 2);
1324 assertEmbedderEventEquals(
1325 calls.get(0).keyData,
1326 Type.kUp,
1327 PHYSICAL_SHIFT_LEFT,
1328 LOGICAL_SHIFT_LEFT,
1329 null,
1330 true,
1332 assertEmbedderEventEquals(
1333 calls.get(1).keyData,
1334 Type.kDown,
1335 PHYSICAL_SHIFT_LEFT,
1336 LOGICAL_SHIFT_LEFT,
1337 null,
1338 false,
1340 calls.clear();
1341
1342 // Up event when the current state is 1.
1343 assertEquals(
1344 true,
1345 tester.keyboardManager.handleEvent(
1346 new FakeKeyEvent(ACTION_UP, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0)));
1347 assertEquals(calls.size(), 1);
1348 assertEmbedderEventEquals(
1349 calls.get(0).keyData,
1350 Type.kUp,
1351 PHYSICAL_SHIFT_LEFT,
1352 LOGICAL_SHIFT_LEFT,
1353 null,
1354 false,
1356 calls.clear();
1357
1358 // Up event when the current state is 0.
1359 assertEquals(
1360 true,
1361 tester.keyboardManager.handleEvent(
1362 new FakeKeyEvent(ACTION_UP, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0)));
1363 assertEquals(calls.size(), 1);
1364 assertEmbedderEventEquals(
1365 calls.get(0).keyData, Type.kDown, 0l, 0l, null, true, DeviceType.kKeyboard);
1366 calls.clear();
1367
1368 // Down event when the current state is 0.
1369 assertEquals(
1370 true,
1371 tester.keyboardManager.handleEvent(
1372 new FakeKeyEvent(
1373 ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', SHIFT_LEFT_ON)));
1374 assertEquals(calls.size(), 1);
1375 assertEmbedderEventEquals(
1376 calls.get(0).keyData,
1377 Type.kDown,
1378 PHYSICAL_SHIFT_LEFT,
1379 LOGICAL_SHIFT_LEFT,
1380 null,
1381 false,
1383 calls.clear();
1384
1385 // Repeat event when the current state is 1.
1386 assertEquals(
1387 true,
1388 tester.keyboardManager.handleEvent(
1389 new FakeKeyEvent(
1390 ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 1, '\0', SHIFT_LEFT_ON)));
1391 assertEquals(calls.size(), 1);
1392 assertEmbedderEventEquals(
1393 calls.get(0).keyData,
1394 Type.kRepeat,
1395 PHYSICAL_SHIFT_LEFT,
1396 LOGICAL_SHIFT_LEFT,
1397 null,
1398 false,
1400 calls.clear();
1401 }
1402
1403 @Test
1405 // Test if ShiftLeft can be synchronized during events of ShiftRight. The following events seem
1406 // to have weird metaStates that don't follow Android's documentation (always using left masks)
1407 // but are indeed observed on ChromeOS.
1408
1409 // UP_EVENT, truePressed: false
1410
1411 verifyEmbedderEvents(testShiftRightEvent(false, false, UP_EVENT, false), new KeyData[] {});
1412 verifyEmbedderEvents(
1413 testShiftRightEvent(false, true, UP_EVENT, false),
1414 new KeyData[] {
1415 buildShiftKeyData(SHIFT_RIGHT_EVENT, UP_EVENT, false),
1416 });
1417 verifyEmbedderEvents(
1418 testShiftRightEvent(true, false, UP_EVENT, false),
1419 new KeyData[] {
1420 buildShiftKeyData(SHIFT_LEFT_EVENT, UP_EVENT, true),
1421 });
1422 verifyEmbedderEvents(
1423 testShiftRightEvent(true, true, UP_EVENT, false),
1424 new KeyData[] {
1425 buildShiftKeyData(SHIFT_LEFT_EVENT, UP_EVENT, true),
1426 buildShiftKeyData(SHIFT_RIGHT_EVENT, UP_EVENT, false),
1427 });
1428
1429 // UP_EVENT, truePressed: true
1430
1431 verifyEmbedderEvents(
1432 testShiftRightEvent(false, false, UP_EVENT, true),
1433 new KeyData[] {
1434 buildShiftKeyData(SHIFT_LEFT_EVENT, DOWN_EVENT, true),
1435 });
1436 verifyEmbedderEvents(
1437 testShiftRightEvent(false, true, UP_EVENT, true),
1438 new KeyData[] {
1439 buildShiftKeyData(SHIFT_LEFT_EVENT, DOWN_EVENT, true),
1440 buildShiftKeyData(SHIFT_RIGHT_EVENT, UP_EVENT, false),
1441 });
1442 verifyEmbedderEvents(testShiftRightEvent(true, false, UP_EVENT, true), new KeyData[] {});
1443 verifyEmbedderEvents(
1444 testShiftRightEvent(true, true, UP_EVENT, true),
1445 new KeyData[] {
1446 buildShiftKeyData(SHIFT_RIGHT_EVENT, UP_EVENT, false),
1447 });
1448
1449 // DOWN_EVENT, truePressed: false - skipped, because they're impossible.
1450
1451 // DOWN_EVENT, truePressed: true
1452
1453 verifyEmbedderEvents(
1454 testShiftRightEvent(false, false, DOWN_EVENT, true),
1455 new KeyData[] {
1456 buildShiftKeyData(SHIFT_RIGHT_EVENT, DOWN_EVENT, false),
1457 });
1458 verifyEmbedderEvents(
1459 testShiftRightEvent(false, true, DOWN_EVENT, true),
1460 new KeyData[] {
1461 buildShiftKeyData(SHIFT_RIGHT_EVENT, UP_EVENT, true),
1462 buildShiftKeyData(SHIFT_RIGHT_EVENT, DOWN_EVENT, false),
1463 });
1464 verifyEmbedderEvents(
1465 testShiftRightEvent(true, false, DOWN_EVENT, true),
1466 new KeyData[] {
1467 buildShiftKeyData(SHIFT_RIGHT_EVENT, DOWN_EVENT, false),
1468 });
1469 verifyEmbedderEvents(
1470 testShiftRightEvent(true, true, DOWN_EVENT, true),
1471 new KeyData[] {
1472 buildShiftKeyData(SHIFT_RIGHT_EVENT, UP_EVENT, true),
1473 buildShiftKeyData(SHIFT_RIGHT_EVENT, DOWN_EVENT, false),
1474 });
1475 }
1476
1477 @Test
1479 // Test if other modifiers can be synchronized during events of ArrowLeft. Only the minimal
1480 // cases are used here since the full logic has been tested on ShiftLeft.
1481 final KeyboardTester tester = new KeyboardTester();
1482 final ArrayList<CallRecord> calls = new ArrayList<>();
1483
1484 tester.recordEmbedderCallsTo(calls);
1485 tester.respondToTextInputWith(true); // Suppress redispatching
1486
1487 assertEquals(
1488 true,
1489 tester.keyboardManager.handleEvent(
1490 new FakeKeyEvent(
1491 ACTION_DOWN, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', META_CTRL_ON)));
1492 assertEquals(calls.size(), 2);
1493 assertEmbedderEventEquals(
1494 calls.get(0).keyData,
1495 Type.kDown,
1496 PHYSICAL_CONTROL_LEFT,
1497 LOGICAL_CONTROL_LEFT,
1498 null,
1499 true,
1501 calls.clear();
1502
1503 assertEquals(
1504 true,
1505 tester.keyboardManager.handleEvent(
1506 new FakeKeyEvent(ACTION_UP, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', 0)));
1507 assertEquals(calls.size(), 2);
1508 assertEmbedderEventEquals(
1509 calls.get(0).keyData,
1510 Type.kUp,
1511 PHYSICAL_CONTROL_LEFT,
1512 LOGICAL_CONTROL_LEFT,
1513 null,
1514 true,
1516 calls.clear();
1517
1518 assertEquals(
1519 true,
1520 tester.keyboardManager.handleEvent(
1521 new FakeKeyEvent(
1522 ACTION_DOWN, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', META_ALT_ON)));
1523 assertEquals(calls.size(), 2);
1524 assertEmbedderEventEquals(
1525 calls.get(0).keyData,
1526 Type.kDown,
1527 PHYSICAL_ALT_LEFT,
1528 LOGICAL_ALT_LEFT,
1529 null,
1530 true,
1532 calls.clear();
1533
1534 assertEquals(
1535 true,
1536 tester.keyboardManager.handleEvent(
1537 new FakeKeyEvent(ACTION_UP, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', 0)));
1538 assertEquals(calls.size(), 2);
1539 assertEmbedderEventEquals(
1540 calls.get(0).keyData,
1541 Type.kUp,
1542 PHYSICAL_ALT_LEFT,
1543 LOGICAL_ALT_LEFT,
1544 null,
1545 true,
1547 calls.clear();
1548 }
1549
1550 // Regression test for https://github.com/flutter/flutter/issues/108124
1551 @Test
1553 // Test if ShiftLeft can be correctly synchronized during down events of
1554 // ShiftLeft that have 0 for their metaState.
1555 final KeyboardTester tester = new KeyboardTester();
1556 final ArrayList<CallRecord> calls = new ArrayList<>();
1557 // Even though the event is for ShiftRight, we still set SHIFT | SHIFT_LEFT here.
1558 // See the comment in synchronizePressingKey for the reason.
1559 final int SHIFT_LEFT_ON = META_SHIFT_LEFT_ON | META_SHIFT_ON;
1560
1561 tester.recordEmbedderCallsTo(calls);
1562 tester.respondToTextInputWith(true); // Suppress redispatching
1563
1564 // Test: Down event when the current state is 0.
1565 assertEquals(
1566 true,
1567 tester.keyboardManager.handleEvent(
1568 new FakeKeyEvent(ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', 0)));
1569 assertEquals(calls.size(), 2);
1570 assertEmbedderEventEquals(
1571 calls.get(0).keyData,
1572 Type.kDown,
1573 PHYSICAL_SHIFT_LEFT,
1574 LOGICAL_SHIFT_LEFT,
1575 null,
1576 false,
1578 assertEmbedderEventEquals(
1579 calls.get(1).keyData,
1580 Type.kUp,
1581 PHYSICAL_SHIFT_LEFT,
1582 LOGICAL_SHIFT_LEFT,
1583 null,
1584 true,
1586 calls.clear();
1587
1588 // A normal down event.
1589 assertEquals(
1590 true,
1591 tester.keyboardManager.handleEvent(
1592 new FakeKeyEvent(
1593 ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 0, '\0', SHIFT_LEFT_ON)));
1594 assertEquals(calls.size(), 1);
1595 assertEmbedderEventEquals(
1596 calls.get(0).keyData,
1597 Type.kDown,
1598 PHYSICAL_SHIFT_LEFT,
1599 LOGICAL_SHIFT_LEFT,
1600 null,
1601 false,
1603 calls.clear();
1604
1605 // Test: Repeat event when the current state is 0.
1606 assertEquals(
1607 true,
1608 tester.keyboardManager.handleEvent(
1609 new FakeKeyEvent(ACTION_DOWN, SCAN_SHIFT_LEFT, KEYCODE_SHIFT_LEFT, 1, '\0', 0)));
1610 assertEquals(calls.size(), 2);
1611 assertEmbedderEventEquals(
1612 calls.get(0).keyData,
1613 Type.kRepeat,
1614 PHYSICAL_SHIFT_LEFT,
1615 LOGICAL_SHIFT_LEFT,
1616 null,
1617 false,
1619 assertEmbedderEventEquals(
1620 calls.get(1).keyData,
1621 Type.kUp,
1622 PHYSICAL_SHIFT_LEFT,
1623 LOGICAL_SHIFT_LEFT,
1624 null,
1625 true,
1627 calls.clear();
1628 }
1629
1630 // Regression test for https://github.com/flutter/flutter/issues/110640
1631 @Test
1633 // Test if ShiftLeft can be correctly synchronized during down events of
1634 // ShiftLeft that have 0 for their metaState and 0 for their scanCode.
1635 final KeyboardTester tester = new KeyboardTester();
1636 final ArrayList<CallRecord> calls = new ArrayList<>();
1637
1638 tester.recordEmbedderCallsTo(calls);
1639 tester.respondToTextInputWith(true); // Suppress redispatching
1640
1641 // Test: DOWN event when the current state is 0 and scanCode is 0.
1642 final KeyEvent keyEvent = new FakeKeyEvent(ACTION_DOWN, 0, KEYCODE_SHIFT_LEFT, 0, '\0', 0);
1643 // Compute physicalKey in the same way as KeyboardManager.getPhysicalKey.
1644 final Long physicalKey = KEYCODE_SHIFT_LEFT | KeyboardMap.kAndroidPlane;
1645
1646 assertEquals(tester.keyboardManager.handleEvent(keyEvent), true);
1647 assertEquals(calls.size(), 2);
1648 assertEmbedderEventEquals(
1649 calls.get(0).keyData,
1650 Type.kDown,
1651 physicalKey,
1652 LOGICAL_SHIFT_LEFT,
1653 null,
1654 false,
1656 assertEmbedderEventEquals(
1657 calls.get(1).keyData,
1658 Type.kUp,
1659 physicalKey,
1660 LOGICAL_SHIFT_LEFT,
1661 null,
1662 true,
1664 calls.clear();
1665 }
1666
1667 @Test
1668 public void normalCapsLockEvents() {
1669 final KeyboardTester tester = new KeyboardTester();
1670 final ArrayList<CallRecord> calls = new ArrayList<>();
1671
1672 tester.recordEmbedderCallsTo(calls);
1673 tester.respondToTextInputWith(true); // Suppress redispatching
1674
1675 // The following two events seem to have weird metaStates that don't follow Android's
1676 // documentation (CapsLock flag set on down events) but are indeed observed on ChromeOS.
1677
1678 assertEquals(
1679 true,
1680 tester.keyboardManager.handleEvent(
1681 new FakeKeyEvent(
1682 ACTION_DOWN, SCAN_CAPS_LOCK, KEYCODE_CAPS_LOCK, 0, '\0', META_CAPS_LOCK_ON)));
1683 assertEquals(calls.size(), 1);
1684 assertEmbedderEventEquals(
1685 calls.get(0).keyData,
1686 Type.kDown,
1687 PHYSICAL_CAPS_LOCK,
1688 LOGICAL_CAPS_LOCK,
1689 null,
1690 false,
1692 calls.clear();
1693
1694 assertEquals(
1695 true,
1696 tester.keyboardManager.handleEvent(
1697 new FakeKeyEvent(ACTION_UP, SCAN_CAPS_LOCK, KEYCODE_CAPS_LOCK, 0, '\0', 0)));
1698 assertEquals(calls.size(), 1);
1699 assertEmbedderEventEquals(
1700 calls.get(0).keyData,
1701 Type.kUp,
1702 PHYSICAL_CAPS_LOCK,
1703 LOGICAL_CAPS_LOCK,
1704 null,
1705 false,
1707 calls.clear();
1708
1709 assertEquals(
1710 true,
1711 tester.keyboardManager.handleEvent(
1712 new FakeKeyEvent(
1713 ACTION_DOWN, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', META_CAPS_LOCK_ON)));
1714 assertEquals(calls.size(), 1);
1715 assertEmbedderEventEquals(
1716 calls.get(0).keyData,
1717 Type.kDown,
1718 PHYSICAL_ARROW_LEFT,
1719 LOGICAL_ARROW_LEFT,
1720 null,
1721 false,
1723 calls.clear();
1724
1725 assertEquals(
1726 true,
1727 tester.keyboardManager.handleEvent(
1728 new FakeKeyEvent(
1729 ACTION_UP, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', META_CAPS_LOCK_ON)));
1730 assertEquals(calls.size(), 1);
1731 assertEmbedderEventEquals(
1732 calls.get(0).keyData,
1733 Type.kUp,
1734 PHYSICAL_ARROW_LEFT,
1735 LOGICAL_ARROW_LEFT,
1736 null,
1737 false,
1739 calls.clear();
1740
1741 assertEquals(
1742 true,
1743 tester.keyboardManager.handleEvent(
1744 new FakeKeyEvent(
1745 ACTION_DOWN, SCAN_CAPS_LOCK, KEYCODE_CAPS_LOCK, 0, '\0', META_CAPS_LOCK_ON)));
1746 assertEquals(calls.size(), 1);
1747 assertEmbedderEventEquals(
1748 calls.get(0).keyData,
1749 Type.kDown,
1750 PHYSICAL_CAPS_LOCK,
1751 LOGICAL_CAPS_LOCK,
1752 null,
1753 false,
1755 calls.clear();
1756
1757 assertEquals(
1758 true,
1759 tester.keyboardManager.handleEvent(
1760 new FakeKeyEvent(ACTION_UP, SCAN_CAPS_LOCK, KEYCODE_CAPS_LOCK, 0, '\0', 0)));
1761 assertEquals(calls.size(), 1);
1762 assertEmbedderEventEquals(
1763 calls.get(0).keyData,
1764 Type.kUp,
1765 PHYSICAL_CAPS_LOCK,
1766 LOGICAL_CAPS_LOCK,
1767 null,
1768 false,
1770 calls.clear();
1771
1772 assertEquals(
1773 true,
1774 tester.keyboardManager.handleEvent(
1775 new FakeKeyEvent(ACTION_DOWN, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', 0)));
1776 assertEquals(calls.size(), 1);
1777 assertEmbedderEventEquals(
1778 calls.get(0).keyData,
1779 Type.kDown,
1780 PHYSICAL_ARROW_LEFT,
1781 LOGICAL_ARROW_LEFT,
1782 null,
1783 false,
1785 calls.clear();
1786
1787 assertEquals(
1788 true,
1789 tester.keyboardManager.handleEvent(
1790 new FakeKeyEvent(ACTION_UP, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', 0)));
1791 assertEquals(calls.size(), 1);
1792 assertEmbedderEventEquals(
1793 calls.get(0).keyData,
1794 Type.kUp,
1795 PHYSICAL_ARROW_LEFT,
1796 LOGICAL_ARROW_LEFT,
1797 null,
1798 false,
1800 calls.clear();
1801 }
1802
1803 @Test
1804 public void synchronizeCapsLock() {
1805 final KeyboardTester tester = new KeyboardTester();
1806 final ArrayList<CallRecord> calls = new ArrayList<>();
1807
1808 tester.recordEmbedderCallsTo(calls);
1809 tester.respondToTextInputWith(true); // Suppress redispatching
1810
1811 assertEquals(
1812 true,
1813 tester.keyboardManager.handleEvent(
1814 new FakeKeyEvent(
1815 ACTION_DOWN, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', META_CAPS_LOCK_ON)));
1816 assertEquals(calls.size(), 3);
1817 assertEmbedderEventEquals(
1818 calls.get(0).keyData,
1819 Type.kDown,
1820 PHYSICAL_CAPS_LOCK,
1821 LOGICAL_CAPS_LOCK,
1822 null,
1823 true,
1825 assertEmbedderEventEquals(
1826 calls.get(1).keyData,
1827 Type.kUp,
1828 PHYSICAL_CAPS_LOCK,
1829 LOGICAL_CAPS_LOCK,
1830 null,
1831 true,
1833 assertEmbedderEventEquals(
1834 calls.get(2).keyData,
1835 Type.kDown,
1836 PHYSICAL_ARROW_LEFT,
1837 LOGICAL_ARROW_LEFT,
1838 null,
1839 false,
1841 calls.clear();
1842
1843 assertEquals(
1844 true,
1845 tester.keyboardManager.handleEvent(
1846 new FakeKeyEvent(
1847 ACTION_DOWN, SCAN_CAPS_LOCK, KEYCODE_CAPS_LOCK, 0, '\0', META_CAPS_LOCK_ON)));
1848 assertEquals(calls.size(), 1);
1849 assertEmbedderEventEquals(
1850 calls.get(0).keyData,
1851 Type.kDown,
1852 PHYSICAL_CAPS_LOCK,
1853 LOGICAL_CAPS_LOCK,
1854 null,
1855 false,
1857 calls.clear();
1858
1859 assertEquals(
1860 true,
1861 tester.keyboardManager.handleEvent(
1862 new FakeKeyEvent(
1863 ACTION_UP, SCAN_ARROW_LEFT, KEYCODE_DPAD_LEFT, 0, '\0', META_CAPS_LOCK_ON)));
1864 assertEquals(calls.size(), 3);
1865 assertEmbedderEventEquals(
1866 calls.get(0).keyData,
1867 Type.kUp,
1868 PHYSICAL_CAPS_LOCK,
1869 LOGICAL_CAPS_LOCK,
1870 null,
1871 true,
1873 assertEmbedderEventEquals(
1874 calls.get(1).keyData,
1875 Type.kDown,
1876 PHYSICAL_CAPS_LOCK,
1877 LOGICAL_CAPS_LOCK,
1878 null,
1879 true,
1881 assertEmbedderEventEquals(
1882 calls.get(2).keyData,
1883 Type.kUp,
1884 PHYSICAL_ARROW_LEFT,
1885 LOGICAL_ARROW_LEFT,
1886 null,
1887 false,
1889 calls.clear();
1890
1891 assertEquals(
1892 true,
1893 tester.keyboardManager.handleEvent(
1894 new FakeKeyEvent(ACTION_UP, SCAN_CAPS_LOCK, KEYCODE_CAPS_LOCK, 0, '\0', 0)));
1895 assertEquals(calls.size(), 1);
1896 assertEmbedderEventEquals(
1897 calls.get(0).keyData,
1898 Type.kUp,
1899 PHYSICAL_CAPS_LOCK,
1900 LOGICAL_CAPS_LOCK,
1901 null,
1902 false,
1904 calls.clear();
1905 }
1906
1907 @Test
1908 public void getKeyboardState() {
1909 final KeyboardTester tester = new KeyboardTester();
1910
1911 tester.respondToTextInputWith(true); // Suppress redispatching.
1912
1913 // Initial pressed state is empty.
1914 assertEquals(tester.keyboardManager.getKeyboardState(), Map.of());
1915
1916 tester.keyboardManager.handleEvent(
1917 new FakeKeyEvent(ACTION_DOWN, SCAN_KEY_A, KEYCODE_A, 1, 'a', 0));
1918 assertEquals(tester.keyboardManager.getKeyboardState(), Map.of(PHYSICAL_KEY_A, LOGICAL_KEY_A));
1919
1920 tester.keyboardManager.handleEvent(
1921 new FakeKeyEvent(ACTION_UP, SCAN_KEY_A, KEYCODE_A, 0, 'a', 0));
1922 assertEquals(tester.keyboardManager.getKeyboardState(), Map.of());
1923 }
1924}
static SkISize times(const SkISize &size, float factor)
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition: M44Test.cpp:18
GLenum type
static CallRecord embedderCall(@NonNull KeyData keyData, @Nullable Consumer< Boolean > reply)
static CallRecord channelCall( @NonNull JSONObject channelObject, @Nullable Consumer< Boolean > reply)
void recordEmbedderCallsTo(@NonNull ArrayList< CallRecord > storage)
void recordChannelCallsTo(@NonNull ArrayList< CallRecord > storage)
static void assertChannelEventEquals( @NonNull JSONObject message, @NonNull String type, @NonNull Integer keyCode)
static void assertEmbedderEventEquals( @NonNull KeyData data, Type type, long physicalKey, long logicalKey, String character, boolean synthesized, DeviceType deviceType)
static ByteBuffer buildJsonResponse(boolean handled)
static KeyData buildShiftKeyData(boolean isLeft, boolean isDown, boolean isSynthesized)
static String printBufferBytes(@NonNull ByteBuffer buffer)
static KeyData buildKeyData(Type type, long physicalKey, long logicalKey, @Nullable String characters, boolean synthesized, DeviceType deviceType)
static ByteBuffer buildBinaryResponse(boolean handled)
static void verifyEmbedderEvents(List< CallRecord > receivedCalls, KeyData[] expectedData)
static List< CallRecord > testShiftRightEvent(boolean preEventLeftPressed, boolean preEventRightPressed, boolean rightEventIsDown, boolean truePressed)
boolean handleEvent(@NonNull KeyEvent keyEvent)
Object decodeMessage(@Nullable ByteBuffer message)
static final JSONMessageCodec INSTANCE
ByteBuffer encodeMessage(@Nullable Object message)
FlKeyEvent * event
GAsyncResult * result
Win32Message message
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530
Definition: tester.py:1
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63