276 lines
9.8 KiB
Java
276 lines
9.8 KiB
Java
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.inputmethodservice;
|
|
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.Context;
|
|
import android.graphics.Rect;
|
|
import android.os.Bundle;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.util.Log;
|
|
import android.util.SparseArray;
|
|
import android.view.InputChannel;
|
|
import android.view.InputDevice;
|
|
import android.view.InputEvent;
|
|
import android.view.InputEventReceiver;
|
|
import android.view.KeyEvent;
|
|
import android.view.MotionEvent;
|
|
import android.view.inputmethod.CompletionInfo;
|
|
import android.view.inputmethod.CursorAnchorInfo;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.ExtractedText;
|
|
import android.view.inputmethod.InputMethodSession;
|
|
|
|
import com.android.internal.inputmethod.IInputMethodSession;
|
|
import com.android.internal.inputmethod.IRemoteInputConnection;
|
|
import com.android.internal.os.HandlerCaller;
|
|
import com.android.internal.os.SomeArgs;
|
|
|
|
class IInputMethodSessionWrapper extends IInputMethodSession.Stub
|
|
implements HandlerCaller.Callback {
|
|
private static final String TAG = "InputMethodWrapper";
|
|
|
|
private static final int DO_DISPLAY_COMPLETIONS = 65;
|
|
private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
|
|
private static final int DO_UPDATE_SELECTION = 90;
|
|
private static final int DO_UPDATE_CURSOR = 95;
|
|
private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99;
|
|
private static final int DO_APP_PRIVATE_COMMAND = 100;
|
|
private static final int DO_FINISH_SESSION = 110;
|
|
private static final int DO_VIEW_CLICKED = 115;
|
|
private static final int DO_REMOVE_IME_SURFACE = 130;
|
|
private static final int DO_FINISH_INPUT = 140;
|
|
private static final int DO_INVALIDATE_INPUT = 150;
|
|
|
|
|
|
@UnsupportedAppUsage
|
|
HandlerCaller mCaller;
|
|
InputMethodSession mInputMethodSession;
|
|
InputChannel mChannel;
|
|
ImeInputEventReceiver mReceiver;
|
|
|
|
public IInputMethodSessionWrapper(Context context,
|
|
InputMethodSession inputMethodSession, InputChannel channel) {
|
|
mCaller = new HandlerCaller(context, null,
|
|
this, true /*asyncHandler*/);
|
|
mInputMethodSession = inputMethodSession;
|
|
mChannel = channel;
|
|
if (channel != null) {
|
|
mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
|
|
}
|
|
}
|
|
|
|
public InputMethodSession getInternalInputMethodSession() {
|
|
return mInputMethodSession;
|
|
}
|
|
|
|
@Override
|
|
public void executeMessage(Message msg) {
|
|
if (mInputMethodSession == null) {
|
|
// The session has been finished. Args needs to be recycled
|
|
// for cases below.
|
|
switch (msg.what) {
|
|
case DO_UPDATE_SELECTION:
|
|
case DO_APP_PRIVATE_COMMAND: {
|
|
SomeArgs args = (SomeArgs)msg.obj;
|
|
args.recycle();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (msg.what) {
|
|
case DO_DISPLAY_COMPLETIONS:
|
|
mInputMethodSession.displayCompletions((CompletionInfo[])msg.obj);
|
|
return;
|
|
case DO_UPDATE_EXTRACTED_TEXT:
|
|
mInputMethodSession.updateExtractedText(msg.arg1,
|
|
(ExtractedText)msg.obj);
|
|
return;
|
|
case DO_UPDATE_SELECTION: {
|
|
SomeArgs args = (SomeArgs)msg.obj;
|
|
mInputMethodSession.updateSelection(args.argi1, args.argi2,
|
|
args.argi3, args.argi4, args.argi5, args.argi6);
|
|
args.recycle();
|
|
return;
|
|
}
|
|
case DO_UPDATE_CURSOR: {
|
|
mInputMethodSession.updateCursor((Rect)msg.obj);
|
|
return;
|
|
}
|
|
case DO_UPDATE_CURSOR_ANCHOR_INFO: {
|
|
mInputMethodSession.updateCursorAnchorInfo((CursorAnchorInfo)msg.obj);
|
|
return;
|
|
}
|
|
case DO_APP_PRIVATE_COMMAND: {
|
|
SomeArgs args = (SomeArgs)msg.obj;
|
|
mInputMethodSession.appPrivateCommand((String)args.arg1,
|
|
(Bundle)args.arg2);
|
|
args.recycle();
|
|
return;
|
|
}
|
|
case DO_FINISH_SESSION: {
|
|
doFinishSession();
|
|
return;
|
|
}
|
|
case DO_VIEW_CLICKED: {
|
|
mInputMethodSession.viewClicked(msg.arg1 == 1);
|
|
return;
|
|
}
|
|
case DO_REMOVE_IME_SURFACE: {
|
|
mInputMethodSession.removeImeSurface();
|
|
return;
|
|
}
|
|
case DO_FINISH_INPUT: {
|
|
mInputMethodSession.finishInput();
|
|
return;
|
|
}
|
|
case DO_INVALIDATE_INPUT: {
|
|
final SomeArgs args = (SomeArgs) msg.obj;
|
|
try {
|
|
mInputMethodSession.invalidateInputInternal((EditorInfo) args.arg1,
|
|
(IRemoteInputConnection) args.arg2, msg.arg1);
|
|
} finally {
|
|
args.recycle();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
Log.w(TAG, "Unhandled message code: " + msg.what);
|
|
}
|
|
|
|
private void doFinishSession() {
|
|
mInputMethodSession = null;
|
|
if (mReceiver != null) {
|
|
mReceiver.dispose();
|
|
mReceiver = null;
|
|
}
|
|
if (mChannel != null) {
|
|
mChannel.dispose();
|
|
mChannel = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void displayCompletions(CompletionInfo[] completions) {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessageO(
|
|
DO_DISPLAY_COMPLETIONS, completions));
|
|
}
|
|
|
|
@Override
|
|
public void updateExtractedText(int token, ExtractedText text) {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
|
|
DO_UPDATE_EXTRACTED_TEXT, token, text));
|
|
}
|
|
|
|
@Override
|
|
public void updateSelection(int oldSelStart, int oldSelEnd,
|
|
int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
|
|
oldSelStart, oldSelEnd, newSelStart, newSelEnd,
|
|
candidatesStart, candidatesEnd));
|
|
}
|
|
|
|
@Override
|
|
public void viewClicked(boolean focusChanged) {
|
|
mCaller.executeOrSendMessage(
|
|
mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
|
|
}
|
|
|
|
@Override
|
|
public void removeImeSurface() {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_IME_SURFACE));
|
|
}
|
|
|
|
@Override
|
|
public void updateCursor(Rect newCursor) {
|
|
mCaller.executeOrSendMessage(
|
|
mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
|
|
}
|
|
|
|
@Override
|
|
public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
|
|
mCaller.executeOrSendMessage(
|
|
mCaller.obtainMessageO(DO_UPDATE_CURSOR_ANCHOR_INFO, cursorAnchorInfo));
|
|
}
|
|
|
|
@Override
|
|
public void appPrivateCommand(String action, Bundle data) {
|
|
mCaller.executeOrSendMessage(
|
|
mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
|
|
}
|
|
|
|
@Override
|
|
public void finishSession() {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
|
|
}
|
|
|
|
@Override
|
|
public void invalidateInput(EditorInfo editorInfo, IRemoteInputConnection inputConnection,
|
|
int sessionId) {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(
|
|
DO_INVALIDATE_INPUT, sessionId, editorInfo, inputConnection));
|
|
}
|
|
|
|
@Override
|
|
public void finishInput() {
|
|
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
|
|
}
|
|
private final class ImeInputEventReceiver extends InputEventReceiver
|
|
implements InputMethodSession.EventCallback {
|
|
private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>();
|
|
|
|
public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
|
|
super(inputChannel, looper);
|
|
}
|
|
|
|
@Override
|
|
public void onInputEvent(InputEvent event) {
|
|
if (mInputMethodSession == null) {
|
|
// The session has been finished.
|
|
finishInputEvent(event, false);
|
|
return;
|
|
}
|
|
|
|
final int seq = event.getSequenceNumber();
|
|
mPendingEvents.put(seq, event);
|
|
if (event instanceof KeyEvent) {
|
|
KeyEvent keyEvent = (KeyEvent)event;
|
|
mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
|
|
} else {
|
|
MotionEvent motionEvent = (MotionEvent)event;
|
|
if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
|
|
mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this);
|
|
} else {
|
|
mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void finishedEvent(int seq, boolean handled) {
|
|
int index = mPendingEvents.indexOfKey(seq);
|
|
if (index >= 0) {
|
|
InputEvent event = mPendingEvents.valueAt(index);
|
|
mPendingEvents.removeAt(index);
|
|
finishInputEvent(event, handled);
|
|
}
|
|
}
|
|
}
|
|
}
|