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