2016-10-19 12:23:52 -07:00
|
|
|
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package io.flutter.plugin.editing;
|
|
|
|
|
|
|
|
|
|
import android.app.Activity;
|
|
|
|
|
import android.content.Context;
|
2017-06-14 10:24:41 -07:00
|
|
|
import android.text.Editable;
|
2016-10-19 12:23:52 -07:00
|
|
|
import android.text.InputType;
|
2017-06-14 10:24:41 -07:00
|
|
|
import android.text.Selection;
|
|
|
|
|
import android.view.inputmethod.BaseInputConnection;
|
2016-10-19 12:23:52 -07:00
|
|
|
import android.view.inputmethod.EditorInfo;
|
|
|
|
|
import android.view.inputmethod.InputConnection;
|
|
|
|
|
import android.view.inputmethod.InputMethodManager;
|
2017-03-17 09:04:59 +01:00
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
import io.flutter.plugin.common.MethodChannel;
|
|
|
|
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
|
|
|
|
import io.flutter.plugin.common.MethodChannel.Result;
|
2017-03-17 09:04:59 +01:00
|
|
|
import io.flutter.plugin.common.JSONMethodCodec;
|
2017-04-06 10:15:06 -07:00
|
|
|
import io.flutter.plugin.common.JSONUtil;
|
2017-03-17 09:04:59 +01:00
|
|
|
import io.flutter.plugin.common.MethodCall;
|
2016-10-19 12:23:52 -07:00
|
|
|
import io.flutter.view.FlutterView;
|
2017-03-17 09:04:59 +01:00
|
|
|
|
|
|
|
|
import java.util.Map;
|
2016-10-19 12:23:52 -07:00
|
|
|
import org.json.JSONArray;
|
|
|
|
|
import org.json.JSONException;
|
|
|
|
|
import org.json.JSONObject;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Android implementation of the text input plugin.
|
|
|
|
|
*/
|
2017-03-17 09:04:59 +01:00
|
|
|
public class TextInputPlugin implements MethodCallHandler {
|
2016-10-19 12:23:52 -07:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
private final FlutterView mView;
|
2017-06-14 10:24:41 -07:00
|
|
|
private final InputMethodManager mImm;
|
2017-04-18 14:30:31 +02:00
|
|
|
private final MethodChannel mFlutterChannel;
|
2016-10-19 12:23:52 -07:00
|
|
|
private int mClient = 0;
|
|
|
|
|
private JSONObject mConfiguration;
|
2017-06-14 10:24:41 -07:00
|
|
|
private Editable mEditable;
|
2017-06-21 10:47:02 -07:00
|
|
|
private boolean mRestartInputPending;
|
2016-10-19 12:23:52 -07:00
|
|
|
|
2017-06-14 10:24:41 -07:00
|
|
|
public TextInputPlugin(FlutterView view) {
|
2017-03-17 09:04:59 +01:00
|
|
|
mView = view;
|
2017-06-14 10:24:41 -07:00
|
|
|
mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
2017-04-18 14:30:31 +02:00
|
|
|
mFlutterChannel = new MethodChannel(view, "flutter/textinput",
|
2017-03-17 09:04:59 +01:00
|
|
|
JSONMethodCodec.INSTANCE);
|
|
|
|
|
mFlutterChannel.setMethodCallHandler(this);
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2017-04-18 14:30:31 +02:00
|
|
|
public void onMethodCall(MethodCall call, Result result) {
|
2017-03-17 09:04:59 +01:00
|
|
|
String method = call.method;
|
|
|
|
|
Object args = call.arguments;
|
|
|
|
|
try {
|
|
|
|
|
if (method.equals("TextInput.show")) {
|
|
|
|
|
showTextInput(mView);
|
2017-04-18 14:30:31 +02:00
|
|
|
result.success(null);
|
2017-03-17 09:04:59 +01:00
|
|
|
} else if (method.equals("TextInput.hide")) {
|
|
|
|
|
hideTextInput(mView);
|
2017-04-18 14:30:31 +02:00
|
|
|
result.success(null);
|
2017-03-17 09:04:59 +01:00
|
|
|
} else if (method.equals("TextInput.setClient")) {
|
|
|
|
|
final JSONArray argumentList = (JSONArray) args;
|
|
|
|
|
setTextInputClient(mView, argumentList.getInt(0), argumentList.getJSONObject(1));
|
2017-04-18 14:30:31 +02:00
|
|
|
result.success(null);
|
2017-03-17 09:04:59 +01:00
|
|
|
} else if (method.equals("TextInput.setEditingState")) {
|
|
|
|
|
setTextInputEditingState(mView, (JSONObject) args);
|
2017-04-18 14:30:31 +02:00
|
|
|
result.success(null);
|
2017-03-17 09:04:59 +01:00
|
|
|
} else if (method.equals("TextInput.clearClient")) {
|
|
|
|
|
clearTextInputClient();
|
2017-04-18 14:30:31 +02:00
|
|
|
result.success(null);
|
2017-03-17 09:04:59 +01:00
|
|
|
} else {
|
2017-04-18 14:30:31 +02:00
|
|
|
result.notImplemented();
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
|
|
|
|
} catch (JSONException e) {
|
2017-04-18 14:30:31 +02:00
|
|
|
result.error("error", "JSON error: " + e.getMessage(), null);
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 14:55:58 -07:00
|
|
|
private static int inputTypeFromTextInputType(String inputType, boolean obscureText) {
|
2016-10-19 12:23:52 -07:00
|
|
|
if (inputType.equals("TextInputType.datetime"))
|
|
|
|
|
return InputType.TYPE_CLASS_DATETIME;
|
2016-10-28 12:26:56 -07:00
|
|
|
if (inputType.equals("TextInputType.number"))
|
2016-10-19 12:23:52 -07:00
|
|
|
return InputType.TYPE_CLASS_NUMBER;
|
2016-10-28 12:26:56 -07:00
|
|
|
if (inputType.equals("TextInputType.phone"))
|
2016-10-19 12:23:52 -07:00
|
|
|
return InputType.TYPE_CLASS_PHONE;
|
2017-06-02 15:04:35 -07:00
|
|
|
|
|
|
|
|
int textType = InputType.TYPE_CLASS_TEXT;
|
|
|
|
|
if (inputType.equals("TextInputType.emailAddress"))
|
|
|
|
|
textType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
|
2017-06-02 15:13:37 -07:00
|
|
|
else if (inputType.equals("TextInputType.url"))
|
|
|
|
|
textType |= InputType.TYPE_TEXT_VARIATION_URI;
|
2017-06-02 15:50:23 -07:00
|
|
|
if (obscureText) {
|
|
|
|
|
// Note: both required. Some devices ignore TYPE_TEXT_FLAG_NO_SUGGESTIONS.
|
2017-06-02 15:04:35 -07:00
|
|
|
textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
2017-06-02 15:50:23 -07:00
|
|
|
textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
|
|
|
|
}
|
2017-06-02 15:04:35 -07:00
|
|
|
return textType;
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
public InputConnection createInputConnection(FlutterView view, EditorInfo outAttrs)
|
|
|
|
|
throws JSONException {
|
2016-10-19 12:23:52 -07:00
|
|
|
if (mClient == 0)
|
|
|
|
|
return null;
|
2017-06-14 10:24:41 -07:00
|
|
|
|
2017-05-09 14:55:58 -07:00
|
|
|
outAttrs.inputType = inputTypeFromTextInputType(mConfiguration.getString("inputType"),
|
|
|
|
|
mConfiguration.optBoolean("obscureText"));
|
2017-05-10 12:20:03 -07:00
|
|
|
if (!mConfiguration.isNull("actionLabel"))
|
|
|
|
|
outAttrs.actionLabel = mConfiguration.getString("actionLabel");
|
2017-03-17 09:04:59 +01:00
|
|
|
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
2017-06-14 10:24:41 -07:00
|
|
|
|
|
|
|
|
InputConnectionAdaptor connection = new InputConnectionAdaptor(view, mClient, mFlutterChannel, mEditable);
|
|
|
|
|
outAttrs.initialSelStart = Math.max(Selection.getSelectionStart(mEditable), 0);
|
|
|
|
|
outAttrs.initialSelEnd = Math.max(Selection.getSelectionEnd(mEditable), 0);
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
return connection;
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void showTextInput(FlutterView view) {
|
2017-06-14 10:24:41 -07:00
|
|
|
mImm.showSoftInput(view, 0);
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void hideTextInput(FlutterView view) {
|
2017-06-14 10:24:41 -07:00
|
|
|
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
private void setTextInputClient(FlutterView view, int client, JSONObject configuration) {
|
2016-10-19 12:23:52 -07:00
|
|
|
mClient = client;
|
|
|
|
|
mConfiguration = configuration;
|
2017-06-14 10:24:41 -07:00
|
|
|
mEditable = Editable.Factory.getInstance().newEditable("");
|
|
|
|
|
|
2017-06-21 10:47:02 -07:00
|
|
|
// setTextInputClient will be followed by a call to setTextInputEditingState.
|
|
|
|
|
// Do a restartInput at that time.
|
|
|
|
|
mRestartInputPending = true;
|
2016-10-19 12:23:52 -07:00
|
|
|
}
|
|
|
|
|
|
2017-06-16 14:01:45 -07:00
|
|
|
private void applyStateToSelection(JSONObject state) throws JSONException {
|
|
|
|
|
int selStart = state.getInt("selectionBase");
|
|
|
|
|
int selEnd = state.getInt("selectionExtent");
|
|
|
|
|
if (selStart != -1 && selEnd != -1) {
|
|
|
|
|
Selection.setSelection(mEditable, selStart, selEnd);
|
|
|
|
|
} else {
|
|
|
|
|
Selection.removeSelection(mEditable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-14 10:24:41 -07:00
|
|
|
private void setTextInputEditingState(FlutterView view, JSONObject state)
|
|
|
|
|
throws JSONException {
|
2017-06-21 10:47:02 -07:00
|
|
|
if (!mRestartInputPending &&
|
|
|
|
|
state.getString("text").equals(mEditable.toString())) {
|
2017-06-16 14:01:45 -07:00
|
|
|
applyStateToSelection(state);
|
2017-06-14 10:24:41 -07:00
|
|
|
mImm.updateSelection(
|
|
|
|
|
mView,
|
|
|
|
|
Math.max(Selection.getSelectionStart(mEditable), 0),
|
|
|
|
|
Math.max(Selection.getSelectionEnd(mEditable), 0),
|
|
|
|
|
BaseInputConnection.getComposingSpanStart(mEditable),
|
|
|
|
|
BaseInputConnection.getComposingSpanEnd(mEditable));
|
|
|
|
|
} else {
|
|
|
|
|
mEditable.replace(0, mEditable.length(), state.getString("text"));
|
2017-06-16 14:01:45 -07:00
|
|
|
applyStateToSelection(state);
|
2017-06-14 10:24:41 -07:00
|
|
|
mImm.restartInput(view);
|
2017-06-21 10:47:02 -07:00
|
|
|
mRestartInputPending = false;
|
2017-06-14 10:24:41 -07:00
|
|
|
}
|
2016-11-30 15:07:37 -08:00
|
|
|
}
|
2017-03-17 09:04:59 +01:00
|
|
|
|
2016-10-19 12:23:52 -07:00
|
|
|
private void clearTextInputClient() {
|
|
|
|
|
mClient = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|