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