Android,InputMethod.java简单认识

package android.accessibilityservice;

import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;

import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;

public class InputMethod {
    private static final String LOG_TAG = "A11yInputMethod";

    private final AccessibilityService mService;
    private boolean mInputStarted;
    private RemoteAccessibilityInputConnection mStartedInputConnection;
    private EditorInfo mInputEditorInfo;

    public InputMethod(@NonNull AccessibilityService service) {
        mService = service;
    }

    @Nullable
    public final AccessibilityInputConnection getCurrentInputConnection() {
        if (mStartedInputConnection != null) {
            return new AccessibilityInputConnection(mStartedInputConnection);
        }
        return null;
    }

    public final boolean getCurrentInputStarted() {
        return mInputStarted;
    }

    @Nullable
    public final EditorInfo getCurrentInputEditorInfo() {
        return mInputEditorInfo;
    }

    public void onStartInput(@NonNull EditorInfo attribute, boolean restarting) {
        // Intentionally empty
    }

    public void onFinishInput() {
        // Intentionally empty
    }

    public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart,
            int newSelEnd, int candidatesStart, int candidatesEnd) {
        // Intentionally empty
    }

    final void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
        final AccessibilityInputMethodSessionWrapper wrapper =
                new AccessibilityInputMethodSessionWrapper(mService.getMainLooper(),
                        new SessionImpl());
        try {
            callback.sessionCreated(wrapper, mService.getConnectionId());
        } catch (RemoteException ignored) {
        }
    }

    final void startInput(@Nullable RemoteAccessibilityInputConnection ic,
            @NonNull EditorInfo attribute) {
        Log.v(LOG_TAG, "startInput(): editor=" + attribute);
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.startInput");
        doStartInput(ic, attribute, false /* restarting */);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

    final void restartInput(@Nullable RemoteAccessibilityInputConnection ic,
            @NonNull EditorInfo attribute) {
        Log.v(LOG_TAG, "restartInput(): editor=" + attribute);
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.restartInput");
        doStartInput(ic, attribute, true /* restarting */);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }


    final void doStartInput(RemoteAccessibilityInputConnection ic, EditorInfo attribute,
            boolean restarting) {
        if ((ic == null || !restarting) && mInputStarted) {
            doFinishInput();
            if (ic == null) {
                // Unlike InputMethodService, A11y IME should not observe fallback InputConnection.
                return;
            }
        }
        mInputStarted = true;
        mStartedInputConnection = ic;
        mInputEditorInfo = attribute;
        Log.v(LOG_TAG, "CALL: onStartInput");
        onStartInput(attribute, restarting);
    }

    final void doFinishInput() {
        Log.v(LOG_TAG, "CALL: doFinishInput");
        if (mInputStarted) {
            Log.v(LOG_TAG, "CALL: onFinishInput");
            onFinishInput();
        }
        mInputStarted = false;
        mStartedInputConnection = null;
        mInputEditorInfo = null;
    }

    public final class AccessibilityInputConnection {
        private final RemoteAccessibilityInputConnection mIc;
        AccessibilityInputConnection(RemoteAccessibilityInputConnection ic) {
            this.mIc = ic;
        }

        public void commitText(@NonNull CharSequence text, int newCursorPosition,
                @Nullable TextAttribute textAttribute) {
            if (mIc != null) {
                mIc.commitText(text, newCursorPosition, textAttribute);
            }
        }

        public void setSelection(int start, int end) {
            if (mIc != null) {
                mIc.setSelection(start, end);
            }
        }

        @Nullable
        public SurroundingText getSurroundingText(
                @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
                @InputConnection.GetTextType int flags) {
            if (mIc != null) {
                return mIc.getSurroundingText(beforeLength, afterLength, flags);
            }
            return null;
        }

        public void deleteSurroundingText(int beforeLength, int afterLength) {
            if (mIc != null) {
                mIc.deleteSurroundingText(beforeLength, afterLength);
            }
        }

        public void sendKeyEvent(@NonNull KeyEvent event) {
            if (mIc != null) {
                mIc.sendKeyEvent(event);
            }
        }

        public void performEditorAction(int editorAction) {
            if (mIc != null) {
                mIc.performEditorAction(editorAction);
            }
        }

        public void performContextMenuAction(int id) {
            if (mIc != null) {
                mIc.performContextMenuAction(id);
            }
        }

        public int getCursorCapsMode(int reqModes) {
            if (mIc != null) {
                return mIc.getCursorCapsMode(reqModes);
            }
            return 0;
        }

        public void clearMetaKeyStates(int states) {
            if (mIc != null) {
                mIc.clearMetaKeyStates(states);
            }
        }
    }

    /**
     * Concrete implementation of {@link AccessibilityInputMethodSession} that provides all of the
     * standard behavior for an A11y input method session.
     */
    private final class SessionImpl implements AccessibilityInputMethodSession {
        boolean mEnabled = true;

        @Override
        public void setEnabled(boolean enabled) {
            mEnabled = enabled;
        }

        @Override
        public void finishInput() {
            if (mEnabled) {
                doFinishInput();
            }
        }

        @Override
        public void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart,
                int newSelEnd, int candidatesStart, int candidatesEnd) {
            if (mEnabled) {
                InputMethod.this.onUpdateSelection(oldSelEnd, oldSelEnd, newSelStart,
                        newSelEnd, candidatesStart, candidatesEnd);
            }
        }

        @Override
        public void invalidateInput(EditorInfo editorInfo,
                IRemoteAccessibilityInputConnection connection, int sessionId) {
            if (!mEnabled || mStartedInputConnection == null
                    || !mStartedInputConnection.isSameConnection(connection)) {
                // This is not an error, and can be safely ignored.
                return;
            }
            editorInfo.makeCompatible(mService.getApplicationInfo().targetSdkVersion);
            restartInput(new RemoteAccessibilityInputConnection(mStartedInputConnection, sessionId),
                    editorInfo);
        }
    }
}

