1 /* 2 * Copyright (C) 2007 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.view.inputmethod; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.graphics.RectF; 24 import android.os.Bundle; 25 import android.os.CancellationSignal; 26 import android.os.Handler; 27 import android.view.KeyEvent; 28 29 import com.android.internal.util.Preconditions; 30 31 import java.util.concurrent.Executor; 32 import java.util.function.Consumer; 33 import java.util.function.IntConsumer; 34 35 /** 36 * <p>Wrapper class for proxying calls to another InputConnection. Subclass and have fun! 37 */ 38 public class InputConnectionWrapper implements InputConnection { 39 private InputConnection mTarget; 40 final boolean mMutable; 41 42 /** 43 * Initializes a wrapper. 44 * 45 * <p><b>Caveat:</b> Although the system can accept {@code (InputConnection) null} in some 46 * places, you cannot emulate such a behavior by non-null {@link InputConnectionWrapper} that 47 * has {@code null} in {@code target}.</p> 48 * @param target the {@link InputConnection} to be proxied. 49 * @param mutable set {@code true} to protect this object from being reconfigured to target 50 * another {@link InputConnection}. Note that this is ignored while the target is {@code null}. 51 */ InputConnectionWrapper(InputConnection target, boolean mutable)52 public InputConnectionWrapper(InputConnection target, boolean mutable) { 53 mMutable = mutable; 54 mTarget = target; 55 } 56 57 /** 58 * Change the target of the input connection. 59 * 60 * <p><b>Caveat:</b> Although the system can accept {@code (InputConnection) null} in some 61 * places, you cannot emulate such a behavior by non-null {@link InputConnectionWrapper} that 62 * has {@code null} in {@code target}.</p> 63 * @param target the {@link InputConnection} to be proxied. 64 * @throws SecurityException when this wrapper has non-null target and is immutable. 65 */ setTarget(InputConnection target)66 public void setTarget(InputConnection target) { 67 if (mTarget != null && !mMutable) { 68 throw new SecurityException("not mutable"); 69 } 70 mTarget = target; 71 } 72 73 /** 74 * {@inheritDoc} 75 * @throws NullPointerException if the target is {@code null}. 76 * @throws IllegalArgumentException if {@code length} is negative. 77 */ 78 @Nullable 79 @Override getTextBeforeCursor(@ntRangefrom = 0) int n, int flags)80 public CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags) { 81 Preconditions.checkArgumentNonnegative(n); 82 return mTarget.getTextBeforeCursor(n, flags); 83 } 84 85 /** 86 * {@inheritDoc} 87 * @throws NullPointerException if the target is {@code null}. 88 * @throws IllegalArgumentException if {@code length} is negative. 89 */ 90 @Nullable 91 @Override getTextAfterCursor(@ntRangefrom = 0) int n, int flags)92 public CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags) { 93 Preconditions.checkArgumentNonnegative(n); 94 return mTarget.getTextAfterCursor(n, flags); 95 } 96 97 /** 98 * {@inheritDoc} 99 * @throws NullPointerException if the target is {@code null}. 100 */ 101 @Override getSelectedText(int flags)102 public CharSequence getSelectedText(int flags) { 103 return mTarget.getSelectedText(flags); 104 } 105 106 /** 107 * {@inheritDoc} 108 * @throws NullPointerException if the target is {@code null}. 109 * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative. 110 */ 111 @Nullable 112 @Override getSurroundingText(int beforeLength, int afterLength, int flags)113 public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) { 114 Preconditions.checkArgumentNonnegative(beforeLength); 115 Preconditions.checkArgumentNonnegative(afterLength); 116 return mTarget.getSurroundingText(beforeLength, afterLength, flags); 117 } 118 119 /** 120 * {@inheritDoc} 121 * @throws NullPointerException if the target is {@code null}. 122 */ 123 @Override getCursorCapsMode(int reqModes)124 public int getCursorCapsMode(int reqModes) { 125 return mTarget.getCursorCapsMode(reqModes); 126 } 127 128 /** 129 * {@inheritDoc} 130 * @throws NullPointerException if the target is {@code null}. 131 */ 132 @Override getExtractedText(ExtractedTextRequest request, int flags)133 public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { 134 return mTarget.getExtractedText(request, flags); 135 } 136 137 /** 138 * {@inheritDoc} 139 * @throws NullPointerException if the target is {@code null}. 140 */ 141 @Override deleteSurroundingTextInCodePoints(int beforeLength, int afterLength)142 public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { 143 return mTarget.deleteSurroundingTextInCodePoints(beforeLength, afterLength); 144 } 145 146 /** 147 * {@inheritDoc} 148 * @throws NullPointerException if the target is {@code null}. 149 */ 150 @Override deleteSurroundingText(int beforeLength, int afterLength)151 public boolean deleteSurroundingText(int beforeLength, int afterLength) { 152 return mTarget.deleteSurroundingText(beforeLength, afterLength); 153 } 154 155 /** 156 * {@inheritDoc} 157 * @throws NullPointerException if the target is {@code null}. 158 */ 159 @Override setComposingText(CharSequence text, int newCursorPosition)160 public boolean setComposingText(CharSequence text, int newCursorPosition) { 161 return mTarget.setComposingText(text, newCursorPosition); 162 } 163 164 /** 165 * {@inheritDoc} 166 * @throws NullPointerException if the target is {@code null}. 167 */ 168 @Override setComposingText(@onNull CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute)169 public boolean setComposingText(@NonNull CharSequence text, 170 int newCursorPosition, @Nullable TextAttribute textAttribute) { 171 return mTarget.setComposingText(text, newCursorPosition, textAttribute); 172 } 173 174 /** 175 * {@inheritDoc} 176 * @throws NullPointerException if the target is {@code null}. 177 */ 178 @Override setComposingRegion(int start, int end)179 public boolean setComposingRegion(int start, int end) { 180 return mTarget.setComposingRegion(start, end); 181 } 182 183 /** 184 * {@inheritDoc} 185 * @throws NullPointerException if the target is {@code null}. 186 */ 187 @Override setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute)188 public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) { 189 return mTarget.setComposingRegion(start, end, textAttribute); 190 } 191 192 /** 193 * {@inheritDoc} 194 * @throws NullPointerException if the target is {@code null}. 195 */ 196 @Override finishComposingText()197 public boolean finishComposingText() { 198 return mTarget.finishComposingText(); 199 } 200 201 /** 202 * {@inheritDoc} 203 * @throws NullPointerException if the target is {@code null}. 204 */ 205 @Override commitText(CharSequence text, int newCursorPosition)206 public boolean commitText(CharSequence text, int newCursorPosition) { 207 return mTarget.commitText(text, newCursorPosition); 208 } 209 210 /** 211 * {@inheritDoc} 212 * @throws NullPointerException if the target is {@code null}. 213 */ 214 @Override commitText(@onNull CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute)215 public boolean commitText(@NonNull CharSequence text, int newCursorPosition, 216 @Nullable TextAttribute textAttribute) { 217 return mTarget.commitText(text, newCursorPosition, textAttribute); 218 } 219 220 /** 221 * {@inheritDoc} 222 * @throws NullPointerException if the target is {@code null}. 223 */ 224 @Override commitCompletion(CompletionInfo text)225 public boolean commitCompletion(CompletionInfo text) { 226 return mTarget.commitCompletion(text); 227 } 228 229 /** 230 * {@inheritDoc} 231 * @throws NullPointerException if the target is {@code null}. 232 */ 233 @Override commitCorrection(CorrectionInfo correctionInfo)234 public boolean commitCorrection(CorrectionInfo correctionInfo) { 235 return mTarget.commitCorrection(correctionInfo); 236 } 237 238 /** 239 * {@inheritDoc} 240 * @throws NullPointerException if the target is {@code null}. 241 */ 242 @Override setSelection(int start, int end)243 public boolean setSelection(int start, int end) { 244 return mTarget.setSelection(start, end); 245 } 246 247 /** 248 * {@inheritDoc} 249 * @throws NullPointerException if the target is {@code null}. 250 */ 251 @Override performEditorAction(int editorAction)252 public boolean performEditorAction(int editorAction) { 253 return mTarget.performEditorAction(editorAction); 254 } 255 256 /** 257 * {@inheritDoc} 258 * @throws NullPointerException if the target is {@code null}. 259 */ 260 @Override performContextMenuAction(int id)261 public boolean performContextMenuAction(int id) { 262 return mTarget.performContextMenuAction(id); 263 } 264 265 /** 266 * {@inheritDoc} 267 * @throws NullPointerException if the target is {@code null}. 268 */ 269 @Override beginBatchEdit()270 public boolean beginBatchEdit() { 271 return mTarget.beginBatchEdit(); 272 } 273 274 /** 275 * {@inheritDoc} 276 * @throws NullPointerException if the target is {@code null}. 277 */ 278 @Override endBatchEdit()279 public boolean endBatchEdit() { 280 return mTarget.endBatchEdit(); 281 } 282 283 /** 284 * {@inheritDoc} 285 * @throws NullPointerException if the target is {@code null}. 286 */ 287 @Override sendKeyEvent(KeyEvent event)288 public boolean sendKeyEvent(KeyEvent event) { 289 return mTarget.sendKeyEvent(event); 290 } 291 292 /** 293 * {@inheritDoc} 294 * @throws NullPointerException if the target is {@code null}. 295 */ 296 @Override clearMetaKeyStates(int states)297 public boolean clearMetaKeyStates(int states) { 298 return mTarget.clearMetaKeyStates(states); 299 } 300 301 /** 302 * {@inheritDoc} 303 * @throws NullPointerException if the target is {@code null}. 304 */ 305 @Override reportFullscreenMode(boolean enabled)306 public boolean reportFullscreenMode(boolean enabled) { 307 return mTarget.reportFullscreenMode(enabled); 308 } 309 310 /** 311 * {@inheritDoc} 312 * @throws NullPointerException if the target is {@code null}. 313 */ 314 @Override performSpellCheck()315 public boolean performSpellCheck() { 316 return mTarget.performSpellCheck(); 317 } 318 319 /** 320 * {@inheritDoc} 321 * @throws NullPointerException if the target is {@code null}. 322 */ 323 @Override performPrivateCommand(String action, Bundle data)324 public boolean performPrivateCommand(String action, Bundle data) { 325 return mTarget.performPrivateCommand(action, data); 326 } 327 328 /** 329 * {@inheritDoc} 330 * @throws NullPointerException if the target is {@code null}. 331 */ 332 @Override performHandwritingGesture( @onNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor, @Nullable IntConsumer consumer)333 public void performHandwritingGesture( 334 @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor, 335 @Nullable IntConsumer consumer) { 336 mTarget.performHandwritingGesture(gesture, executor, consumer); 337 } 338 339 /** 340 * {@inheritDoc} 341 * @throws NullPointerException if the target is {@code null}. 342 */ 343 @Override previewHandwritingGesture( @onNull PreviewableHandwritingGesture gesture, @Nullable CancellationSignal cancellationSignal)344 public boolean previewHandwritingGesture( 345 @NonNull PreviewableHandwritingGesture gesture, 346 @Nullable CancellationSignal cancellationSignal) { 347 return mTarget.previewHandwritingGesture(gesture, cancellationSignal); 348 } 349 350 /** 351 * {@inheritDoc} 352 * @throws NullPointerException if the target is {@code null}. 353 */ 354 @Override requestCursorUpdates(int cursorUpdateMode)355 public boolean requestCursorUpdates(int cursorUpdateMode) { 356 return mTarget.requestCursorUpdates(cursorUpdateMode); 357 } 358 359 /** 360 * {@inheritDoc} 361 * @throws NullPointerException if the target is {@code null}. 362 */ 363 @Override requestCursorUpdates(@ursorUpdateMode int cursorUpdateMode, @CursorUpdateFilter int cursorUpdateFilter)364 public boolean requestCursorUpdates(@CursorUpdateMode int cursorUpdateMode, 365 @CursorUpdateFilter int cursorUpdateFilter) { 366 return mTarget.requestCursorUpdates(cursorUpdateMode, cursorUpdateFilter); 367 } 368 369 /** 370 * {@inheritDoc} 371 * @throws NullPointerException if the target is {@code null}. 372 */ 373 @Override requestTextBoundsInfo( @onNull RectF bounds, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<TextBoundsInfoResult> consumer)374 public void requestTextBoundsInfo( 375 @NonNull RectF bounds, @NonNull @CallbackExecutor Executor executor, 376 @NonNull Consumer<TextBoundsInfoResult> consumer) { 377 mTarget.requestTextBoundsInfo(bounds, executor, consumer); 378 } 379 380 /** 381 * {@inheritDoc} 382 * @throws NullPointerException if the target is {@code null}. 383 */ 384 @Override getHandler()385 public Handler getHandler() { 386 return mTarget.getHandler(); 387 } 388 389 /** 390 * {@inheritDoc} 391 * @throws NullPointerException if the target is {@code null}. 392 */ 393 @Override closeConnection()394 public void closeConnection() { 395 mTarget.closeConnection(); 396 } 397 398 /** 399 * {@inheritDoc} 400 * @throws NullPointerException if the target is {@code null}. 401 */ 402 @Override commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts)403 public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { 404 return mTarget.commitContent(inputContentInfo, flags, opts); 405 } 406 407 /** 408 * {@inheritDoc} 409 * @throws NullPointerException if the target is {@code null}. 410 */ 411 @Override setImeConsumesInput(boolean imeConsumesInput)412 public boolean setImeConsumesInput(boolean imeConsumesInput) { 413 return mTarget.setImeConsumesInput(imeConsumesInput); 414 } 415 416 /** 417 * Called by the system when it needs to take a snapshot of multiple text-related data in an 418 * atomic manner. 419 * 420 * <p><strong>Editor authors</strong>: Supporting this method is strongly encouraged. Atomically 421 * taken {@link TextSnapshot} is going to be really helpful for the system when optimizing IPCs 422 * in a safe and deterministic manner. Return {@code null} if an atomically taken 423 * {@link TextSnapshot} is unavailable. The system continues supporting such a scenario 424 * gracefully.</p> 425 * 426 * <p><strong>IME authors</strong>: Currently IMEs cannot call this method directly and always 427 * receive {@code null} as the result.</p> 428 * 429 * <p>Beware that there is a bug that this method was not overridden in 430 * {@link InputConnectionWrapper}, which ended up always returning {@code null} when gets 431 * called even if the wrapped {@link InputConnection} implements this method. The bug was 432 * fixed in {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}.</p> 433 * 434 * @return {@code null} if {@link TextSnapshot} is unavailable and/or this API is called from 435 * IMEs. Beware the bug in older devices mentioned above. 436 * @throws NullPointerException if the target is {@code null}. 437 */ 438 @Nullable 439 @Override takeSnapshot()440 public TextSnapshot takeSnapshot() { 441 return mTarget.takeSnapshot(); 442 } 443 444 /** 445 * {@inheritDoc} 446 * @throws NullPointerException if the target is {@code null}. 447 */ 448 @Override replaceText( @ntRangefrom = 0) int start, @IntRange(from = 0) int end, @NonNull CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute)449 public boolean replaceText( 450 @IntRange(from = 0) int start, 451 @IntRange(from = 0) int end, 452 @NonNull CharSequence text, 453 int newCursorPosition, 454 @Nullable TextAttribute textAttribute) { 455 return mTarget.replaceText(start, end, text, newCursorPosition, textAttribute); 456 } 457 } 458