1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.sampleinputmethodaccessibilityservice; 18 19 import android.os.Parcel; 20 import android.view.inputmethod.EditorInfo; 21 22 import androidx.annotation.Nullable; 23 24 final class EventMonitor { 25 @FunctionalInterface 26 interface DebugMessageCallback { onMessageChanged(String message)27 void onMessageChanged(String message); 28 } 29 30 private enum State { 31 BeforeFirstStartInput, 32 InputStarted, 33 InputRestarted, 34 InputFinished, 35 } 36 37 private State mState = State.BeforeFirstStartInput; 38 private int mStartInputCount = 0; 39 private int mUpdateSelectionCount = 0; 40 private int mFinishInputCount = 0; 41 private int mSelStart = -1; 42 private int mSelEnd = -1; 43 private int mCompositionStart = -1; 44 private int mCompositionEnd = -1; 45 @Nullable 46 private EditorInfo mEditorInfo; 47 48 @Nullable 49 private final DebugMessageCallback mDebugMessageCallback; 50 onStartInput(EditorInfo attribute, boolean restarting)51 void onStartInput(EditorInfo attribute, boolean restarting) { 52 ++mStartInputCount; 53 mState = restarting ? State.InputRestarted : State.InputStarted; 54 mSelStart = attribute.initialSelStart; 55 mSelEnd = attribute.initialSelEnd; 56 mCompositionStart = -1; 57 mCompositionEnd = -1; 58 mEditorInfo = cloneEditorInfo(attribute); 59 updateMessage(); 60 } 61 onFinishInput()62 void onFinishInput() { 63 ++mFinishInputCount; 64 mState = State.InputFinished; 65 mEditorInfo = null; 66 updateMessage(); 67 } 68 onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)69 void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, 70 int newSelEnd, int candidatesStart, int candidatesEnd) { 71 ++mUpdateSelectionCount; 72 mSelStart = newSelStart; 73 mSelEnd = newSelEnd; 74 mCompositionStart = candidatesStart; 75 mCompositionEnd = candidatesEnd; 76 updateMessage(); 77 } 78 EventMonitor(@ullable DebugMessageCallback callback)79 EventMonitor(@Nullable DebugMessageCallback callback) { 80 mDebugMessageCallback = callback; 81 } 82 updateMessage()83 private void updateMessage() { 84 if (mDebugMessageCallback == null) { 85 return; 86 } 87 88 final StringBuilder sb = new StringBuilder(); 89 sb.append("state=").append(mState).append("\n") 90 .append("startInputCount=").append(mStartInputCount).append("\n") 91 .append("finishInputCount=").append(mFinishInputCount).append("\n") 92 .append("updateSelectionCount=").append(mUpdateSelectionCount).append("\n"); 93 if (mSelStart == -1 && mSelEnd == -1) { 94 sb.append("selection=none\n"); 95 } else { 96 sb.append("selection=(").append(mSelStart).append(",").append(mSelEnd).append(")\n"); 97 } 98 if (mCompositionStart == -1 && mCompositionEnd == -1) { 99 sb.append("composition=none"); 100 } else { 101 sb.append("composition=(") 102 .append(mCompositionStart).append(",").append(mCompositionEnd).append(")"); 103 } 104 if (mEditorInfo != null) { 105 sb.append("\n"); 106 sb.append("packageName=").append(mEditorInfo.packageName).append("\n"); 107 sb.append("inputType="); 108 EditorInfoUtil.dumpInputType(sb, mEditorInfo.inputType); 109 sb.append("\n"); 110 sb.append("imeOptions="); 111 EditorInfoUtil.dumpImeOptions(sb, mEditorInfo.imeOptions); 112 } 113 114 mDebugMessageCallback.onMessageChanged(sb.toString()); 115 } 116 cloneEditorInfo(EditorInfo original)117 private static EditorInfo cloneEditorInfo(EditorInfo original) { 118 Parcel parcel = null; 119 try { 120 parcel = Parcel.obtain(); 121 original.writeToParcel(parcel, 0); 122 parcel.setDataPosition(0); 123 return EditorInfo.CREATOR.createFromParcel(parcel); 124 } finally { 125 if (parcel != null) { 126 parcel.recycle(); 127 } 128 } 129 } 130 } 131