1 /* 2 * Copyright (C) 2017 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 android.inputmethodservice.cts.ime; 18 19 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_BIND_INPUT; 20 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_CREATE; 21 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_DESTROY; 22 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT; 23 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT_VIEW; 24 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT; 25 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT_VIEW; 26 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_UNBIND_INPUT; 27 28 import android.content.Intent; 29 import android.inputmethodservice.InputMethodService; 30 import android.inputmethodservice.cts.DeviceEvent; 31 import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType; 32 import android.inputmethodservice.cts.ime.ImeCommandReceiver.ImeCommandCallbacks; 33 import android.os.Bundle; 34 import android.os.Process; 35 import android.util.Log; 36 import android.view.inputmethod.EditorInfo; 37 import android.view.inputmethod.InputConnection; 38 39 import java.util.function.Consumer; 40 41 /** 42 * Base class to create test {@link InputMethodService}. 43 */ 44 public abstract class CtsBaseInputMethod extends InputMethodService implements ImeCommandCallbacks { 45 46 protected static final boolean DEBUG = false; 47 48 public static final String EDITOR_INFO_KEY_REPLY_USER_HANDLE_SESSION_ID = 49 "android.inputmethodservice.cts.ime.ReplyUserHandleSessionId"; 50 51 public static final String ACTION_KEY_REPLY_USER_HANDLE = 52 "android.inputmethodservice.cts.ime.ReplyUserHandle"; 53 54 public static final String BUNDLE_KEY_REPLY_USER_HANDLE = 55 "android.inputmethodservice.cts.ime.ReplyUserHandle"; 56 57 public static final String BUNDLE_KEY_REPLY_USER_HANDLE_SESSION_ID = 58 "android.inputmethodservice.cts.ime.ReplyUserHandleSessionId"; 59 60 private final ImeCommandReceiver<CtsBaseInputMethod> mImeCommandReceiver = 61 new ImeCommandReceiver<>(); 62 private String mLogTag; 63 64 @Override onCreate()65 public void onCreate() { 66 mLogTag = getClass().getSimpleName(); 67 if (DEBUG) { 68 Log.d(mLogTag, "onCreate:"); 69 } 70 sendEvent(ON_CREATE); 71 72 super.onCreate(); 73 74 mImeCommandReceiver.register(this /* ime */); 75 } 76 77 @Override onBindInput()78 public void onBindInput() { 79 if (DEBUG) { 80 Log.d(mLogTag, "onBindInput"); 81 } 82 sendEvent(ON_BIND_INPUT); 83 super.onBindInput(); 84 } 85 86 @Override onStartInput(EditorInfo editorInfo, boolean restarting)87 public void onStartInput(EditorInfo editorInfo, boolean restarting) { 88 if (DEBUG) { 89 Log.d(mLogTag, "onStartInput:" 90 + " editorInfo=" + editorInfo 91 + " restarting=" + restarting); 92 } 93 sendEvent(ON_START_INPUT, editorInfo, restarting); 94 95 super.onStartInput(editorInfo, restarting); 96 97 if (editorInfo.extras != null) { 98 final String sessionKey = 99 editorInfo.extras.getString(EDITOR_INFO_KEY_REPLY_USER_HANDLE_SESSION_ID, null); 100 if (sessionKey != null) { 101 final Bundle bundle = new Bundle(); 102 bundle.putString(BUNDLE_KEY_REPLY_USER_HANDLE_SESSION_ID, sessionKey); 103 bundle.putParcelable(BUNDLE_KEY_REPLY_USER_HANDLE, Process.myUserHandle()); 104 getCurrentInputConnection().performPrivateCommand( 105 ACTION_KEY_REPLY_USER_HANDLE, bundle); 106 } 107 } 108 } 109 110 @Override onStartInputView(EditorInfo editorInfo, boolean restarting)111 public void onStartInputView(EditorInfo editorInfo, boolean restarting) { 112 if (DEBUG) { 113 Log.d(mLogTag, "onStartInputView:" 114 + " editorInfo=" + editorInfo 115 + " restarting=" + restarting); 116 } 117 sendEvent(ON_START_INPUT_VIEW, editorInfo, restarting); 118 119 super.onStartInputView(editorInfo, restarting); 120 } 121 122 @Override onUnbindInput()123 public void onUnbindInput() { 124 super.onUnbindInput(); 125 if (DEBUG) { 126 Log.d(mLogTag, "onUnbindInput"); 127 } 128 sendEvent(ON_UNBIND_INPUT); 129 } 130 131 @Override onFinishInputView(boolean finishingInput)132 public void onFinishInputView(boolean finishingInput) { 133 if (DEBUG) { 134 Log.d(mLogTag, "onFinishInputView: finishingInput=" + finishingInput); 135 } 136 sendEvent(ON_FINISH_INPUT_VIEW, finishingInput); 137 138 super.onFinishInputView(finishingInput); 139 } 140 141 @Override onFinishInput()142 public void onFinishInput() { 143 if (DEBUG) { 144 Log.d(mLogTag, "onFinishInput:"); 145 } 146 sendEvent(ON_FINISH_INPUT); 147 148 super.onFinishInput(); 149 } 150 151 @Override onDestroy()152 public void onDestroy() { 153 if (DEBUG) { 154 Log.d(mLogTag, "onDestroy:"); 155 } 156 sendEvent(ON_DESTROY); 157 158 super.onDestroy(); 159 160 unregisterReceiver(mImeCommandReceiver); 161 } 162 163 // 164 // Implementations of {@link ImeCommandCallbacks}. 165 // 166 167 @Override commandCommitText(CharSequence text, int newCursorPosition)168 public void commandCommitText(CharSequence text, int newCursorPosition) { 169 executeOnInputConnection(ic -> { 170 // TODO: Log the return value of {@link InputConnection#commitText(CharSequence,int)}. 171 ic.commitText(text, newCursorPosition); 172 }); 173 } 174 175 @Override commandSwitchInputMethod(String imeId)176 public void commandSwitchInputMethod(String imeId) { 177 switchInputMethod(imeId); 178 } 179 180 @Override commandRequestHideSelf(int flags)181 public void commandRequestHideSelf(int flags) { 182 requestHideSelf(flags); 183 } 184 executeOnInputConnection(Consumer<InputConnection> consumer)185 private void executeOnInputConnection(Consumer<InputConnection> consumer) { 186 final InputConnection ic = getCurrentInputConnection(); 187 // TODO: Check and log whether {@code ic} is null or equals to 188 // {@link #getCurrentInputBindin().getConnection()}. 189 if (ic != null) { 190 consumer.accept(ic); 191 } 192 } 193 sendEvent(DeviceEventType type, Object... args)194 private void sendEvent(DeviceEventType type, Object... args) { 195 final String sender = getClass().getName(); 196 final Intent intent = DeviceEvent.newDeviceEventIntent(sender, type); 197 // TODO: Send arbitrary {@code args} in {@code intent}. 198 sendBroadcast(intent); 199 } 200 } 201