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) {
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);
106 int inputTargetId = 0;
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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));
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)
MethodCall decodeMethodCall(@NonNull ByteBuffer message)
static final JSONMethodCodec INSTANCE
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
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
SIT bool any(const Vec< 1, T > &x)