InputConnectionAdaptor.java 6.7 KB
Newer Older
1 2 3 4 5 6
// 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;

7
import android.content.Context;
8 9 10
import android.text.Editable;
import android.text.Selection;
import android.view.inputmethod.BaseInputConnection;
11
import android.view.inputmethod.EditorInfo;
12
import android.view.inputmethod.InputMethodManager;
13
import android.view.KeyEvent;
14

15
import io.flutter.plugin.common.MethodChannel;
16 17
import io.flutter.view.FlutterView;

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

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

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

41
    // Send the current state of the editable to Flutter.
42
    private void updateEditingState() {
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
        // 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);

        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);
        mFlutterChannel.invokeMethod("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;
83 84 85 86
    }

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

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

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

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

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

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

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

    @Override
    public boolean performEditorAction(int actionCode) {
175
        // TODO(abarth): Support more actions.
176 177 178 179 180 181 182 183 184 185 186
        switch (actionCode) {
            case EditorInfo.IME_ACTION_NONE:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
                    Arrays.asList(mClient, "TextInputAction.newline"));
                break;
            default:
            case EditorInfo.IME_ACTION_DONE:
                mFlutterChannel.invokeMethod("TextInputClient.performAction",
                    Arrays.asList(mClient, "TextInputAction.done"));
                break;
        }
187
        return true;
188 189
    }
}