您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
233 行
9.2 KiB
233 行
9.2 KiB
// 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 com.unity.uiwidgets.plugin.editing;
|
|
|
|
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 com.unity.uiwidgets.plugin.UIWidgetsMessageManager;
|
|
|
|
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
|
|
class InputConnectionAdaptor extends BaseInputConnection {
|
|
|
|
private View mTextInputView;
|
|
private final int mClient;
|
|
private final Editable mEditable;
|
|
private int mBatchCount;
|
|
private InputMethodManager mImm;
|
|
|
|
public InputConnectionAdaptor(View view, int client,
|
|
Editable editable) {
|
|
super(view, true);
|
|
mTextInputView = view;
|
|
mClient = client;
|
|
mEditable = editable;
|
|
mBatchCount = 0;
|
|
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(mTextInputView,
|
|
selectionStart, selectionEnd,
|
|
composingStart, composingEnd);
|
|
|
|
HashMap<Object, Object> state = new HashMap<Object, Object>();
|
|
state.put("text", mEditable.toString());
|
|
state.put("selectionBase", selectionStart);
|
|
state.put("selectionExtent", selectionEnd);
|
|
state.put("composingBase", composingStart);
|
|
state.put("composingExtent", composingEnd);
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.updateEditingState",
|
|
Arrays.asList(mClient, state));
|
|
}
|
|
|
|
@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;
|
|
}
|
|
|
|
@Override
|
|
public boolean sendKeyEvent(KeyEvent event) {
|
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
|
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
|
|
int selStart = Selection.getSelectionStart(mEditable);
|
|
int selEnd = Selection.getSelectionEnd(mEditable);
|
|
String text = mEditable.toString();
|
|
if(selStart >= 0 && selStart < text.length() && isTrailSurrogate(text.charAt(selStart))) {
|
|
selStart++;
|
|
}
|
|
if(selEnd >= 0 && selEnd < text.length() && isTrailSurrogate(text.charAt(selEnd))) {
|
|
selEnd++;
|
|
}
|
|
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 of the cursor.
|
|
int newSel = Math.max(selStart - 1, 0);
|
|
if(selStart >= 2 && isTrailSurrogate(text.charAt(selStart-1)))
|
|
newSel = selStart - 2;
|
|
Selection.setSelection(mEditable, newSel);
|
|
mEditable.delete(newSel, selStart);
|
|
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) {
|
|
// TODO(mattcarroll): is newline an appropriate action for "none"?
|
|
case EditorInfo.IME_ACTION_NONE:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.newline"));
|
|
break;
|
|
case EditorInfo.IME_ACTION_UNSPECIFIED:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.unspecified"));
|
|
break;
|
|
case EditorInfo.IME_ACTION_GO:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.go"));
|
|
break;
|
|
case EditorInfo.IME_ACTION_SEARCH:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.search"));
|
|
break;
|
|
case EditorInfo.IME_ACTION_SEND:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.send"));
|
|
break;
|
|
case EditorInfo.IME_ACTION_NEXT:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.next"));
|
|
break;
|
|
case EditorInfo.IME_ACTION_PREVIOUS:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.previous"));
|
|
break;
|
|
default:
|
|
case EditorInfo.IME_ACTION_DONE:
|
|
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
|
|
Arrays.asList(mClient, "TextInputAction.done"));
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean isLeadSurrogate(int c) {
|
|
return (c & 0xfffffc00) == 0xd800;
|
|
}
|
|
|
|
|
|
private boolean isTrailSurrogate(int c) {
|
|
return (c & 0xfffffc00) == 0xdc00;
|
|
}
|
|
}
|