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