• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package android.inputmethodservice;
18 
19 import static android.view.inputmethod.Flags.FLAG_VERIFY_KEY_EVENT;
20 
21 import android.annotation.FlaggedApi;
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.res.Configuration;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.view.KeyEvent;
32 import android.view.MotionEvent;
33 import android.view.WindowManager;
34 import android.view.WindowManagerGlobal;
35 import android.view.inputmethod.Flags;
36 import android.view.inputmethod.InputMethod;
37 import android.view.inputmethod.InputMethodSession;
38 import android.window.WindowProviderService;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 
45 /**
46  * AbstractInputMethodService provides a abstract base class for input methods.
47  * Normal input method implementations will not derive from this directly,
48  * instead building on top of {@link InputMethodService} or another more
49  * complete base class.  Be sure to read {@link InputMethod} for more
50  * information on the basics of writing input methods.
51  *
52  * <p>This class combines a Service (representing the input method component
53  * to the system with the InputMethod interface that input methods must
54  * implement.  This base class takes care of reporting your InputMethod from
55  * the service when clients bind to it, but provides no standard implementation
56  * of the InputMethod interface itself.  Derived classes must implement that
57  * interface.</p>
58  *
59  * <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft
60  * input may not be the entire screen. For example, some devices may support to show the soft input
61  * on only half of screen.</p>
62  *
63  * <p>In that case, moving the soft input from one half screen to another will trigger a
64  * {@link android.content.res.Resources} update to match the new {@link Configuration} and
65  * this {@link AbstractInputMethodService} may also receive a
66  * {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes
67  * </p>
68  *
69  * @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration)
70  * @see Context#isUiContext Context#isUiContext to see the concept of UI Context.
71  */
72 public abstract class AbstractInputMethodService extends WindowProviderService
73         implements KeyEvent.Callback {
74     private InputMethod mInputMethod;
75 
76     /**
77      * @return {@link InputMethod} instance returned from {@link #onCreateInputMethodInterface()}.
78      *         {@code null} if {@link #onCreateInputMethodInterface()} is not yet called.
79      * @hide
80      */
81     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
82     @Nullable
getInputMethodInternal()83     public final InputMethod getInputMethodInternal() {
84         return mInputMethod;
85     }
86 
87     /**
88      * Keep the strong reference to {@link InputMethodServiceInternal} to ensure that it will not be
89      * garbage-collected until {@link AbstractInputMethodService} gets garbage-collected.
90      *
91      * <p>This is necessary because {@link RemoteInputConnection} internally uses
92      * {@link java.lang.ref.WeakReference} to hold {@link InputMethodServiceInternal}.</p>
93      */
94     @Nullable
95     private InputMethodServiceInternal mInputMethodServiceInternal;
96 
97     final KeyEvent.DispatcherState mDispatcherState
98             = new KeyEvent.DispatcherState();
99 
100     /**
101      * Base class for derived classes to implement their {@link InputMethod}
102      * interface.  This takes care of basic maintenance of the input method,
103      * but most behavior must be implemented in a derived class.
104      */
105     public abstract class AbstractInputMethodImpl implements InputMethod {
106         /**
107          * Instantiate a new client session for the input method, by calling
108          * back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
109          * AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
110          */
111         @MainThread
createSession(SessionCallback callback)112         public void createSession(SessionCallback callback) {
113             callback.sessionCreated(onCreateInputMethodSessionInterface());
114         }
115 
116         /**
117          * Take care of enabling or disabling an existing session by calling its
118          * {@link AbstractInputMethodSessionImpl#revokeSelf()
119          * AbstractInputMethodSessionImpl.setEnabled()} method.
120          */
121         @MainThread
setSessionEnabled(InputMethodSession session, boolean enabled)122         public void setSessionEnabled(InputMethodSession session, boolean enabled) {
123             ((AbstractInputMethodSessionImpl)session).setEnabled(enabled);
124         }
125 
126         /**
127          * Take care of killing an existing session by calling its
128          * {@link AbstractInputMethodSessionImpl#revokeSelf()
129          * AbstractInputMethodSessionImpl.revokeSelf()} method.
130          */
131         @MainThread
revokeSession(InputMethodSession session)132         public void revokeSession(InputMethodSession session) {
133             ((AbstractInputMethodSessionImpl)session).revokeSelf();
134         }
135     }
136 
137     /**
138      * Base class for derived classes to implement their {@link InputMethodSession}
139      * interface.  This takes care of basic maintenance of the session,
140      * but most behavior must be implemented in a derived class.
141      */
142     public abstract class AbstractInputMethodSessionImpl implements InputMethodSession {
143         boolean mEnabled = true;
144         boolean mRevoked;
145 
146         /**
147          * Check whether this session has been enabled by the system.  If not
148          * enabled, you should not execute any calls on to it.
149          */
isEnabled()150         public boolean isEnabled() {
151             return mEnabled;
152         }
153 
154         /**
155          * Check whether this session has been revoked by the system.  Revoked
156          * session is also always disabled, so there is generally no need to
157          * explicitly check for this.
158          */
isRevoked()159         public boolean isRevoked() {
160             return mRevoked;
161         }
162 
163         /**
164          * Change the enabled state of the session.  This only works if the
165          * session has not been revoked.
166          */
setEnabled(boolean enabled)167         public void setEnabled(boolean enabled) {
168             if (!mRevoked) {
169                 mEnabled = enabled;
170             }
171         }
172 
173         /**
174          * Revoke the session from the client.  This disabled the session, and
175          * prevents it from ever being enabled again.
176          */
revokeSelf()177         public void revokeSelf() {
178             mRevoked = true;
179             mEnabled = false;
180         }
181 
182         /**
183          * Take care of dispatching incoming key events to the appropriate
184          * callbacks on the service, and tell the client when this is done.
185          */
186         @Override
dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback)187         public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
188             boolean handled = event.dispatch(AbstractInputMethodService.this,
189                     mDispatcherState, this);
190             if (callback != null) {
191                 callback.finishedEvent(seq, handled);
192             }
193             if (Flags.imeSwitcherRevamp() && !handled && event.getAction() == KeyEvent.ACTION_DOWN
194                     && event.getUnicodeChar() > 0 && mInputMethodServiceInternal != null) {
195                 mInputMethodServiceInternal.notifyUserActionIfNecessary();
196             }
197         }
198 
199         @FlaggedApi(FLAG_VERIFY_KEY_EVENT)
200         @Override
onShouldVerifyKeyEvent(@onNull KeyEvent event)201         public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) {
202             return AbstractInputMethodService.this.onShouldVerifyKeyEvent(event);
203         }
204 
205         /**
206          * Take care of dispatching incoming trackball events to the appropriate
207          * callbacks on the service, and tell the client when this is done.
208          */
209         @Override
dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback)210         public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
211             boolean handled = onTrackballEvent(event);
212             if (callback != null) {
213                 callback.finishedEvent(seq, handled);
214             }
215         }
216 
217         /**
218          * Take care of dispatching incoming generic motion events to the appropriate
219          * callbacks on the service, and tell the client when this is done.
220          */
221         @Override
dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback)222         public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) {
223             boolean handled = onGenericMotionEvent(event);
224             if (callback != null) {
225                 callback.finishedEvent(seq, handled);
226             }
227         }
228     }
229 
230     /**
231      * Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
232      * for used for processing events from the target application.
233      * Normally you will not need to use this directly, but
234      * just use the standard high-level event callbacks like {@link #onKeyDown}.
235      */
getKeyDispatcherState()236     public KeyEvent.DispatcherState getKeyDispatcherState() {
237         return mDispatcherState;
238     }
239 
240     /**
241      * Called by the framework during initialization, when the InputMethod
242      * interface for this service needs to be created.
243      */
onCreateInputMethodInterface()244     public abstract AbstractInputMethodImpl onCreateInputMethodInterface();
245 
246     /**
247      * Called by the framework when a new InputMethodSession interface is
248      * needed for a new client of the input method.
249      */
onCreateInputMethodSessionInterface()250     public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
251 
252     /**
253      * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
254      * calls on your input method.
255      */
256     @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)257     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
258     }
259 
260     @Override
onBind(Intent intent)261     final public IBinder onBind(Intent intent) {
262         if (mInputMethod == null) {
263             mInputMethod = onCreateInputMethodInterface();
264         }
265         if (mInputMethodServiceInternal == null) {
266             mInputMethodServiceInternal = createInputMethodServiceInternal();
267         }
268         return new IInputMethodWrapper(mInputMethodServiceInternal, mInputMethod);
269     }
270 
271     /**
272      * Used to inject custom {@link InputMethodServiceInternal}.
273      *
274      * @return the {@link InputMethodServiceInternal} to be used.
275      */
276     @NonNull
createInputMethodServiceInternal()277     InputMethodServiceInternal createInputMethodServiceInternal() {
278         return new InputMethodServiceInternal() {
279             /**
280              * {@inheritDoc}
281              */
282             @NonNull
283             @Override
284             public Context getContext() {
285                 return AbstractInputMethodService.this;
286             }
287 
288             /**
289              * {@inheritDoc}
290              */
291             @Override
292             public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
293                 AbstractInputMethodService.this.dump(fd, fout, args);
294             }
295         };
296     }
297 
298     /**
299      * Implement this to handle trackball events on your input method.
300      *
301      * @param event The motion event being received.
302      * @return True if the event was handled in this function, false otherwise.
303      * @see android.view.View#onTrackballEvent(MotionEvent)
304      */
305     public boolean onTrackballEvent(MotionEvent event) {
306         return false;
307     }
308 
309     /**
310      * Implement this to handle generic motion events on your input method.
311      *
312      * @param event The motion event being received.
313      * @return True if the event was handled in this function, false otherwise.
314      * @see android.view.View#onGenericMotionEvent(MotionEvent)
315      */
316     public boolean onGenericMotionEvent(MotionEvent event) {
317         return false;
318     }
319 
320     /**
321      * @see InputMethodService#onShouldVerifyKeyEvent(KeyEvent)
322      */
323     @FlaggedApi(FLAG_VERIFY_KEY_EVENT)
324     public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) {
325         return false;
326     }
327 
328     /** @hide */
329     @Override
330     public final int getWindowType() {
331         return WindowManager.LayoutParams.TYPE_INPUT_METHOD;
332     }
333 
334     /** @hide */
335     @Override
336     @Nullable
337     public final Bundle getWindowContextOptions() {
338         return super.getWindowContextOptions();
339     }
340 
341     /** @hide */
342     @Override
343     public final int getInitialDisplayId() {
344         try {
345             return WindowManagerGlobal.getWindowManagerService().getImeDisplayId();
346         } catch (RemoteException e) {
347             throw e.rethrowFromSystemServer();
348         }
349     }
350 }
351