这个代码是一个安卓的辅助服务类,用于提供输入法的API。代码的逻辑和作用如下:

  • InputMethod类:这个类是一个辅助服务的输入法类,它有以下主要方法:

    • 构造方法:创建一个新的InputMethod实例,绑定一个辅助服务对象,用于控制编辑。
    • getCurrentInputConnection:获取当前绑定的输入连接对象,或者返回null。
    • getCurrentInputStarted:判断输入是否已经开始。
    • getCurrentInputEditorInfo:获取当前编辑器的属性信息。
    • onStartInput:当输入开始时,初始化输入的状态,根据编辑器的属性信息,可以重写这个方法。
    • onFinishInput:当输入结束时,清理输入的状态,可以重写这个方法。
    • onUpdateSelection:当编辑器报告新的文本选择区域时,可以重写这个方法。
    • createImeSession:创建一个输入法会话对象,用于和远程输入连接通信。
    • startInput:开始输入,调用doStartInput方法。
    • restartInput:重新开始输入,调用doStartInput方法。
    • doStartInput:根据输入连接对象和编辑器属性信息,初始化输入的状态,调用onStartInput方法。
    • doFinishInput:结束输入,调用onFinishInput方法。
  • AccessibilityInputConnection类:这个类是一个辅助服务的输入连接类,它封装了一个远程输入连接对象,提供了以下主要方法:

    • commitText:提交文本到文本框,并设置新的光标位置,可以提供额外的文本信息。
    • setSelection:设置文本编辑器的选择区域,如果开始和结束位置相同,就是设置光标位置。
    • getSurroundingText:获取当前光标周围的文本,包括选择的文本,可以指定前后的长度和返回的文本类型。
    • deleteSurroundingText:删除光标前后的文本,不包括选择的文本,可以指定前后的长度。
    • sendKeyEvent:发送一个键盘事件到当前绑定的进程,事件会像正常的键盘事件一样被分发到当前聚焦的视图。
    • performEditorAction:执行编辑器支持的一个动作,比如回车,搜索等。
    • performContextMenuAction:执行编辑器的一个上下文菜单动作,比如全选,剪切,复制,粘贴等。
    • getCursorCapsMode:获取当前光标位置的大小写模式,可以指定所需的模式。
    • clearMetaKeyStates:清除给定的元键按下状态,可以用于清除硬件键盘的锁定元键状态。
  • SessionImpl类:这个类是一个辅助服务的输入法会话类,它实现了AccessibilityInputMethodSession接口,提供了以下主要方法:

    • setEnabled:设置会话是否可用。
    • finishInput:结束输入,调用doFinishInput方法。
    • updateSelection:更新选择区域,调用onUpdateSelection方法。
    • invalidateInput:使输入失效,如果输入连接对象和会话ID匹配,就重新开始输入,调用restartInput方法。
  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[19:49:14][ERROR]<airtest.core.api> Traceback (most recent call last): File "airtest\core\android\adb.py", line 374, in shell File "airtest\core\android\adb.py", line 332, in raw_shell File "airtest\core\android\adb.py", line 197, in cmd airtest.core.error.AdbError: stdout[b''] stderr[b'java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission\r\n\tat android.os.Parcel.createException(Parcel.java:2074)\r\n\tat android.os.Parcel.readException(Parcel.java:2042)\r\n\tat android.os.Parcel.readException(Parcel.java:1990)\r\n\tat android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:991)\r\n\tat android.hardware.input.InputManager.injectInputEvent(InputManager.java:886)\r\n\tat com.android.commands.input.Input.injectMotionEvent(Input.java:428)\r\n\tat com.android.commands.input.Input.access$200(Input.java:41)\r\n\tat com.android.commands.input.Input$InputTap.sendTap(Input.java:224)\r\n\tat com.android.commands.input.Input$InputTap.run(Input.java:218)\r\n\tat com.android.commands.input.Input.onRun(Input.java:108)\r\n\tat com.android.internal.os.BaseCommand.run(BaseCommand.java:56)\r\n\tat com.android.commands.input.Input.main(Input.java:71)\r\n\tat com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)\r\n\tat com.android.internal.os.RuntimeInit.main(RuntimeInit.java:380)\r\nCaused by: android.os.RemoteException: Remote stack trace:\r\n\tat com.android.server.input.InputManagerService.injectInputEventInternal(InputManagerService.java:754)\r\n\tat com.android.server.input.InputManagerService.injectInputEvent(InputManagerService.java:728)\r\n\tat android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:451)\r\n\tat android.os.Binder.execTransactInternal(Binder.java:1021)\r\n\tat android.os.Binder.execTransact(Binder.java:994)\r\n\r\n'] During handling of the above exception, another exception occurred:
07-25

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值