From faabc10178ce9b241f532c4f45d50b2dc4de2cae Mon Sep 17 00:00:00 2001 From: gspencergoog Date: Tue, 26 Sep 2017 12:53:19 -0700 Subject: [PATCH] Support correct keyboards for multi-line text editing. (#4115) This addresses part of #8028, implementing the Engine-side support for it. --- .../editing/InputConnectionAdaptor.java | 16 ++++++++-- .../plugin/editing/TextInputPlugin.java | 29 ++++++++++++++++--- .../Source/FlutterTextInputDelegate.h | 1 + .../Source/FlutterTextInputPlugin.mm | 11 +++++++ .../framework/Source/FlutterViewController.mm | 3 ++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 2e099e9772e..84d7e5f9b53 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -8,6 +8,7 @@ import android.content.Context; import android.text.Editable; import android.text.Selection; import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.view.KeyEvent; @@ -92,7 +93,7 @@ class InputConnectionAdaptor extends BaseInputConnection { public boolean deleteSurroundingText(int beforeLength, int afterLength) { if (Selection.getSelectionStart(mEditable) == -1 || Selection.getSelectionStart(mEditable) == -1) - return true; + return true;9c518cb751e34b0 boolean result = super.deleteSurroundingText(beforeLength, afterLength); updateEditingState(); @@ -162,8 +163,17 @@ class InputConnectionAdaptor extends BaseInputConnection { @Override public boolean performEditorAction(int actionCode) { // TODO(abarth): Support more actions. - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.done")); + switch (actionCode) { + case EditorInfo.IME_ACTION_NONE: + mFlutterChannel.invokeMethod("TextInputClient.performAction", + Arrays.asList(mClient, "TextInputAction.newline")); + break; + default: + case EditorInfo.IME_ACTION_DONE: + mFlutterChannel.invokeMethod("TextInputClient.performAction", + Arrays.asList(mClient, "TextInputAction.done")); + break; + } return true; } } diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 3eba406ec7d..f4c2e7279a4 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -88,7 +88,9 @@ public class TextInputPlugin implements MethodCallHandler { return InputType.TYPE_CLASS_PHONE; int textType = InputType.TYPE_CLASS_TEXT; - if (inputType.equals("TextInputType.emailAddress")) + if (inputType.equals("TextInputType.multiline")) + textType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE; + else if (inputType.equals("TextInputType.emailAddress")) textType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; else if (inputType.equals("TextInputType.url")) textType |= InputType.TYPE_TEXT_VARIATION_URI; @@ -102,6 +104,12 @@ public class TextInputPlugin implements MethodCallHandler { return textType; } + private static int inputActionFromTextInputAction(String inputAction) { + if (inputAction.equals("TextInputAction.newline")) + return EditorInfo.IME_ACTION_NONE; + return EditorInfo.IME_ACTION_DONE; + } + public InputConnection createInputConnection(FlutterView view, EditorInfo outAttrs) throws JSONException { if (mClient == 0) @@ -111,9 +119,22 @@ public class TextInputPlugin implements MethodCallHandler { mConfiguration.getString("inputType"), mConfiguration.optBoolean("obscureText"), mConfiguration.optBoolean("autocorrect", true)); - if (!mConfiguration.isNull("actionLabel")) - outAttrs.actionLabel = mConfiguration.getString("actionLabel"); - outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; + int enterAction; + if (mConfiguration.isNull("inputAction")) { + // If an explicit input action isn't set, then default to none for multi-line fields + // and done for single line fields. + enterAction = (InputType.TYPE_TEXT_FLAG_MULTI_LINE & outAttrs.inputType) != 0 + ? EditorInfo.IME_ACTION_NONE + : EditorInfo.IME_ACTION_DONE; + } else { + enterAction = inputActionFromTextInputAction(mConfiguration.getString("inputAction")); + } + if (!mConfiguration.isNull("actionLabel")) { + outAttrs.actionLabel = mConfiguration.getString("actionLabel"); + outAttrs.actionId = enterAction; + } + outAttrs.imeOptions |= enterAction; InputConnectionAdaptor connection = new InputConnectionAdaptor(view, mClient, mFlutterChannel, mEditable); outAttrs.initialSelStart = Math.max(Selection.getSelectionStart(mEditable), 0); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h index 2ed33436a22..e82f2c41f42 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h @@ -9,6 +9,7 @@ typedef NS_ENUM(NSInteger, FlutterTextInputAction) { FlutterTextInputActionDone, + FlutterTextInputActionNewline, }; @protocol FlutterTextInputDelegate diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 90a9ad57732..a1640d11551 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -12,6 +12,8 @@ static const char _kTextAffinityUpstream[] = "TextAffinity.upstream"; static UIKeyboardType ToUIKeyboardType(NSString* inputType) { if ([inputType isEqualToString:@"TextInputType.text"]) return UIKeyboardTypeDefault; + if ([inputType isEqualToString:@"TextInputType.multiline"]) + return UIKeyboardTypeDefault; if ([inputType isEqualToString:@"TextInputType.number"]) return UIKeyboardTypeDecimalPad; if ([inputType isEqualToString:@"TextInputType.phone"]) @@ -23,6 +25,12 @@ static UIKeyboardType ToUIKeyboardType(NSString* inputType) { return UIKeyboardTypeDefault; } +static UIReturnKeyType ToUIReturnKeyType(NSString* inputType) { + if ([inputType isEqualToString:@"TextInputType.multiline"]) + return UIReturnKeyDefault; + return UIReturnKeyDone; +} + static UITextAutocapitalizationType ToUITextAutocapitalizationType(NSString* inputType) { if ([inputType isEqualToString:@"TextInputType.text"]) return UITextAutocapitalizationTypeSentences; @@ -272,6 +280,8 @@ static UITextAutocapitalizationType ToUITextAutocapitalizationType(NSString* inp [_textInputDelegate performAction:FlutterTextInputActionDone withClient:_textInputClient]; return NO; } + if (self.returnKeyType == UIReturnKeyDefault && [text isEqualToString:@"\n"]) + [_textInputDelegate performAction:FlutterTextInputActionNewline withClient:_textInputClient]; return YES; } @@ -569,6 +579,7 @@ static UITextAutocapitalizationType ToUITextAutocapitalizationType(NSString* inp - (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configuration { _view.keyboardType = ToUIKeyboardType(configuration[@"inputType"]); + _view.returnKeyType = ToUIReturnKeyType(configuration[@"inputType"]); _view.autocapitalizationType = ToUITextAutocapitalizationType(configuration[@"inputType"]); _view.secureTextEntry = [configuration[@"obscureText"] boolValue]; NSString* autocorrect = configuration[@"autocorrect"]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 3fb1f2ea5a3..05dcabdae57 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -626,6 +626,9 @@ static inline blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* to case FlutterTextInputActionDone: actionString = @"TextInputAction.done"; break; + case FlutterTextInputActionNewline: + actionString = @"TextInputAction.newline"; + break; } [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction" arguments:@[ @(client), actionString ]];