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

Commit e439065

Browse files
committed
Fix tests
1 parent 580a28f commit e439065

File tree

5 files changed

+56
-46
lines changed

5 files changed

+56
-46
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ public boolean checkInputConnectionProxy(View view) {
721721
}
722722

723723
/**
724-
* Invoked when key is pressed or released.
724+
* Invoked when a hardware key is pressed or released, before the IME receives the key.
725725
*
726726
* <p>This method is typically invoked in response to the press of a physical keyboard key or a
727727
* D-pad button. It is generally not invoked when a virtual software keyboard is used, though a
@@ -733,14 +733,14 @@ public boolean checkInputConnectionProxy(View view) {
733733
*/
734734
@Override
735735
public boolean dispatchKeyEventPreIme(KeyEvent event) {
736+
// If the key processor doesn't handle it, then send it on to the
737+
// superclass. The key processor will typically handle all events except
738+
// those where it has re-dispatched the event after receiving a reply from
739+
// the framework that the framework did not handle it.
736740
return (isAttachedToFlutterEngine() && androidKeyProcessor.onKeyEvent(event))
737741
|| super.dispatchKeyEventPreIme(event);
738742
}
739743

740-
public AndroidKeyProcessor getKeyProcessor() {
741-
return androidKeyProcessor;
742-
}
743-
744744
/**
745745
* Invoked by Android when a user touch event occurs.
746746
*

shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@
3232
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
3333

3434
class InputConnectionAdaptor extends BaseInputConnection {
35-
private static final String TAG = "InputConnectionAdaptor";
36-
3735
private final View mFlutterView;
3836
private final int mClient;
3937
private final TextInputChannel textInputChannel;
40-
private final TextInputPlugin textInputPlugin;
38+
private final AndroidKeyProcessor keyProcessor;
4139
private final Editable mEditable;
4240
private final EditorInfo mEditorInfo;
4341
private int mBatchCount;
@@ -101,18 +99,18 @@ public InputConnectionAdaptor(
10199
View view,
102100
int client,
103101
TextInputChannel textInputChannel,
102+
AndroidKeyProcessor keyProcessor,
104103
Editable editable,
105104
EditorInfo editorInfo,
106-
FlutterJNI flutterJNI,
107-
TextInputPlugin textInputPlugin) {
105+
FlutterJNI flutterJNI) {
108106
super(view, true);
109107
mFlutterView = view;
110108
mClient = client;
111109
this.textInputChannel = textInputChannel;
112110
mEditable = editable;
113111
mEditorInfo = editorInfo;
114112
mBatchCount = 0;
115-
this.textInputPlugin = textInputPlugin;
113+
this.keyProcessor = keyProcessor;
116114
this.flutterTextUtils = new FlutterTextUtils(flutterJNI);
117115
// We create a dummy Layout with max width so that the selection
118116
// shifting acts as if all text were in one line.
@@ -134,10 +132,10 @@ public InputConnectionAdaptor(
134132
View view,
135133
int client,
136134
TextInputChannel textInputChannel,
135+
AndroidKeyProcessor keyProcessor,
137136
Editable editable,
138-
EditorInfo editorInfo,
139-
TextInputPlugin textInputPlugin) {
140-
this(view, client, textInputChannel, editable, editorInfo, new FlutterJNI(), textInputPlugin);
137+
EditorInfo editorInfo) {
138+
this(view, client, textInputChannel, keyProcessor, editable, editorInfo, new FlutterJNI());
141139
}
142140

143141
// Send the current state of the editable to Flutter.
@@ -330,13 +328,12 @@ private static int clampIndexToEditable(int index, Editable editable) {
330328

331329
@Override
332330
public boolean sendKeyEvent(KeyEvent event) {
333-
String type = event.getAction() == KeyEvent.ACTION_DOWN ? "DOWN" : "UP";
334-
int action = event.getAction();
335-
AndroidKeyProcessor processor = textInputPlugin.getKeyEventProcessor();
336-
if (processor != null) {
337-
if (processor.onKeyEvent(event)) {
338-
return true;
339-
}
331+
// Give the key processor a chance to process this event. It will send it
332+
// to the framework to be handled and return true. If the framework ends up
333+
// not handling it, the processor will re-send the event, this time
334+
// returning false so that it can be processed here.
335+
if (keyProcessor != null && keyProcessor.onKeyEvent(event)) {
336+
return true;
340337
}
341338

342339
markDirty();

shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ public InputConnection createInputConnection(View view, EditorInfo outAttrs) {
465465

466466
InputConnectionAdaptor connection =
467467
new InputConnectionAdaptor(
468-
view, inputTarget.id, textInputChannel, mEditable, outAttrs, this);
468+
view, inputTarget.id, textInputChannel, keyProcessor, mEditable, outAttrs);
469469
outAttrs.initialSelStart = Selection.getSelectionStart(mEditable);
470470
outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable);
471471

shell/platform/android/test/io/flutter/embedding/android/AndroidKeyProcessorTest.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ public void respondsTrueWhenHandlingNewEvents() {
4949
AndroidKeyProcessor processor =
5050
new AndroidKeyProcessor(fakeView, fakeKeyEventChannel, mock(TextInputPlugin.class));
5151

52-
boolean result = processor.onKeyDown(new FakeKeyEvent(KeyEvent.ACTION_DOWN, 65));
52+
boolean result = processor.onKeyEvent(new FakeKeyEvent(KeyEvent.ACTION_DOWN, 65));
5353
assertEquals(true, result);
5454
verify(fakeKeyEventChannel, times(1)).keyDown(any(KeyEventChannel.FlutterKeyEvent.class));
5555
verify(fakeKeyEventChannel, times(0)).keyUp(any(KeyEventChannel.FlutterKeyEvent.class));
56-
verify(fakeView, times(0)).dispatchKeyEvent(any(KeyEvent.class));
56+
verify(fakeView, times(0)).dispatchKeyEventPreIme(any(KeyEvent.class));
5757
}
5858

5959
public void synthesizesEventsWhenKeyDownNotHandled() {
@@ -79,31 +79,31 @@ public View answer(InvocationOnMock invocation) throws Throwable {
7979
ArgumentCaptor.forClass(KeyEventChannel.FlutterKeyEvent.class);
8080
FakeKeyEvent fakeKeyEvent = new FakeKeyEvent(KeyEvent.ACTION_DOWN, 65);
8181

82-
boolean result = processor.onKeyDown(fakeKeyEvent);
82+
boolean result = processor.onKeyEvent(fakeKeyEvent);
8383
assertEquals(true, result);
8484

8585
// Capture the FlutterKeyEvent so we can find out its event ID to use when
8686
// faking our response.
8787
verify(fakeKeyEventChannel, times(1)).keyDown(eventCaptor.capture());
8888
boolean[] dispatchResult = {true};
89-
when(fakeView.dispatchKeyEvent(any(KeyEvent.class)))
89+
when(fakeView.dispatchKeyEventPreIme(any(KeyEvent.class)))
9090
.then(
9191
new Answer<Boolean>() {
9292
@Override
9393
public Boolean answer(InvocationOnMock invocation) throws Throwable {
9494
KeyEvent event = (KeyEvent) invocation.getArguments()[0];
9595
assertEquals(fakeKeyEvent, event);
96-
dispatchResult[0] = processor.onKeyDown(event);
96+
dispatchResult[0] = processor.onKeyEvent(event);
9797
return dispatchResult[0];
9898
}
9999
});
100100

101101
// Fake a response from the framework.
102102
handlerCaptor.getValue().onKeyEventNotHandled(eventCaptor.getValue().eventId);
103-
verify(fakeView, times(1)).dispatchKeyEvent(fakeKeyEvent);
103+
verify(fakeView, times(1)).dispatchKeyEventPreIme(fakeKeyEvent);
104104
assertEquals(false, dispatchResult[0]);
105105
verify(fakeKeyEventChannel, times(0)).keyUp(any(KeyEventChannel.FlutterKeyEvent.class));
106-
verify(fakeRootView, times(1)).dispatchKeyEvent(fakeKeyEvent);
106+
verify(fakeRootView, times(1)).dispatchKeyEventPreIme(fakeKeyEvent);
107107
}
108108

109109
public void synthesizesEventsWhenKeyUpNotHandled() {
@@ -129,31 +129,31 @@ public View answer(InvocationOnMock invocation) throws Throwable {
129129
ArgumentCaptor.forClass(KeyEventChannel.FlutterKeyEvent.class);
130130
FakeKeyEvent fakeKeyEvent = new FakeKeyEvent(KeyEvent.ACTION_UP, 65);
131131

132-
boolean result = processor.onKeyUp(fakeKeyEvent);
132+
boolean result = processor.onKeyEvent(fakeKeyEvent);
133133
assertEquals(true, result);
134134

135135
// Capture the FlutterKeyEvent so we can find out its event ID to use when
136136
// faking our response.
137137
verify(fakeKeyEventChannel, times(1)).keyUp(eventCaptor.capture());
138138
boolean[] dispatchResult = {true};
139-
when(fakeView.dispatchKeyEvent(any(KeyEvent.class)))
139+
when(fakeView.dispatchKeyEventPreIme(any(KeyEvent.class)))
140140
.then(
141141
new Answer<Boolean>() {
142142
@Override
143143
public Boolean answer(InvocationOnMock invocation) throws Throwable {
144144
KeyEvent event = (KeyEvent) invocation.getArguments()[0];
145145
assertEquals(fakeKeyEvent, event);
146-
dispatchResult[0] = processor.onKeyUp(event);
146+
dispatchResult[0] = processor.onKeyEvent(event);
147147
return dispatchResult[0];
148148
}
149149
});
150150

151151
// Fake a response from the framework.
152152
handlerCaptor.getValue().onKeyEventNotHandled(eventCaptor.getValue().eventId);
153-
verify(fakeView, times(1)).dispatchKeyEvent(fakeKeyEvent);
153+
verify(fakeView, times(1)).dispatchKeyEventPreIme(fakeKeyEvent);
154154
assertEquals(false, dispatchResult[0]);
155155
verify(fakeKeyEventChannel, times(0)).keyUp(any(KeyEventChannel.FlutterKeyEvent.class));
156-
verify(fakeRootView, times(1)).dispatchKeyEvent(fakeKeyEvent);
156+
verify(fakeRootView, times(1)).dispatchKeyEventPreIme(fakeKeyEvent);
157157
}
158158

159159
@NonNull

shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import android.view.View;
2626
import android.view.inputmethod.EditorInfo;
2727
import android.view.inputmethod.ExtractedText;
28+
import io.flutter.embedding.android.AndroidKeyProcessor;
2829
import io.flutter.embedding.engine.FlutterJNI;
2930
import io.flutter.embedding.engine.dart.DartExecutor;
3031
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
@@ -68,14 +69,15 @@ public void inputConnectionAdaptor_ReceivesEnter() throws NullPointerException {
6869
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
6970
int inputTargetId = 0;
7071
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
72+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
7173
Editable mEditable = Editable.Factory.getInstance().newEditable("");
7274
Editable spyEditable = spy(mEditable);
7375
EditorInfo outAttrs = new EditorInfo();
7476
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
7577

7678
InputConnectionAdaptor inputConnectionAdaptor =
7779
new InputConnectionAdaptor(
78-
testView, inputTargetId, textInputChannel, spyEditable, outAttrs);
80+
testView, inputTargetId, textInputChannel, mockKeyProcessor, spyEditable, outAttrs);
7981

8082
// Send an enter key and make sure the Editable received it.
8183
FakeKeyEvent keyEvent = new FakeKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
@@ -156,10 +158,11 @@ public void testPerformPrivateCommand_dataIsNull() throws JSONException {
156158
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
157159
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
158160
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
161+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
159162
Editable editable = sampleEditable(0, 0);
160163
InputConnectionAdaptor adaptor =
161164
new InputConnectionAdaptor(
162-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
165+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
163166
adaptor.performPrivateCommand("actionCommand", null);
164167

165168
ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
@@ -183,10 +186,11 @@ public void testPerformPrivateCommand_dataIsByteArray() throws JSONException {
183186
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
184187
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
185188
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
189+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
186190
Editable editable = sampleEditable(0, 0);
187191
InputConnectionAdaptor adaptor =
188192
new InputConnectionAdaptor(
189-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
193+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
190194

191195
Bundle bundle = new Bundle();
192196
byte[] buffer = new byte[] {'a', 'b', 'c', 'd'};
@@ -216,10 +220,11 @@ public void testPerformPrivateCommand_dataIsByte() throws JSONException {
216220
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
217221
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
218222
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
223+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
219224
Editable editable = sampleEditable(0, 0);
220225
InputConnectionAdaptor adaptor =
221226
new InputConnectionAdaptor(
222-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
227+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
223228

224229
Bundle bundle = new Bundle();
225230
byte b = 3;
@@ -247,10 +252,11 @@ public void testPerformPrivateCommand_dataIsCharArray() throws JSONException {
247252
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
248253
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
249254
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
255+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
250256
Editable editable = sampleEditable(0, 0);
251257
InputConnectionAdaptor adaptor =
252258
new InputConnectionAdaptor(
253-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
259+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
254260

255261
Bundle bundle = new Bundle();
256262
char[] buffer = new char[] {'a', 'b', 'c', 'd'};
@@ -281,10 +287,11 @@ public void testPerformPrivateCommand_dataIsChar() throws JSONException {
281287
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
282288
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
283289
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
290+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
284291
Editable editable = sampleEditable(0, 0);
285292
InputConnectionAdaptor adaptor =
286293
new InputConnectionAdaptor(
287-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
294+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
288295

289296
Bundle bundle = new Bundle();
290297
char b = 'a';
@@ -312,10 +319,11 @@ public void testPerformPrivateCommand_dataIsCharSequenceArray() throws JSONExcep
312319
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
313320
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
314321
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
322+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
315323
Editable editable = sampleEditable(0, 0);
316324
InputConnectionAdaptor adaptor =
317325
new InputConnectionAdaptor(
318-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
326+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
319327

320328
Bundle bundle = new Bundle();
321329
CharSequence charSequence1 = new StringBuffer("abc");
@@ -347,10 +355,11 @@ public void testPerformPrivateCommand_dataIsCharSequence() throws JSONException
347355
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
348356
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
349357
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
358+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
350359
Editable editable = sampleEditable(0, 0);
351360
InputConnectionAdaptor adaptor =
352361
new InputConnectionAdaptor(
353-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
362+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
354363

355364
Bundle bundle = new Bundle();
356365
CharSequence charSequence = new StringBuffer("abc");
@@ -380,10 +389,11 @@ public void testPerformPrivateCommand_dataIsFloat() throws JSONException {
380389
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
381390
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
382391
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
392+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
383393
Editable editable = sampleEditable(0, 0);
384394
InputConnectionAdaptor adaptor =
385395
new InputConnectionAdaptor(
386-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
396+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
387397

388398
Bundle bundle = new Bundle();
389399
float value = 0.5f;
@@ -411,10 +421,11 @@ public void testPerformPrivateCommand_dataIsFloatArray() throws JSONException {
411421
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
412422
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
413423
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
424+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
414425
Editable editable = sampleEditable(0, 0);
415426
InputConnectionAdaptor adaptor =
416427
new InputConnectionAdaptor(
417-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
428+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
418429

419430
Bundle bundle = new Bundle();
420431
float[] value = {0.5f, 0.6f};
@@ -907,14 +918,15 @@ public void inputConnectionAdaptor_RepeatFilter() throws NullPointerException {
907918
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
908919
int inputTargetId = 0;
909920
TestTextInputChannel textInputChannel = new TestTextInputChannel(dartExecutor);
921+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
910922
Editable mEditable = Editable.Factory.getInstance().newEditable("");
911923
Editable spyEditable = spy(mEditable);
912924
EditorInfo outAttrs = new EditorInfo();
913925
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
914926

915927
InputConnectionAdaptor inputConnectionAdaptor =
916928
new InputConnectionAdaptor(
917-
testView, inputTargetId, textInputChannel, spyEditable, outAttrs);
929+
testView, inputTargetId, textInputChannel, mockKeyProcessor, spyEditable, outAttrs);
918930

919931
inputConnectionAdaptor.beginBatchEdit();
920932
assertEquals(textInputChannel.updateEditingStateInvocations, 0);
@@ -1159,6 +1171,7 @@ private static InputConnectionAdaptor sampleInputConnectionAdaptor(Editable edit
11591171
int client = 0;
11601172
TextInputChannel textInputChannel = mock(TextInputChannel.class);
11611173
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
1174+
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
11621175
when(mockFlutterJNI.nativeFlutterTextUtilsIsEmoji(anyInt()))
11631176
.thenAnswer((invocation) -> Emoji.isEmoji((int) invocation.getArguments()[0]));
11641177
when(mockFlutterJNI.nativeFlutterTextUtilsIsEmojiModifier(anyInt()))
@@ -1175,7 +1188,7 @@ private static InputConnectionAdaptor sampleInputConnectionAdaptor(Editable edit
11751188
.thenAnswer(
11761189
(invocation) -> Emoji.isRegionalIndicatorSymbol((int) invocation.getArguments()[0]));
11771190
return new InputConnectionAdaptor(
1178-
testView, client, textInputChannel, editable, null, mockFlutterJNI);
1191+
testView, client, textInputChannel, mockKeyProcessor, editable, null, mockFlutterJNI);
11791192
}
11801193

11811194
private class TestTextInputChannel extends TextInputChannel {

0 commit comments

Comments
 (0)