InputConnectionAdaptor.java 8.4 KB
Newer Older
M
Michael Goderbauer 已提交
1
// Copyright 2013 The Flutter Authors. All rights reserved.
2 3 4 5 6
// 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;

7
import android.content.Context;
8 9
import android.text.Editable;
import android.text.Selection;
A
amirh 已提交
10
import android.view.KeyEvent;
11
import android.view.inputmethod.BaseInputConnection;
12
import android.view.inputmethod.EditorInfo;
13
import android.view.inputmethod.InputMethodManager;
14
import io.flutter.plugin.common.ErrorLogResult;
15
import io.flutter.plugin.common.MethodChannel;
16 17
import io.flutter.view.FlutterView;

18 19
import java.util.Arrays;
import java.util.HashMap;
20

21
class InputConnectionAdaptor extends BaseInputConnection {
22
    private final FlutterView mFlutterView;
23
    private final int mClient;
24
    private final MethodChannel mFlutterChannel;
25 26 27
    private final Editable mEditable;
    private int mBatchCount;
    private InputMethodManager mImm;
28

29 30 31
    private static final MethodChannel.Result logger =
        new ErrorLogResult("FlutterTextInput");

32
    public InputConnectionAdaptor(FlutterView view, int client,
33
        MethodChannel flutterChannel, Editable editable) {
34
        super(view, true);
35
        mFlutterView = view;
36
        mClient = client;
37
        mFlutterChannel = flutterChannel;
38 39 40
        mEditable = editable;
        mBatchCount = 0;
        mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
41 42
    }

43
    // Send the current state of the editable to Flutter.
44
    private void updateEditingState() {
45 46 47 48 49 50 51 52 53 54 55 56 57
        // 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);

58
        HashMap<Object, Object> state = new HashMap<>();
59 60 61 62 63 64
        state.put("text", mEditable.toString());
        state.put("selectionBase", selectionStart);
        state.put("selectionExtent", selectionEnd);
        state.put("composingBase", composingStart);
        state.put("composingExtent", composingEnd);
        mFlutterChannel.invokeMethod("TextInputClient.updateEditingState",
65
            Arrays.asList(mClient, state), logger);
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    }

    @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;
85 86 87 88
    }

    @Override
    public boolean commitText(CharSequence text, int newCursorPosition) {
89
        boolean result = super.commitText(text, newCursorPosition);
90 91 92 93 94 95
        updateEditingState();
        return result;
    }

    @Override
    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
96
        if (Selection.getSelectionStart(mEditable) == -1)
G
gspencergoog 已提交
97
            return true;
98

99
        boolean result = super.deleteSurroundingText(beforeLength, afterLength);
100 101 102 103 104 105
        updateEditingState();
        return result;
    }

    @Override
    public boolean setComposingRegion(int start, int end) {
106
        boolean result = super.setComposingRegion(start, end);
107 108 109 110 111 112
        updateEditingState();
        return result;
    }

    @Override
    public boolean setComposingText(CharSequence text, int newCursorPosition) {
113 114 115 116 117 118
        boolean result;
        if (text.length() == 0) {
            result = super.commitText(text, newCursorPosition);
        } else {
            result = super.setComposingText(text, newCursorPosition);
        }
119 120 121 122 123 124
        updateEditingState();
        return result;
    }

    @Override
    public boolean setSelection(int start, int end) {
125
        boolean result = super.setSelection(start, end);
126 127 128 129 130 131
        updateEditingState();
        return result;
    }

    @Override
    public boolean sendKeyEvent(KeyEvent event) {
132
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
133 134 135 136 137 138
            if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
                int selStart = Selection.getSelectionStart(mEditable);
                int selEnd = Selection.getSelectionEnd(mEditable);
                if (selEnd > selStart) {
                    // Delete the selection.
                    Selection.setSelection(mEditable, selStart);
139 140 141
                    mEditable.delete(selStart, selEnd);
                    updateEditingState();
                    return true;
142 143
                } else if (selStart > 0) {
                    // Delete to the left of the cursor.
144 145 146 147 148
                    int newSel = Math.max(selStart - 1, 0);
                    Selection.setSelection(mEditable, newSel);
                    mEditable.delete(newSel, selStart);
                    updateEditingState();
                    return true;
149
                }
150 151 152 153
            } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                int selStart = Selection.getSelectionStart(mEditable);
                int newSel = Math.max(selStart - 1, 0);
                setSelection(newSel, newSel);
154
                return true;
155 156 157 158
            } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                int selStart = Selection.getSelectionStart(mEditable);
                int newSel = Math.min(selStart + 1, mEditable.length());
                setSelection(newSel, newSel);
159
                return true;
160
            } else {
161
                // Enter a character.
162
                int character = event.getUnicodeChar();
163 164
                if (character != 0) {
                    int selStart = Math.max(0, Selection.getSelectionStart(mEditable));
165
                    int selEnd = Math.max(0, Selection.getSelectionEnd(mEditable));
166 167
                    if (selEnd != selStart)
                        mEditable.delete(selStart, selEnd);
168 169 170 171 172
                    mEditable.insert(selStart, String.valueOf((char) character));
                    setSelection(selStart + 1, selStart + 1);
                    updateEditingState();
                }
                return true;
173 174
            }
        }
175
        return false;
176 177 178 179
    }

    @Override
    public boolean performEditorAction(int actionCode) {
180
        switch (actionCode) {
181
            // TODO(mattcarroll): is newline an appropriate action for "none"?
182 183
            case EditorInfo.IME_ACTION_NONE:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
184
                        Arrays.asList(mClient, "TextInputAction.newline"), logger);
185
                break;
186 187
            case EditorInfo.IME_ACTION_UNSPECIFIED:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
188
                        Arrays.asList(mClient, "TextInputAction.unspecified"), logger);
189 190 191
                break;
            case EditorInfo.IME_ACTION_GO:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
192
                        Arrays.asList(mClient, "TextInputAction.go"), logger);
193 194 195
                break;
            case EditorInfo.IME_ACTION_SEARCH:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
196
                        Arrays.asList(mClient, "TextInputAction.search"), logger);
197 198 199
                break;
            case EditorInfo.IME_ACTION_SEND:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
200
                        Arrays.asList(mClient, "TextInputAction.send"), logger);
201 202 203
                break;
            case EditorInfo.IME_ACTION_NEXT:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
204
                        Arrays.asList(mClient, "TextInputAction.next"), logger);
205 206 207
                break;
            case EditorInfo.IME_ACTION_PREVIOUS:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
208
                        Arrays.asList(mClient, "TextInputAction.previous"), logger);
209
                break;
210 211 212
            default:
            case EditorInfo.IME_ACTION_DONE:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
213
                    Arrays.asList(mClient, "TextInputAction.done"), logger);
214 215
                break;
        }
216
        return true;
217 218
    }
}