1package io.flutter.plugin.editing;
3import static org.junit.Assert.assertEquals;
4import static org.junit.Assert.assertFalse;
5import static org.junit.Assert.assertNull;
6import static org.junit.Assert.assertTrue;
7import static org.mockito.ArgumentMatchers.any;
8import static org.mockito.ArgumentMatchers.anyInt;
9import static org.mockito.ArgumentMatchers.isNull;
10import static org.mockito.Mockito.anyString;
11import static org.mockito.Mockito.eq;
12import static org.mockito.Mockito.mock;
13import static org.mockito.Mockito.never;
14import static org.mockito.Mockito.spy;
15import static org.mockito.Mockito.times;
16import static org.mockito.Mockito.verify;
17import static org.mockito.Mockito.when;
19import android.content.ClipDescription;
20import android.content.ClipboardManager;
21import android.content.ContentResolver;
23import android.content.res.AssetManager;
28import android.text.SpannableStringBuilder;
31import android.view.inputmethod.CursorAnchorInfo;
32import android.view.inputmethod.EditorInfo;
33import android.view.inputmethod.ExtractedText;
34import android.view.inputmethod.ExtractedTextRequest;
35import android.view.inputmethod.InputConnection;
36import android.view.inputmethod.InputContentInfo;
37import android.view.inputmethod.InputMethodManager;
38import androidx.core.view.inputmethod.InputConnectionCompat;
39import androidx.test.core.app.ApplicationProvider;
40import androidx.test.ext.junit.runners.AndroidJUnit4;
41import com.ibm.icu.lang.UCharacter;
42import com.ibm.icu.lang.UProperty;
43import io.flutter.embedding.android.KeyboardManager;
44import io.flutter.embedding.engine.FlutterJNI;
45import io.flutter.embedding.engine.dart.DartExecutor;
46import io.flutter.embedding.engine.systemchannels.TextInputChannel;
47import io.flutter.plugin.common.JSONMethodCodec;
48import io.flutter.plugin.common.MethodCall;
49import io.flutter.util.FakeKeyEvent;
50import java.io.ByteArrayInputStream;
51import java.nio.ByteBuffer;
52import java.nio.charset.Charset;
53import org.json.JSONArray;
54import org.json.JSONException;
55import org.junit.Before;
57import org.junit.runner.RunWith;
58import org.mockito.ArgumentCaptor;
59import org.mockito.Mock;
60import org.mockito.MockitoAnnotations;
61import org.robolectric.Shadows;
62import org.robolectric.annotation.Config;
63import org.robolectric.annotation.Implementation;
64import org.robolectric.annotation.Implements;
65import org.robolectric.shadow.api.Shadow;
66import org.robolectric.shadows.ShadowContentResolver;
67import org.robolectric.shadows.ShadowInputMethodManager;
71 shadows = {InputConnectionAdaptorTest.TestImm.class})
72@RunWith(AndroidJUnit4.class)
74 private final Context ctx = ApplicationProvider.getApplicationContext();
75 private ContentResolver contentResolver;
76 private ShadowContentResolver shadowContentResolver;
80 private void verifyMethodCall(ByteBuffer
buffer, String methodName, String[] expectedArgs)
81 throws JSONException {
84 assertEquals(methodName, methodCall.method);
85 if (expectedArgs !=
null) {
86 JSONArray
args = methodCall.arguments();
87 assertEquals(expectedArgs.length,
args.length());
88 for (
int i = 0; i <
args.length(); i++) {
89 assertEquals(expectedArgs[i],
args.get(i).toString());
96 MockitoAnnotations.openMocks(
this);
97 contentResolver = ctx.getContentResolver();
98 shadowContentResolver = Shadows.shadowOf(contentResolver);
103 View testView =
new View(ctx);
104 FlutterJNI mockFlutterJni = mock(FlutterJNI.class);
105 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
106 int inputTargetId = 0;
107 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
109 Selection.setSelection(mEditable, 0, 0);
111 EditorInfo outAttrs =
new EditorInfo();
112 outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
116 testView, inputTargetId, textInputChannel, mockKeyboardManager, spyEditable, outAttrs);
119 FakeKeyEvent keyEvent =
new FakeKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
'\n');
121 verify(spyEditable,
times(1)).insert(
eq(0), anyString());
132 assertTrue(didConsume);
133 assertEquals(0, Selection.getSelectionStart(editable));
137 @SuppressWarnings(
"deprecation")
141 ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class);
145 CharSequence textToBeCut = editable.subSequence(selStart, selEnd);
150 assertTrue(didConsume);
151 assertTrue(clipboardManager.hasText());
152 assertEquals(textToBeCut, clipboardManager.getPrimaryClip().getItemAt(0).getText());
153 assertFalse(editable.
toString().contains(textToBeCut));
156 @SuppressWarnings(
"deprecation")
160 ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class);
166 assertFalse(clipboardManager.hasText());
170 assertTrue(didConsume);
171 assertTrue(clipboardManager.hasText());
173 editable.subSequence(selStart, selEnd),
174 clipboardManager.getPrimaryClip().getItemAt(0).getText());
177 @SuppressWarnings(
"deprecation")
181 ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class);
182 String textToBePasted =
"deadbeef";
183 clipboardManager.setText(textToBePasted);
189 assertTrue(didConsume);
190 assertTrue(editable.
toString().startsWith(textToBePasted));
193 @SuppressWarnings(
"deprecation")
197 View testView =
new View(ctx);
199 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
200 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
201 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
213 String uri =
"content://mock/uri/test/commitContent";
214 Charset charset = Charset.forName(
"UTF-8");
215 String fakeImageData =
"fake image data";
216 byte[] fakeImageDataBytes = fakeImageData.getBytes(charset);
217 shadowContentResolver.registerInputStream(
218 Uri.parse(uri),
new ByteArrayInputStream(fakeImageDataBytes));
220 boolean commitContentSuccess =
222 new InputContentInfo(
224 new ClipDescription(
"commitContent test",
new String[] {
"image/png"})),
225 InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION,
227 assertTrue(commitContentSuccess);
229 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
230 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
231 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
232 assertEquals(
"flutter/textinput", channelCaptor.getValue());
234 String fakeImageDataIntString =
"";
235 for (
int i = 0; i < fakeImageDataBytes.length; i++) {
236 int byteAsInt = fakeImageDataBytes[i];
237 fakeImageDataIntString += byteAsInt;
238 if (i < (fakeImageDataBytes.length - 1)) {
239 fakeImageDataIntString +=
",";
243 bufferCaptor.getValue(),
244 "TextInputClient.performAction",
247 "TextInputAction.commitContent",
249 + fakeImageDataIntString
250 +
"],\"mimeType\":\"image\\/png\",\"uri\":\"content:\\/\\/mock\\/uri\\/test\\/commitContent\"}"
254 @SuppressWarnings(
"deprecation")
258 View testView =
new View(ctx);
260 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
261 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
262 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
275 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
276 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
277 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
278 assertEquals(
"flutter/textinput", channelCaptor.getValue());
280 bufferCaptor.getValue(),
281 "TextInputClient.performPrivateCommand",
282 new String[] {
"0",
"{\"action\":\"actionCommand\"}"});
285 @SuppressWarnings(
"deprecation")
289 View testView =
new View(ctx);
291 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
292 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
293 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
305 Bundle bundle =
new Bundle();
306 byte[]
buffer =
new byte[] {
'a',
'b',
'c',
'd'};
307 bundle.putByteArray(
"keyboard_layout",
buffer);
310 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
311 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
312 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
313 assertEquals(
"flutter/textinput", channelCaptor.getValue());
315 bufferCaptor.getValue(),
316 "TextInputClient.performPrivateCommand",
318 "0",
"{\"data\":{\"keyboard_layout\":[97,98,99,100]},\"action\":\"actionCommand\"}"
322 @SuppressWarnings(
"deprecation")
326 View testView =
new View(ctx);
328 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
329 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
330 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
342 Bundle bundle =
new Bundle();
344 bundle.putByte(
"keyboard_layout",
b);
347 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
348 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
349 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
350 assertEquals(
"flutter/textinput", channelCaptor.getValue());
352 bufferCaptor.getValue(),
353 "TextInputClient.performPrivateCommand",
354 new String[] {
"0",
"{\"data\":{\"keyboard_layout\":3},\"action\":\"actionCommand\"}"});
357 @SuppressWarnings(
"deprecation")
361 View testView =
new View(ctx);
363 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
364 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
365 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
377 Bundle bundle =
new Bundle();
378 char[]
buffer =
new char[] {
'a',
'b',
'c',
'd'};
379 bundle.putCharArray(
"keyboard_layout",
buffer);
382 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
383 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
384 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
385 assertEquals(
"flutter/textinput", channelCaptor.getValue());
387 bufferCaptor.getValue(),
388 "TextInputClient.performPrivateCommand",
391 "{\"data\":{\"keyboard_layout\":[\"a\",\"b\",\"c\",\"d\"]},\"action\":\"actionCommand\"}"
395 @SuppressWarnings(
"deprecation")
399 View testView =
new View(ctx);
401 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
402 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
403 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
415 Bundle bundle =
new Bundle();
417 bundle.putChar(
"keyboard_layout",
b);
420 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
421 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
422 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
423 assertEquals(
"flutter/textinput", channelCaptor.getValue());
425 bufferCaptor.getValue(),
426 "TextInputClient.performPrivateCommand",
427 new String[] {
"0",
"{\"data\":{\"keyboard_layout\":\"a\"},\"action\":\"actionCommand\"}"});
430 @SuppressWarnings(
"deprecation")
434 View testView =
new View(ctx);
436 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
437 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
438 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
450 Bundle bundle =
new Bundle();
451 CharSequence charSequence1 =
new StringBuffer(
"abc");
452 CharSequence charSequence2 =
new StringBuffer(
"efg");
453 CharSequence[]
value = {charSequence1, charSequence2};
454 bundle.putCharSequenceArray(
"keyboard_layout",
value);
457 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
458 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
459 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
460 assertEquals(
"flutter/textinput", channelCaptor.getValue());
462 bufferCaptor.getValue(),
463 "TextInputClient.performPrivateCommand",
465 "0",
"{\"data\":{\"keyboard_layout\":[\"abc\",\"efg\"]},\"action\":\"actionCommand\"}"
469 @SuppressWarnings(
"deprecation")
473 View testView =
new View(ctx);
475 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
476 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
477 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
489 Bundle bundle =
new Bundle();
490 CharSequence charSequence =
new StringBuffer(
"abc");
491 bundle.putCharSequence(
"keyboard_layout", charSequence);
494 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
495 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
496 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
497 assertEquals(
"flutter/textinput", channelCaptor.getValue());
499 bufferCaptor.getValue(),
500 "TextInputClient.performPrivateCommand",
502 "0",
"{\"data\":{\"keyboard_layout\":\"abc\"},\"action\":\"actionCommand\"}"
506 @SuppressWarnings(
"deprecation")
510 View testView =
new View(ctx);
512 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
513 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
514 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
526 Bundle bundle =
new Bundle();
528 bundle.putFloat(
"keyboard_layout",
value);
531 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
532 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
533 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
534 assertEquals(
"flutter/textinput", channelCaptor.getValue());
536 bufferCaptor.getValue(),
537 "TextInputClient.performPrivateCommand",
538 new String[] {
"0",
"{\"data\":{\"keyboard_layout\":0.5},\"action\":\"actionCommand\"}"});
541 @SuppressWarnings(
"deprecation")
545 View testView =
new View(ctx);
547 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
548 DartExecutor dartExecutor = spy(
new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
549 TextInputChannel textInputChannel =
new TextInputChannel(dartExecutor);
561 Bundle bundle =
new Bundle();
562 float[]
value = {0.5f, 0.6f};
563 bundle.putFloatArray(
"keyboard_layout",
value);
566 ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
567 ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
568 verify(dartExecutor,
times(1)).send(channelCaptor.capture(), bufferCaptor.capture(), isNull());
569 assertEquals(
"flutter/textinput", channelCaptor.getValue());
571 bufferCaptor.getValue(),
572 "TextInputClient.performPrivateCommand",
574 "0",
"{\"data\":{\"keyboard_layout\":[0.5,0.6]},\"action\":\"actionCommand\"}"
586 KeyEvent shiftKeyUp =
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT);
589 assertFalse(didConsume);
590 assertEquals(selStart, Selection.getSelectionStart(editable));
591 assertEquals(selEnd, Selection.getSelectionEnd(editable));
600 KeyEvent leftKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
603 assertTrue(didConsume);
604 assertEquals(selStart - 1, Selection.getSelectionStart(editable));
605 assertEquals(selStart - 1, Selection.getSelectionEnd(editable));
614 KeyEvent downKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
619 assertTrue(didConsume);
620 assertEquals(Selection.getSelectionStart(editable), 74);
624 assertTrue(didConsume);
625 assertEquals(Selection.getSelectionStart(editable), 73);
627 assertTrue(didConsume);
628 assertEquals(Selection.getSelectionStart(editable), 72);
632 assertTrue(didConsume);
633 assertEquals(Selection.getSelectionStart(editable), 69);
638 assertTrue(didConsume);
639 assertEquals(Selection.getSelectionStart(editable), 66);
642 assertTrue(didConsume);
643 assertEquals(Selection.getSelectionStart(editable), 66);
647 assertTrue(didConsume);
648 assertEquals(Selection.getSelectionStart(editable), 55);
652 assertTrue(didConsume);
653 assertEquals(Selection.getSelectionStart(editable), 53);
655 assertTrue(didConsume);
656 assertEquals(Selection.getSelectionStart(editable), 52);
658 assertTrue(didConsume);
659 assertEquals(Selection.getSelectionStart(editable), 51);
665 assertTrue(didConsume);
666 assertEquals(Selection.getSelectionStart(editable), 37);
670 for (
int i = 0; i < 6; i++) {
672 assertTrue(didConsume);
674 assertEquals(Selection.getSelectionStart(editable), 37);
679 assertTrue(didConsume);
680 assertEquals(Selection.getSelectionStart(editable), 23);
685 assertTrue(didConsume);
686 assertEquals(Selection.getSelectionStart(editable), 21);
689 assertTrue(didConsume);
690 assertEquals(Selection.getSelectionStart(editable), 21);
694 assertTrue(didConsume);
695 assertEquals(Selection.getSelectionStart(editable), 19);
699 assertTrue(didConsume);
700 assertEquals(Selection.getSelectionStart(editable), 16);
705 assertTrue(didConsume);
706 assertEquals(Selection.getSelectionStart(editable), 13);
709 assertTrue(didConsume);
710 assertEquals(Selection.getSelectionStart(editable), 13);
715 assertTrue(didConsume);
716 assertEquals(Selection.getSelectionStart(editable), 11);
721 assertTrue(didConsume);
722 assertEquals(Selection.getSelectionStart(editable), 11);
726 assertTrue(didConsume);
727 assertEquals(Selection.getSelectionStart(editable), 9);
731 assertTrue(didConsume);
732 assertEquals(Selection.getSelectionStart(editable), 7);
736 assertTrue(didConsume);
737 assertEquals(Selection.getSelectionStart(editable), 3);
741 assertTrue(didConsume);
742 assertEquals(Selection.getSelectionStart(editable), 1);
746 assertTrue(didConsume);
747 assertEquals(Selection.getSelectionStart(editable), 0);
757 KeyEvent leftKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
760 assertTrue(didConsume);
761 assertEquals(selStart, Selection.getSelectionStart(editable));
762 assertEquals(selEnd - 1, Selection.getSelectionEnd(editable));
771 KeyEvent shiftLeftKeyDown =
773 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_SHIFT_ON);
776 assertTrue(didConsume);
777 assertEquals(selStart, Selection.getSelectionStart(editable));
778 assertEquals(selStart - 1, Selection.getSelectionEnd(editable));
787 KeyEvent rightKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
790 assertTrue(didConsume);
791 assertEquals(selStart + 1, Selection.getSelectionStart(editable));
792 assertEquals(selStart + 1, Selection.getSelectionEnd(editable));
801 String SAMPLE_REGION_TEXT =
"🇷🇷🇷🇷🇷🇷🇷";
805 KeyEvent downKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
810 assertTrue(didConsume);
811 assertEquals(Selection.getSelectionStart(editable), 4);
813 assertTrue(didConsume);
814 assertEquals(Selection.getSelectionStart(editable), 8);
816 assertTrue(didConsume);
817 assertEquals(Selection.getSelectionStart(editable), 12);
822 assertTrue(didConsume);
823 assertEquals(Selection.getSelectionStart(editable), 14);
829 assertTrue(didConsume);
830 assertEquals(Selection.getSelectionStart(editable), 8);
839 KeyEvent downKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
844 assertTrue(didConsume);
845 assertEquals(Selection.getSelectionStart(editable), 1);
849 assertTrue(didConsume);
850 assertEquals(Selection.getSelectionStart(editable), 3);
854 assertTrue(didConsume);
855 assertEquals(Selection.getSelectionStart(editable), 7);
859 assertTrue(didConsume);
860 assertEquals(Selection.getSelectionStart(editable), 9);
864 assertTrue(didConsume);
865 assertEquals(Selection.getSelectionStart(editable), 10);
869 assertTrue(didConsume);
870 assertEquals(Selection.getSelectionStart(editable), 12);
874 assertTrue(didConsume);
875 assertEquals(Selection.getSelectionStart(editable), 13);
879 assertTrue(didConsume);
880 assertEquals(Selection.getSelectionStart(editable), 16);
885 assertTrue(didConsume);
886 assertEquals(Selection.getSelectionStart(editable), 16);
891 assertTrue(didConsume);
892 assertEquals(Selection.getSelectionStart(editable), 19);
896 assertTrue(didConsume);
897 assertEquals(Selection.getSelectionStart(editable), 21);
902 assertTrue(didConsume);
903 assertEquals(Selection.getSelectionStart(editable), 23);
906 for (
int i = 0; i < 7; i++) {
908 assertTrue(didConsume);
909 assertEquals(Selection.getSelectionStart(editable), 25 + 2 * i);
911 assertEquals(Selection.getSelectionStart(editable), 37);
916 for (
int i = 0; i < 6; i++) {
918 assertTrue(didConsume);
919 assertEquals(Selection.getSelectionStart(editable), 41 + 2 * i);
921 assertEquals(Selection.getSelectionStart(editable), 51);
926 assertTrue(didConsume);
927 assertEquals(Selection.getSelectionStart(editable), 52);
929 assertTrue(didConsume);
930 assertEquals(Selection.getSelectionStart(editable), 53);
932 assertTrue(didConsume);
933 assertEquals(Selection.getSelectionStart(editable), 55);
937 assertTrue(didConsume);
938 assertEquals(Selection.getSelectionStart(editable), 66);
943 assertTrue(didConsume);
944 assertEquals(Selection.getSelectionStart(editable), 68);
946 assertTrue(didConsume);
947 assertEquals(Selection.getSelectionStart(editable), 69);
951 assertTrue(didConsume);
952 assertEquals(Selection.getSelectionStart(editable), 72);
956 assertTrue(didConsume);
957 assertEquals(Selection.getSelectionStart(editable), 73);
959 assertTrue(didConsume);
960 assertEquals(Selection.getSelectionStart(editable), 74);
964 assertTrue(didConsume);
965 assertEquals(Selection.getSelectionStart(editable), 75);
975 KeyEvent rightKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
978 assertTrue(didConsume);
979 assertEquals(selStart, Selection.getSelectionStart(editable));
980 assertEquals(selEnd + 1, Selection.getSelectionEnd(editable));
989 KeyEvent shiftRightKeyDown =
991 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, 0, KeyEvent.META_SHIFT_ON);
994 assertTrue(didConsume);
995 assertEquals(selStart, Selection.getSelectionStart(editable));
996 assertEquals(selStart + 1, Selection.getSelectionEnd(editable));
1001 int selStart = SAMPLE_TEXT.indexOf(
'\n') + 4;
1005 KeyEvent upKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
1008 assertTrue(didConsume);
1011 assertTrue(Selection.getSelectionStart(editable) < selStart);
1020 KeyEvent downKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
1023 assertTrue(didConsume);
1026 assertTrue(Selection.getSelectionStart(editable) > selStart);
1035 KeyEvent keyEvent =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
1037 assertFalse(didConsume);
1038 assertEquals(Selection.getSelectionStart(editable), -1);
1039 assertEquals(Selection.getSelectionEnd(editable), -1);
1041 keyEvent =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
1043 assertFalse(didConsume);
1044 assertEquals(Selection.getSelectionStart(editable), -1);
1045 assertEquals(Selection.getSelectionEnd(editable), -1);
1047 keyEvent =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
1049 assertFalse(didConsume);
1050 assertEquals(Selection.getSelectionStart(editable), -1);
1051 assertEquals(Selection.getSelectionEnd(editable), -1);
1053 keyEvent =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
1055 assertFalse(didConsume);
1056 assertEquals(Selection.getSelectionStart(editable), -1);
1057 assertEquals(Selection.getSelectionEnd(editable), -1);
1067 ExtractedText extractedText = adaptor.getExtractedText(
null, 0);
1069 assertEquals(extractedText.text, SAMPLE_TEXT);
1070 assertEquals(extractedText.selectionStart, selStart);
1071 assertEquals(extractedText.selectionEnd, selStart);
1077 View testView =
new View(ctx);
1082 mock(TextInputChannel.class),
1083 mockKeyboardManager,
1086 TestImm testImm = Shadow.extract(ctx.getSystemService(
Context.INPUT_METHOD_SERVICE));
1090 ExtractedTextRequest request =
new ExtractedTextRequest();
1091 request.token = 123;
1093 ExtractedText extractedText = adaptor.getExtractedText(request, 0);
1094 assertEquals(5, extractedText.selectionStart);
1095 assertEquals(5, extractedText.selectionEnd);
1096 assertFalse(extractedText.text instanceof SpannableStringBuilder);
1103 request.flags = InputConnection.GET_TEXT_WITH_STYLES;
1104 extractedText = adaptor.getExtractedText(request, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
1105 assertEquals(2, extractedText.selectionStart);
1106 assertEquals(3, extractedText.selectionEnd);
1107 assertTrue(extractedText.text instanceof SpannableStringBuilder);
1116 extractedText = adaptor.getExtractedText(request, 0);
1117 assertEquals(3, extractedText.selectionStart);
1118 assertEquals(5, extractedText.selectionEnd);
1119 assertTrue(extractedText.text instanceof SpannableStringBuilder);
1128 View testView =
new View(ctx);
1133 mock(TextInputChannel.class),
1134 mockKeyboardManager,
1137 TestImm testImm = Shadow.extract(ctx.getSystemService(
Context.INPUT_METHOD_SERVICE));
1148 assertEquals(0, cursorAnchorInfo.getSelectionStart());
1149 assertEquals(1, cursorAnchorInfo.getSelectionEnd());
1156 assertEquals(0, cursorAnchorInfo.getSelectionStart());
1157 assertEquals(1, cursorAnchorInfo.getSelectionEnd());
1170 KeyEvent shiftKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT);
1173 assertFalse(didConsume);
1174 verify(mockKeyboardManager, never()).handleEvent(shiftKeyDown);
1180 when(mockKeyboardManager.handleEvent(any())).thenReturn(
true);
1183 KeyEvent shiftKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT);
1186 boolean didConsume = adaptor.
sendKeyEvent(shiftKeyDown);
1187 assertTrue(didConsume);
1188 verify(mockKeyboardManager,
times(1)).handleEvent(shiftKeyDown);
1196 KeyEvent downKeyDown =
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
1198 for (
int i = 0; i < 4; i++) {
1200 assertFalse(didConsume);
1202 assertEquals(5, Selection.getSelectionStart(editable));
1210 FakeKeyEvent keyEvent =
new FakeKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK,
'\b');
1213 assertFalse(didConsume);
1220 for (
int i = 0; i < 5; i++) {
1224 verify(adaptor,
times(1)).endBatchEdit();
1226 verify(adaptor,
times(4)).endBatchEdit();
1229 private static final String SAMPLE_TEXT =
1230 "Lorem ipsum dolor sit amet," +
"\nconsectetur adipiscing elit.";
1232 private static final String SAMPLE_EMOJI_TEXT =
1252 private static final String SAMPLE_RTL_TEXT =
"متن ساختگی" +
"\nبرای تستfor test😊";
1257 sample.
replace(0, 0, SAMPLE_TEXT);
1258 Selection.setSelection(sample, selStart, selEnd);
1262 private static ListenableEditingState sampleEditable(
int selStart,
int selEnd, String text) {
1263 ListenableEditingState sample =
1264 new ListenableEditingState(
null,
new View(ApplicationProvider.getApplicationContext()));
1265 sample.replace(0, 0, text);
1266 Selection.setSelection(sample, selStart, selEnd);
1270 private static InputConnectionAdaptor sampleInputConnectionAdaptor(
1271 ListenableEditingState editable) {
1272 return sampleInputConnectionAdaptor(editable, mock(KeyboardManager.class));
1275 private static InputConnectionAdaptor sampleInputConnectionAdaptor(
1276 ListenableEditingState editable, KeyboardManager mockKeyboardManager) {
1277 View testView =
new View(ApplicationProvider.getApplicationContext());
1279 TextInputChannel textInputChannel = mock(TextInputChannel.class);
1280 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
1281 when(mockFlutterJNI.isCodePointEmoji(anyInt()))
1282 .thenAnswer((invocation) -> Emoji.isEmoji((
int) invocation.getArguments()[0]));
1283 when(mockFlutterJNI.isCodePointEmojiModifier(anyInt()))
1284 .thenAnswer((invocation) -> Emoji.isEmojiModifier((
int) invocation.getArguments()[0]));
1285 when(mockFlutterJNI.isCodePointEmojiModifierBase(anyInt()))
1286 .thenAnswer((invocation) -> Emoji.isEmojiModifierBase((
int) invocation.getArguments()[0]));
1287 when(mockFlutterJNI.isCodePointVariantSelector(anyInt()))
1288 .thenAnswer((invocation) -> Emoji.isVariationSelector((
int) invocation.getArguments()[0]));
1289 when(mockFlutterJNI.isCodePointRegionalIndicator(anyInt()))
1291 (invocation) -> Emoji.isRegionalIndicatorSymbol((
int) invocation.getArguments()[0]));
1292 return new InputConnectionAdaptor(
1293 testView, client, textInputChannel, mockKeyboardManager, editable,
null, mockFlutterJNI);
1296 private static class Emoji {
1297 public static boolean isEmoji(
int codePoint) {
1298 return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI);
1301 public static boolean isEmojiModifier(
int codePoint) {
1302 return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER);
1305 public static boolean isEmojiModifierBase(
int codePoint) {
1306 return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER_BASE);
1309 public static boolean isRegionalIndicatorSymbol(
int codePoint) {
1310 return UCharacter.hasBinaryProperty(codePoint, UProperty.REGIONAL_INDICATOR);
1313 public static boolean isVariationSelector(
int codePoint) {
1314 return UCharacter.hasBinaryProperty(codePoint, UProperty.VARIATION_SELECTOR);
1318 private class TestTextInputChannel
extends TextInputChannel {
1319 public TestTextInputChannel(DartExecutor dartExecutor) {
1320 super(dartExecutor);
1323 public int inputClientId;
1325 public int selectionStart;
1326 public int selectionEnd;
1327 public int composingStart;
1328 public int composingEnd;
1329 public int updateEditingStateInvocations = 0;
1332 public void updateEditingState(
1339 this.inputClientId = inputClientId;
1341 this.selectionStart = selectionStart;
1342 this.selectionEnd = selectionEnd;
1343 this.composingStart = composingStart;
1344 this.composingEnd = composingEnd;
1345 updateEditingStateInvocations++;
1349 @Implements(InputMethodManager.class)
1350 public static class
TestImm extends ShadowInputMethodManager {
1365 lastCursorAnchorInfo = cursorAnchorInfo;
1370 lastExtractedTextToken = token;
1371 lastExtractedText =
text;
1376 View view,
int selStart,
int selEnd,
int candidatesStart,
int candidatesEnd) {
1377 lastSelectionStart = selStart;
1378 lastSelectionEnd = selEnd;
1379 lastCandidatesStart = candidatesStart;
1380 lastCandidatesEnd = candidatesEnd;
1384 lastExtractedText =
null;
1385 lastExtractedTextToken =
empty;
1387 lastSelectionStart =
empty;
1388 lastSelectionEnd =
empty;
1389 lastCandidatesStart =
empty;
1390 lastCandidatesEnd =
empty;
1392 lastCursorAnchorInfo =
null;
static SkISize times(const SkISize &size, float factor)
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart, int tbend)
final int getSelectionEnd()
EMSCRIPTEN_KEEPALIVE void empty()
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]