Commit 21ebee21 authored by yangwu.jia's avatar yangwu.jia

修复输入框的bug

parent 9c19498c
package com.idlefish.flutterboost;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
import io.flutter.plugin.editing.TextInputPlugin;
public class XAndroidKeyProcessor {
@NonNull
private final KeyEventChannel keyEventChannel;
@NonNull
private final XTextInputPlugin textInputPlugin;
private int combiningCharacter;
public XAndroidKeyProcessor(@NonNull KeyEventChannel keyEventChannel, @NonNull XTextInputPlugin textInputPlugin) {
this.keyEventChannel = keyEventChannel;
this.textInputPlugin = textInputPlugin;
}
public void onKeyUp(@NonNull KeyEvent keyEvent) {
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
keyEventChannel.keyUp(
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter)
);
}
public void onKeyDown(@NonNull KeyEvent keyEvent) {
if (textInputPlugin.getLastInputConnection() != null
&& textInputPlugin.getInputMethodManager().isAcceptingText()) {
textInputPlugin.getLastInputConnection().sendKeyEvent(keyEvent);
}
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
keyEventChannel.keyDown(
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter)
);
}
/**
* Applies the given Unicode character in {@code newCharacterCodePoint} to a previously
* entered Unicode combining character and returns the combination of these characters
* if a combination exists.
* <p>
* This method mutates {@link #combiningCharacter} over time to combine characters.
* <p>
* One of the following things happens in this method:
* <ul>
* <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is not a combining character, then {@code newCharacterCodePoint} is returned.</li>
* <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is a combining character, then {@code newCharacterCodePoint} is saved as the
* {@link #combiningCharacter} and null is returned.</li>
* <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is also a combining character, then the {@code newCharacterCodePoint} is combined with
* the existing {@link #combiningCharacter} and null is returned.</li>
* <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is not a combining character, then the {@link #combiningCharacter} is applied to the
* regular {@code newCharacterCodePoint} and the resulting complex character is returned. The
* {@link #combiningCharacter} is cleared.</li>
* </ul>
* <p>
* The following reference explains the concept of a "combining character":
* https://en.wikipedia.org/wiki/Combining_character
*/
@Nullable
private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint) {
if (newCharacterCodePoint == 0) {
return null;
}
Character complexCharacter = (char) newCharacterCodePoint;
boolean isNewCodePointACombiningCharacter = (newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0;
if (isNewCodePointACombiningCharacter) {
// If a combining character was entered before, combine this one with that one.
int plainCodePoint = newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT_MASK;
if (combiningCharacter != 0) {
combiningCharacter = KeyCharacterMap.getDeadChar(combiningCharacter, plainCodePoint);
} else {
combiningCharacter = plainCodePoint;
}
} else {
// The new character is a regular character. Apply combiningCharacter to it, if it exists.
if (combiningCharacter != 0) {
int combinedChar = KeyCharacterMap.getDeadChar(combiningCharacter, newCharacterCodePoint);
if (combinedChar > 0) {
complexCharacter = (char) combinedChar;
}
combiningCharacter = 0;
}
}
return complexCharacter;
}
}
\ No newline at end of file
...@@ -90,9 +90,9 @@ public class XFlutterView extends FrameLayout { ...@@ -90,9 +90,9 @@ public class XFlutterView extends FrameLayout {
// These components essentially add some additional behavioral logic on top of // These components essentially add some additional behavioral logic on top of
// existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc. // existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc.
@Nullable @Nullable
private TextInputPlugin textInputPlugin; private XTextInputPlugin textInputPlugin;
@Nullable @Nullable
private AndroidKeyProcessor androidKeyProcessor; private XAndroidKeyProcessor androidKeyProcessor;
@Nullable @Nullable
private AndroidTouchProcessor androidTouchProcessor; private AndroidTouchProcessor androidTouchProcessor;
@Nullable @Nullable
...@@ -585,7 +585,7 @@ public class XFlutterView extends FrameLayout { ...@@ -585,7 +585,7 @@ public class XFlutterView extends FrameLayout {
+ " to new engine."); + " to new engine.");
detachFromFlutterEngine(); detachFromFlutterEngine();
} }
this.requestFocus();
this.flutterEngine = flutterEngine; this.flutterEngine = flutterEngine;
// Instruct our FlutterRenderer that we are now its designated RenderSurface. // Instruct our FlutterRenderer that we are now its designated RenderSurface.
...@@ -599,16 +599,21 @@ public class XFlutterView extends FrameLayout { ...@@ -599,16 +599,21 @@ public class XFlutterView extends FrameLayout {
// Initialize various components that know how to process Android View I/O // Initialize various components that know how to process Android View I/O
// in a way that Flutter understands. // in a way that Flutter understands.
if(this.textInputPlugin!=null){
this.textInputPlugin.destroy();
resolveMemoryLeaks();
if(textInputPlugin==null){
textInputPlugin = new XTextInputPlugin(
this,
flutterEngine.getTextInputChannel(),
this.flutterEngine.getPlatformViewsController()
);
} }
this.textInputPlugin = new TextInputPlugin(this, this.flutterEngine.getDartExecutor(), this.flutterEngine.getPlatformViewsController()); textInputPlugin.setTextInputMethodHandler();
textInputPlugin.getInputMethodManager().restartInput(this);
this.androidKeyProcessor = new AndroidKeyProcessor( this.androidKeyProcessor = new XAndroidKeyProcessor(
this.flutterEngine.getKeyEventChannel(), this.flutterEngine.getKeyEventChannel(),
textInputPlugin textInputPlugin
); );
...@@ -687,9 +692,8 @@ public class XFlutterView extends FrameLayout { ...@@ -687,9 +692,8 @@ public class XFlutterView extends FrameLayout {
// now that the engine is detached. The new InputConnection will be null, which // now that the engine is detached. The new InputConnection will be null, which
// signifies that this View does not process input (until a new engine is attached). // signifies that this View does not process input (until a new engine is attached).
// TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin // TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin
textInputPlugin.getInputMethodManager().restartInput(this);
textInputPlugin.destroy(); // resolveMemoryLeaks();
resolveMemoryLeaks();
// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface. // Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
FlutterRenderer flutterRenderer = flutterEngine.getRenderer(); FlutterRenderer flutterRenderer = flutterEngine.getRenderer();
// didRenderFirstFrame = false; // didRenderFirstFrame = false;
...@@ -697,7 +701,9 @@ public class XFlutterView extends FrameLayout { ...@@ -697,7 +701,9 @@ public class XFlutterView extends FrameLayout {
flutterRenderer.detachFromRenderSurface(); flutterRenderer.detachFromRenderSurface();
flutterEngine = null; flutterEngine = null;
} }
public void release(){
textInputPlugin.release();
}
public void resolveMemoryLeaks(){ public void resolveMemoryLeaks(){
try { try {
......
package com.idlefish.flutterboost;
import android.content.Context;
import android.text.Editable;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.plugin.common.ErrorLogResult;
import io.flutter.plugin.common.MethodChannel;
import android.content.Context;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.Layout;
import android.text.Layout.Directions;
import android.text.Selection;
import android.text.TextPaint;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.Log;
import io.flutter.plugin.common.ErrorLogResult;
import io.flutter.plugin.common.MethodChannel;
class XInputConnectionAdaptor extends BaseInputConnection {
private final View mFlutterView;
private final int mClient;
private final TextInputChannel textInputChannel;
private final Editable mEditable;
private int mBatchCount;
private InputMethodManager mImm;
private final Layout mLayout;
@SuppressWarnings("deprecation")
public XInputConnectionAdaptor(
View view,
int client,
TextInputChannel textInputChannel,
Editable editable
) {
super(view, true);
mFlutterView = view;
mClient = client;
this.textInputChannel = textInputChannel;
mEditable = editable;
mBatchCount = 0;
// We create a dummy Layout with max width so that the selection
// shifting acts as if all text were in one line.
mLayout = new DynamicLayout(mEditable, new TextPaint(), Integer.MAX_VALUE, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
}
// Send the current state of the editable to Flutter.
private void updateEditingState() {
// If the IME is in the middle of a batch edit, then wait until it completes.
if (mBatchCount > 0)
return;
int selectionStart = Selection.getSelectionStart(mEditable);
int selectionEnd = Selection.getSelectionEnd(mEditable);
int composingStart = BaseInputConnection.getComposingSpanStart(mEditable);
int composingEnd = BaseInputConnection.getComposingSpanEnd(mEditable);
mImm.updateSelection(mFlutterView,
selectionStart, selectionEnd,
composingStart, composingEnd);
textInputChannel.updateEditingState(
mClient,
mEditable.toString(),
selectionStart,
selectionEnd,
composingStart,
composingEnd
);
}
@Override
public Editable getEditable() {
return mEditable;
}
@Override
public boolean beginBatchEdit() {
mBatchCount++;
return super.beginBatchEdit();
}
@Override
public boolean endBatchEdit() {
boolean result = super.endBatchEdit();
mBatchCount--;
updateEditingState();
return result;
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
boolean result = super.commitText(text, newCursorPosition);
updateEditingState();
return result;
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (Selection.getSelectionStart(mEditable) == -1)
return true;
boolean result = super.deleteSurroundingText(beforeLength, afterLength);
updateEditingState();
return result;
}
@Override
public boolean setComposingRegion(int start, int end) {
boolean result = super.setComposingRegion(start, end);
updateEditingState();
return result;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
boolean result;
if (text.length() == 0) {
result = super.commitText(text, newCursorPosition);
} else {
result = super.setComposingText(text, newCursorPosition);
}
updateEditingState();
return result;
}
@Override
public boolean setSelection(int start, int end) {
boolean result = super.setSelection(start, end);
updateEditingState();
return result;
}
// Sanitizes the index to ensure the index is within the range of the
// contents of editable.
private static int clampIndexToEditable(int index, Editable editable) {
int clamped = Math.max(0, Math.min(editable.length(), index));
if (clamped != index) {
Log.d("flutter", "Text selection index was clamped ("
+ index + "->" + clamped
+ ") to remain in bounds. This may not be your fault, as some keyboards may select outside of bounds."
);
}
return clamped;
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
int selStart = clampIndexToEditable(Selection.getSelectionStart(mEditable), mEditable);
int selEnd = clampIndexToEditable(Selection.getSelectionEnd(mEditable), mEditable);
if (selEnd > selStart) {
// Delete the selection.
Selection.setSelection(mEditable, selStart);
mEditable.delete(selStart, selEnd);
updateEditingState();
return true;
} else if (selStart > 0) {
// Delete to the left/right of the cursor depending on direction of text.
// TODO(garyq): Explore how to obtain per-character direction. The
// isRTLCharAt() call below is returning blanket direction assumption
// based on the first character in the line.
boolean isRtl = mLayout.isRtlCharAt(mLayout.getLineForOffset(selStart));
try {
if (isRtl) {
Selection.extendRight(mEditable, mLayout);
} else {
Selection.extendLeft(mEditable, mLayout);
}
} catch (IndexOutOfBoundsException e) {
// On some Chinese devices (primarily Huawei, some Xiaomi),
// on initial app startup before focus is lost, the
// Selection.extendLeft and extendRight calls always extend
// from the index of the initial contents of mEditable. This
// try-catch will prevent crashing on Huawei devices by falling
// back to a simple way of deletion, although this a hack and
// will not handle emojis.
Selection.setSelection(mEditable, selStart, selStart - 1);
}
int newStart = clampIndexToEditable(Selection.getSelectionStart(mEditable), mEditable);
int newEnd = clampIndexToEditable(Selection.getSelectionEnd(mEditable), mEditable);
Selection.setSelection(mEditable, Math.min(newStart, newEnd));
// Min/Max the values since RTL selections will start at a higher
// index than they end at.
mEditable.delete(Math.min(newStart, newEnd), Math.max(newStart, newEnd));
updateEditingState();
return true;
}
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
int selStart = Selection.getSelectionStart(mEditable);
int newSel = Math.max(selStart - 1, 0);
setSelection(newSel, newSel);
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
int selStart = Selection.getSelectionStart(mEditable);
int newSel = Math.min(selStart + 1, mEditable.length());
setSelection(newSel, newSel);
return true;
} else {
// Enter a character.
int character = event.getUnicodeChar();
if (character != 0) {
int selStart = Math.max(0, Selection.getSelectionStart(mEditable));
int selEnd = Math.max(0, Selection.getSelectionEnd(mEditable));
if (selEnd != selStart)
mEditable.delete(selStart, selEnd);
mEditable.insert(selStart, String.valueOf((char) character));
setSelection(selStart + 1, selStart + 1);
updateEditingState();
}
return true;
}
}
return false;
}
@Override
public boolean performEditorAction(int actionCode) {
switch (actionCode) {
case EditorInfo.IME_ACTION_NONE:
textInputChannel.newline(mClient);
break;
case EditorInfo.IME_ACTION_UNSPECIFIED:
textInputChannel.unspecifiedAction(mClient);
break;
case EditorInfo.IME_ACTION_GO:
textInputChannel.go(mClient);
break;
case EditorInfo.IME_ACTION_SEARCH:
textInputChannel.search(mClient);
break;
case EditorInfo.IME_ACTION_SEND:
textInputChannel.send(mClient);
break;
case EditorInfo.IME_ACTION_NEXT:
textInputChannel.next(mClient);
break;
case EditorInfo.IME_ACTION_PREVIOUS:
textInputChannel.previous(mClient);
break;
default:
case EditorInfo.IME_ACTION_DONE:
textInputChannel.done(mClient);
break;
}
return true;
}
}
\ No newline at end of file
package com.idlefish.flutterboost;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
import android.text.InputType;
import android.text.Selection;
import android.view.View;
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.plugin.platform.PlatformViewsController;
/**
* Android implementation of the text input plugin.
*/
public class XTextInputPlugin {
@NonNull
private View mView;
@NonNull
private final InputMethodManager mImm;
@NonNull
private final TextInputChannel textInputChannel;
@NonNull
private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
@Nullable
private TextInputChannel.Configuration configuration;
@Nullable
private Editable mEditable;
private boolean mRestartInputPending;
@Nullable
private InputConnection lastInputConnection;
@NonNull
private PlatformViewsController platformViewsController;
// When true following calls to createInputConnection will return the cached lastInputConnection if the input
// target is a platform view. See the comments on lockPlatformViewInputConnection for more details.
private boolean isInputConnectionLocked;
public XTextInputPlugin(View view, @NonNull TextInputChannel textInputChannel, @NonNull PlatformViewsController platformViewsController) {
mView = view;
mImm = (InputMethodManager) view.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
this.textInputChannel = textInputChannel;
this.platformViewsController = platformViewsController;
// this.platformViewsController.attachTextInputPlugin(this);
}
public void release() {
mView = null;
}
public void setTextInputMethodHandler() {
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 setPlatformViewClient(int platformViewId) {
setPlatformViewTextInputClient(platformViewId);
}
@Override
public void setEditingState(TextInputChannel.TextEditState editingState) {
setTextInputEditingState(mView, editingState);
}
@Override
public void clearClient() {
clearTextInputClient();
}
});
}
@NonNull
public InputMethodManager getInputMethodManager() {
return mImm;
}
/***
* Use the current platform view input connection until unlockPlatformViewInputConnection is called.
*
* The current input connection instance is cached and any following call to @{link createInputConnection} returns
* the cached connection until unlockPlatformViewInputConnection is called.
*
* This is a no-op if the current input target isn't a platform view.
*
* This is used to preserve an input connection when moving a platform view from one virtual display to another.
*/
public void lockPlatformViewInputConnection() {
if (inputTarget.type == InputTarget.Type.PLATFORM_VIEW) {
isInputConnectionLocked = true;
}
}
/**
* Unlocks the input connection.
* <p>
* See also: @{link lockPlatformViewInputConnection}.
*/
public void unlockPlatformViewInputConnection() {
isInputConnectionLocked = false;
}
/**
* Detaches the text input plugin from the platform views controller.
* <p>
* The TextInputPlugin instance should not be used after calling this.
*/
public void destroy() {
platformViewsController.detachTextInputPlugin();
}
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;
} else if (type.type == TextInputChannel.TextInputType.VISIBLE_PASSWORD) {
textType |= InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
}
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(View view, EditorInfo outAttrs) {
if (inputTarget.type == InputTarget.Type.NO_TARGET) {
lastInputConnection = null;
return null;
}
if (inputTarget.type == InputTarget.Type.PLATFORM_VIEW) {
if (isInputConnectionLocked) {
return lastInputConnection;
}
lastInputConnection = platformViewsController.getPlatformViewById(inputTarget.id).onCreateInputConnection(outAttrs);
return lastInputConnection;
}
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;
XInputConnectionAdaptor connection = new XInputConnectionAdaptor(
view,
inputTarget.id,
textInputChannel,
mEditable
);
outAttrs.initialSelStart = Selection.getSelectionStart(mEditable);
outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable);
lastInputConnection = connection;
return lastInputConnection;
}
@Nullable
public InputConnection getLastInputConnection() {
return lastInputConnection;
}
/**
* Clears a platform view text input client if it is the current input target.
* <p>
* This is called when a platform view is disposed to make sure we're not hanging to a stale input
* connection.
*/
public void clearPlatformViewClient(int platformViewId) {
if (inputTarget.type == InputTarget.Type.PLATFORM_VIEW && inputTarget.id == platformViewId) {
inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
hideTextInput(mView);
mImm.restartInput(mView);
mRestartInputPending = false;
}
}
private void showTextInput(View view) {
view.requestFocus();
mImm.showSoftInput(view, 0);
}
private void hideTextInput(View view) {
// Note: a race condition may lead to us hiding the keyboard here just after a platform view has shown it.
// This can only potentially happen when switching focus from a Flutter text field to a platform view's text
// field(by text field here I mean anything that keeps the keyboard open).
// See: https://github.com/flutter/flutter/issues/34169
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}
private void setTextInputClient(int client, TextInputChannel.Configuration configuration) {
inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, 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;
unlockPlatformViewInputConnection();
}
private void setPlatformViewTextInputClient(int platformViewId) {
// We need to make sure that the Flutter view is focused so that no imm operations get short circuited.
// Not asking for focus here specifically manifested in a but on API 28 devices where the platform view's
// request to show a keyboard was ignored.
mView.requestFocus();
inputTarget = new InputTarget(InputTarget.Type.PLATFORM_VIEW, platformViewId);
mImm.restartInput(mView);
mRestartInputPending = false;
}
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(View 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() {
if (inputTarget.type == InputTarget.Type.PLATFORM_VIEW) {
// Focus changes in the framework tree have no guarantees on the order focus nodes are notified. A node
// that lost focus may be notified before or after a node that gained focus.
// When moving the focus from a Flutter text field to an AndroidView, it is possible that the Flutter text
// field's focus node will be notified that it lost focus after the AndroidView was notified that it gained
// focus. When this happens the text field will send a clearTextInput command which we ignore.
// By doing this we prevent the framework from clearing a platform view input client(the only way to do so
// is to set a new framework text client). I don't see an obvious use case for "clearing" a platform views
// text input client, and it may be error prone as we don't know how the platform view manages the input
// connection and we probably shouldn't interfere.
// If we ever want to allow the framework to clear a platform view text client we should probably consider
// changing the focus manager such that focus nodes that lost focus are notified before focus nodes that
// gained focus as part of the same focus event.
return;
}
inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
unlockPlatformViewInputConnection();
}
static private class InputTarget {
enum Type {
NO_TARGET,
// InputConnection is managed by the TextInputPlugin, and events are forwarded to the Flutter framework.
FRAMEWORK_CLIENT,
// InputConnection is managed by an embedded platform view.
PLATFORM_VIEW
}
public InputTarget(@NonNull Type type, int id) {
this.type = type;
this.id = id;
}
@NonNull
Type type;
// The ID of the input target.
//
// For framework clients this is the framework input connection client ID.
// For platform views this is the platform view's ID.
int id;
}
}
\ No newline at end of file
...@@ -226,6 +226,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine ...@@ -226,6 +226,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
flutterEngine.getActivityControlSurface().detachFromActivityForConfigChanges(); flutterEngine.getActivityControlSurface().detachFromActivityForConfigChanges();
} }
flutterView.release();
} }
......
...@@ -54,7 +54,7 @@ public class FlutterSplashView extends FrameLayout { ...@@ -54,7 +54,7 @@ public class FlutterSplashView extends FrameLayout {
public void onFlutterEngineDetachedFromFlutterView() { public void onFlutterEngineDetachedFromFlutterView() {
} }
}; };
int i=0;
@NonNull @NonNull
private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener() { private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener() {
@Override @Override
...@@ -291,6 +291,9 @@ public class FlutterSplashView extends FrameLayout { ...@@ -291,6 +291,9 @@ public class FlutterSplashView extends FrameLayout {
} }
} }
public void onAttach() { public void onAttach() {
Debuger.log("BoostFlutterView onAttach"); Debuger.log("BoostFlutterView onAttach");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment