mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
203 lines
7.6 KiB
Java
203 lines
7.6 KiB
Java
// Copyright 2013 The Flutter 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.content.Context;
|
|
import android.support.annotation.NonNull;
|
|
import android.text.Editable;
|
|
import android.text.InputType;
|
|
import android.text.Selection;
|
|
import android.view.inputmethod.BaseInputConnection;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputConnection;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
|
|
import io.flutter.embedding.engine.dart.DartExecutor;
|
|
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
|
|
import io.flutter.view.FlutterView;
|
|
|
|
/**
|
|
* Android implementation of the text input plugin.
|
|
*/
|
|
public class TextInputPlugin {
|
|
private final FlutterView mView;
|
|
private final InputMethodManager mImm;
|
|
private final TextInputChannel textInputChannel;
|
|
private int mClient = 0;
|
|
private TextInputChannel.Configuration configuration;
|
|
private Editable mEditable;
|
|
private boolean mRestartInputPending;
|
|
|
|
public TextInputPlugin(FlutterView view, @NonNull DartExecutor dartExecutor) {
|
|
mView = view;
|
|
mImm = (InputMethodManager) view.getContext().getSystemService(
|
|
Context.INPUT_METHOD_SERVICE);
|
|
|
|
textInputChannel = new TextInputChannel(dartExecutor);
|
|
textInputChannel.setTextInputMethodHandler(new TextInputChannel.TextInputMethodHandler() {
|
|
@Override
|
|
public void show() {
|
|
showTextInput(mView);
|
|
}
|
|
|
|
@Override
|
|
public void hide() {
|
|
hideTextInput(mView);
|
|
}
|
|
|
|
@Override
|
|
public void setClient(int textInputClientId, TextInputChannel.Configuration configuration) {
|
|
setTextInputClient(textInputClientId, configuration);
|
|
}
|
|
|
|
@Override
|
|
public void setEditingState(TextInputChannel.TextEditState editingState) {
|
|
setTextInputEditingState(mView, editingState);
|
|
}
|
|
|
|
@Override
|
|
public void clearClient() {
|
|
clearTextInputClient();
|
|
}
|
|
});
|
|
}
|
|
|
|
private static int inputTypeFromTextInputType(
|
|
TextInputChannel.InputType type,
|
|
boolean obscureText,
|
|
boolean autocorrect,
|
|
TextInputChannel.TextCapitalization textCapitalization
|
|
) {
|
|
if (type.type == TextInputChannel.TextInputType.DATETIME) {
|
|
return InputType.TYPE_CLASS_DATETIME;
|
|
} else if (type.type == TextInputChannel.TextInputType.NUMBER) {
|
|
int textType = InputType.TYPE_CLASS_NUMBER;
|
|
if (type.isSigned) {
|
|
textType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
|
|
}
|
|
if (type.isDecimal) {
|
|
textType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
|
}
|
|
return textType;
|
|
} else if (type.type == TextInputChannel.TextInputType.PHONE) {
|
|
return InputType.TYPE_CLASS_PHONE;
|
|
}
|
|
|
|
int textType = InputType.TYPE_CLASS_TEXT;
|
|
if (type.type == TextInputChannel.TextInputType.MULTILINE) {
|
|
textType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
|
|
} else if (type.type == TextInputChannel.TextInputType.EMAIL_ADDRESS) {
|
|
textType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
|
|
} else if (type.type == TextInputChannel.TextInputType.URL) {
|
|
textType |= InputType.TYPE_TEXT_VARIATION_URI;
|
|
}
|
|
|
|
if (obscureText) {
|
|
// Note: both required. Some devices ignore TYPE_TEXT_FLAG_NO_SUGGESTIONS.
|
|
textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
|
textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
|
} else {
|
|
if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
|
|
}
|
|
|
|
if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) {
|
|
textType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
|
|
} else if (textCapitalization == TextInputChannel.TextCapitalization.WORDS) {
|
|
textType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
|
|
} else if (textCapitalization == TextInputChannel.TextCapitalization.SENTENCES) {
|
|
textType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
|
|
}
|
|
|
|
return textType;
|
|
}
|
|
|
|
public InputConnection createInputConnection(FlutterView view, EditorInfo outAttrs) {
|
|
if (mClient == 0) return null;
|
|
|
|
outAttrs.inputType = inputTypeFromTextInputType(
|
|
configuration.inputType,
|
|
configuration.obscureText,
|
|
configuration.autocorrect,
|
|
configuration.textCapitalization
|
|
);
|
|
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
|
int enterAction;
|
|
if (configuration.inputAction == null) {
|
|
// 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 = configuration.inputAction;
|
|
}
|
|
if (configuration.actionLabel != null) {
|
|
outAttrs.actionLabel = configuration.actionLabel;
|
|
outAttrs.actionId = enterAction;
|
|
}
|
|
outAttrs.imeOptions |= enterAction;
|
|
|
|
InputConnectionAdaptor connection = new InputConnectionAdaptor(
|
|
view,
|
|
mClient,
|
|
textInputChannel,
|
|
mEditable
|
|
);
|
|
outAttrs.initialSelStart = Selection.getSelectionStart(mEditable);
|
|
outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable);
|
|
|
|
return connection;
|
|
}
|
|
|
|
private void showTextInput(FlutterView view) {
|
|
view.requestFocus();
|
|
mImm.showSoftInput(view, 0);
|
|
}
|
|
|
|
private void hideTextInput(FlutterView view) {
|
|
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
|
|
}
|
|
|
|
private void setTextInputClient(int client, TextInputChannel.Configuration configuration) {
|
|
mClient = client;
|
|
this.configuration = configuration;
|
|
mEditable = Editable.Factory.getInstance().newEditable("");
|
|
|
|
// setTextInputClient will be followed by a call to setTextInputEditingState.
|
|
// Do a restartInput at that time.
|
|
mRestartInputPending = true;
|
|
}
|
|
|
|
private void applyStateToSelection(TextInputChannel.TextEditState state) {
|
|
int selStart = state.selectionStart;
|
|
int selEnd = state.selectionEnd;
|
|
if (selStart >= 0 && selStart <= mEditable.length() && selEnd >= 0
|
|
&& selEnd <= mEditable.length()) {
|
|
Selection.setSelection(mEditable, selStart, selEnd);
|
|
} else {
|
|
Selection.removeSelection(mEditable);
|
|
}
|
|
}
|
|
|
|
private void setTextInputEditingState(FlutterView view, TextInputChannel.TextEditState state) {
|
|
if (!mRestartInputPending && state.text.equals(mEditable.toString())) {
|
|
applyStateToSelection(state);
|
|
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.text);
|
|
applyStateToSelection(state);
|
|
mImm.restartInput(view);
|
|
mRestartInputPending = false;
|
|
}
|
|
}
|
|
|
|
private void clearTextInputClient() {
|
|
mClient = 0;
|
|
}
|
|
}
|