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