diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 0564b4f0cdab3..3a3b454beec82 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -435,10 +435,12 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java", "test/io/flutter/external/FlutterLaunchTests.java", "test/io/flutter/plugin/common/StandardMessageCodecTest.java", + "test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java", "test/io/flutter/plugin/editing/TextInputPluginTest.java", "test/io/flutter/plugin/platform/PlatformPluginTest.java", "test/io/flutter/plugin/platform/SingleViewPresentationTest.java", "test/io/flutter/plugins/GeneratedPluginRegistrant.java", + "test/io/flutter/util/FakeKeyEvent.java", "test/io/flutter/util/PreconditionsTest.java", ] diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 20b11c68024f4..b751e548118c2 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -10,6 +10,7 @@ import android.provider.Settings; import android.text.DynamicLayout; import android.text.Editable; +import android.text.InputType; import android.text.Layout; import android.text.Selection; import android.text.TextPaint; @@ -274,8 +275,11 @@ public boolean sendKeyEvent(KeyEvent event) { int newSel = Math.min(selStart + 1, mEditable.length()); setSelection(newSel, newSel); return true; - } else if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER - || event.getKeyCode() == KeyEvent.KEYCODE_NUMPAD_ENTER) { + // When the enter key is pressed on a non-multiline field, consider it a + // submit instead of a newline. + } else if ((event.getKeyCode() == KeyEvent.KEYCODE_ENTER + || event.getKeyCode() == KeyEvent.KEYCODE_NUMPAD_ENTER) + && (InputType.TYPE_TEXT_FLAG_MULTI_LINE & mEditorInfo.inputType) == 0) { performEditorAction(mEditorInfo.imeOptions & EditorInfo.IME_MASK_ACTION); return true; } else { diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index b72cf077ca865..480998ac362d4 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -17,6 +17,7 @@ import io.flutter.embedding.engine.systemchannels.PlatformChannelTest; import io.flutter.external.FlutterLaunchTests; import io.flutter.plugin.common.StandardMessageCodecTest; +import io.flutter.plugin.editing.InputConnectionAdaptorTest; import io.flutter.plugin.editing.TextInputPluginTest; import io.flutter.plugin.platform.PlatformPluginTest; import io.flutter.plugin.platform.SingleViewPresentationTest; @@ -44,6 +45,7 @@ FlutterShellArgsTest.class, FlutterRendererTest.class, FlutterViewTest.class, + InputConnectionAdaptorTest.class, PlatformChannelTest.class, PlatformPluginTest.class, PluginComponentTest.class, diff --git a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java new file mode 100644 index 0000000000000..b366efb110191 --- /dev/null +++ b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java @@ -0,0 +1,50 @@ +package io.flutter.plugin.editing; + +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.res.AssetManager; +import android.text.Editable; +import android.text.InputType; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.systemchannels.TextInputChannel; +import io.flutter.util.FakeKeyEvent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@Config(manifest = Config.NONE, sdk = 27) +@RunWith(RobolectricTestRunner.class) +public class InputConnectionAdaptorTest { + @Test + public void inputConnectionAdaptor_ReceivesEnter() throws NullPointerException { + View testView = new View(RuntimeEnvironment.application); + FlutterJNI mockFlutterJni = mock(FlutterJNI.class); + DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class))); + int inputTargetId = 0; + TextInputChannel textInputChannel = new TextInputChannel(dartExecutor); + Editable mEditable = Editable.Factory.getInstance().newEditable(""); + Editable spyEditable = spy(mEditable); + EditorInfo outAttrs = new EditorInfo(); + outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE; + + InputConnectionAdaptor inputConnectionAdaptor = + new InputConnectionAdaptor( + testView, inputTargetId, textInputChannel, spyEditable, outAttrs); + + // Send an enter key and make sure the Editable received it. + FakeKeyEvent keyEvent = new FakeKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER); + inputConnectionAdaptor.sendKeyEvent(keyEvent); + verify(spyEditable, times(1)).insert(eq(0), anyString()); + } +} diff --git a/shell/platform/android/test/io/flutter/util/FakeKeyEvent.java b/shell/platform/android/test/io/flutter/util/FakeKeyEvent.java new file mode 100644 index 0000000000000..2c9b9ceb1c3b8 --- /dev/null +++ b/shell/platform/android/test/io/flutter/util/FakeKeyEvent.java @@ -0,0 +1,15 @@ +package io.flutter.util; + +import android.view.KeyEvent; + +// In the test environment, keyEvent.getUnicodeChar throws an exception. This +// class works around the exception by hardcoding the returned value. +public class FakeKeyEvent extends KeyEvent { + public FakeKeyEvent(int action, int keyCode) { + super(action, keyCode); + } + + public final int getUnicodeChar() { + return 1; + } +}