2828import io .flutter .plugin .common .MethodCall ;
2929import io .flutter .plugin .platform .PlatformViewsController ;
3030import java .nio .ByteBuffer ;
31+ import java .util .ArrayList ;
32+ import java .util .Arrays ;
33+ import java .util .List ;
3134import org .json .JSONArray ;
3235import org .json .JSONException ;
3336import org .junit .Test ;
@@ -305,9 +308,17 @@ public void inputConnection_createsActionFromEnter() throws JSONException {
305308
306309 @ Test
307310 public void inputConnection_finishComposingTextUpdatesIMM () throws JSONException {
311+ ShadowBuild .setManufacturer ("samsung" );
312+ InputMethodSubtype inputMethodSubtype =
313+ new InputMethodSubtype (0 , 0 , /*locale=*/ "en" , "" , "" , false , false );
314+ Settings .Secure .putString (
315+ RuntimeEnvironment .application .getContentResolver (),
316+ Settings .Secure .DEFAULT_INPUT_METHOD ,
317+ "com.sec.android.inputmethod/.SamsungKeypad" );
308318 TestImm testImm =
309319 Shadow .extract (
310320 RuntimeEnvironment .application .getSystemService (Context .INPUT_METHOD_SERVICE ));
321+ testImm .setCurrentInputMethodSubtype (inputMethodSubtype );
311322 FlutterJNI mockFlutterJni = mock (FlutterJNI .class );
312323 View testView = new View (RuntimeEnvironment .application );
313324 DartExecutor dartExecutor = spy (new DartExecutor (mockFlutterJni , mock (AssetManager .class )));
@@ -338,13 +349,59 @@ public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException
338349 }
339350 }
340351
352+ @ Test
353+ public void inputConnection_samsungFinishComposingTextSetsSelection () throws JSONException {
354+ ShadowBuild .setManufacturer ("samsung" );
355+ InputMethodSubtype inputMethodSubtype =
356+ new InputMethodSubtype (0 , 0 , /*locale=*/ "en" , "" , "" , false , false );
357+ Settings .Secure .putString (
358+ RuntimeEnvironment .application .getContentResolver (),
359+ Settings .Secure .DEFAULT_INPUT_METHOD ,
360+ "com.sec.android.inputmethod/.SamsungKeypad" );
361+ TestImm testImm =
362+ Shadow .extract (
363+ RuntimeEnvironment .application .getSystemService (Context .INPUT_METHOD_SERVICE ));
364+ testImm .setCurrentInputMethodSubtype (inputMethodSubtype );
365+ FlutterJNI mockFlutterJni = mock (FlutterJNI .class );
366+ View testView = new View (RuntimeEnvironment .application );
367+ DartExecutor dartExecutor = spy (new DartExecutor (mockFlutterJni , mock (AssetManager .class )));
368+ TextInputPlugin textInputPlugin =
369+ new TextInputPlugin (testView , dartExecutor , mock (PlatformViewsController .class ));
370+ textInputPlugin .setTextInputClient (
371+ 0 ,
372+ new TextInputChannel .Configuration (
373+ false ,
374+ false ,
375+ true ,
376+ TextInputChannel .TextCapitalization .NONE ,
377+ new TextInputChannel .InputType (TextInputChannel .TextInputType .TEXT , false , false ),
378+ null ,
379+ null ));
380+ // There's a pending restart since we initialized the text input client. Flush that now.
381+ textInputPlugin .setTextInputEditingState (
382+ testView , new TextInputChannel .TextEditState ("" , 0 , 0 ));
383+ InputConnection connection = textInputPlugin .createInputConnection (testView , new EditorInfo ());
384+
385+ testImm .setTrackSelection (true );
386+ connection .finishComposingText ();
387+ testImm .setTrackSelection (false );
388+
389+ List <Integer > expectedSelectionValues =
390+ Arrays .asList (0 , 0 , -1 , -1 , -1 , -1 , -1 , -1 , 0 , 0 , -1 , -1 );
391+ assertEquals (testImm .getSelectionUpdateValues (), expectedSelectionValues );
392+ }
393+
341394 @ Implements (InputMethodManager .class )
342395 public static class TestImm extends ShadowInputMethodManager {
343396 private InputMethodSubtype currentInputMethodSubtype ;
344397 private SparseIntArray restartCounter = new SparseIntArray ();
345398 private CursorAnchorInfo cursorAnchorInfo ;
399+ private ArrayList <Integer > selectionUpdateValues ;
400+ private boolean trackSelection = false ;
346401
347- public TestImm () {}
402+ public TestImm () {
403+ selectionUpdateValues = new ArrayList <Integer >();
404+ }
348405
349406 @ Implementation
350407 public InputMethodSubtype getCurrentInputMethodSubtype () {
@@ -370,6 +427,28 @@ public void updateCursorAnchorInfo(View view, CursorAnchorInfo cursorAnchorInfo)
370427 this .cursorAnchorInfo = cursorAnchorInfo ;
371428 }
372429
430+ // We simply store the values to verify later.
431+ @ Implementation
432+ public void updateSelection (
433+ View view , int selStart , int selEnd , int candidatesStart , int candidatesEnd ) {
434+ if (trackSelection ) {
435+ this .selectionUpdateValues .add (selStart );
436+ this .selectionUpdateValues .add (selEnd );
437+ this .selectionUpdateValues .add (candidatesStart );
438+ this .selectionUpdateValues .add (candidatesEnd );
439+ }
440+ }
441+
442+ // only track values when enabled via this.
443+ public void setTrackSelection (boolean val ) {
444+ trackSelection = val ;
445+ }
446+
447+ // Returns true if the last updateSelection call passed the following values.
448+ public ArrayList <Integer > getSelectionUpdateValues () {
449+ return selectionUpdateValues ;
450+ }
451+
373452 public CursorAnchorInfo getLastCursorAnchorInfo () {
374453 return cursorAnchorInfo ;
375454 }
0 commit comments