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.app.Service;
22 import android.content.Intent;
23 import android.os.IBinder;
24 import android.view.KeyEvent;
25 import android.view.MotionEvent;
26 import android.view.inputmethod.InputConnection;
27 import android.view.inputmethod.InputContentInfo;
28 import android.view.inputmethod.InputMethod;
29 import android.view.inputmethod.InputMethodSession;
30 
31 import java.io.FileDescriptor;
32 import java.io.PrintWriter;
33 
34 /**
35  * AbstractInputMethodService provides a abstract base class for input methods.
36  * Normal input method implementations will not derive from this directly,
37  * instead building on top of {@link InputMethodService} or another more
38  * complete base class.  Be sure to read {@link InputMethod} for more
39  * information on the basics of writing input methods.
40  *
41  * <p>This class combines a Service (representing the input method component
42  * to the system with the InputMethod interface that input methods must
43  * implement.  This base class takes care of reporting your InputMethod from
44  * the service when clients bind to it, but provides no standard implementation
45  * of the InputMethod interface itself.  Derived classes must implement that
46  * interface.
47  */
48 public abstract class AbstractInputMethodService extends Service
49         implements KeyEvent.Callback {
50     private InputMethod mInputMethod;
51 
52     final KeyEvent.DispatcherState mDispatcherState
53             = new KeyEvent.DispatcherState();
54 
55     /**
56      * Base class for derived classes to implement their {@link InputMethod}
57      * interface.  This takes care of basic maintenance of the input method,
58      * but most behavior must be implemented in a derived class.
59      */
60     public abstract class AbstractInputMethodImpl implements InputMethod {
61         /**
62          * Instantiate a new client session for the input method, by calling
63          * back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
64          * AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
65          */
66         @MainThread
createSession(SessionCallback callback)67         public void createSession(SessionCallback callback) {
68             callback.sessionCreated(onCreateInputMethodSessionInterface());
69         }
70 
71         /**
72          * Take care of enabling or disabling an existing session by calling its
73          * {@link AbstractInputMethodSessionImpl#revokeSelf()
74          * AbstractInputMethodSessionImpl.setEnabled()} method.
75          */
76         @MainThread
setSessionEnabled(InputMethodSession session, boolean enabled)77         public void setSessionEnabled(InputMethodSession session, boolean enabled) {
78             ((AbstractInputMethodSessionImpl)session).setEnabled(enabled);
79         }
80 
81         /**
82          * Take care of killing an existing session by calling its
83          * {@link AbstractInputMethodSessionImpl#revokeSelf()
84          * AbstractInputMethodSessionImpl.revokeSelf()} method.
85          */
86         @MainThread
revokeSession(InputMethodSession session)87         public void revokeSession(InputMethodSession session) {
88             ((AbstractInputMethodSessionImpl)session).revokeSelf();
89         }
90     }
91 
92     /**
93      * Base class for derived classes to implement their {@link InputMethodSession}
94      * interface.  This takes care of basic maintenance of the session,
95      * but most behavior must be implemented in a derived class.
96      */
97     public abstract class AbstractInputMethodSessionImpl implements InputMethodSession {
98         boolean mEnabled = true;
99         boolean mRevoked;
100 
101         /**
102          * Check whether this session has been enabled by the system.  If not
103          * enabled, you should not execute any calls on to it.
104          */
isEnabled()105         public boolean isEnabled() {
106             return mEnabled;
107         }
108 
109         /**
110          * Check whether this session has been revoked by the system.  Revoked
111          * session is also always disabled, so there is generally no need to
112          * explicitly check for this.
113          */
isRevoked()114         public boolean isRevoked() {
115             return mRevoked;
116         }
117 
118         /**
119          * Change the enabled state of the session.  This only works if the
120          * session has not been revoked.
121          */
setEnabled(boolean enabled)122         public void setEnabled(boolean enabled) {
123             if (!mRevoked) {
124                 mEnabled = enabled;
125             }
126         }
127 
128         /**
129          * Revoke the session from the client.  This disabled the session, and
130          * prevents it from ever being enabled again.
131          */
revokeSelf()132         public void revokeSelf() {
133             mRevoked = true;
134             mEnabled = false;
135         }
136 
137         /**
138          * Take care of dispatching incoming key events to the appropriate
139          * callbacks on the service, and tell the client when this is done.
140          */
141         @Override
dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback)142         public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
143             boolean handled = event.dispatch(AbstractInputMethodService.this,
144                     mDispatcherState, this);
145             if (callback != null) {
146                 callback.finishedEvent(seq, handled);
147             }
148         }
149 
150         /**
151          * Take care of dispatching incoming trackball events to the appropriate
152          * callbacks on the service, and tell the client when this is done.
153          */
154         @Override
dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback)155         public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
156             boolean handled = onTrackballEvent(event);
157             if (callback != null) {
158                 callback.finishedEvent(seq, handled);
159             }
160         }
161 
162         /**
163          * Take care of dispatching incoming generic motion events to the appropriate
164          * callbacks on the service, and tell the client when this is done.
165          */
166         @Override
dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback)167         public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) {
168             boolean handled = onGenericMotionEvent(event);
169             if (callback != null) {
170                 callback.finishedEvent(seq, handled);
171             }
172         }
173     }
174 
175     /**
176      * Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
177      * for used for processing events from the target application.
178      * Normally you will not need to use this directly, but
179      * just use the standard high-level event callbacks like {@link #onKeyDown}.
180      */
getKeyDispatcherState()181     public KeyEvent.DispatcherState getKeyDispatcherState() {
182         return mDispatcherState;
183     }
184 
185     /**
186      * Called by the framework during initialization, when the InputMethod
187      * interface for this service needs to be created.
188      */
onCreateInputMethodInterface()189     public abstract AbstractInputMethodImpl onCreateInputMethodInterface();
190 
191     /**
192      * Called by the framework when a new InputMethodSession interface is
193      * needed for a new client of the input method.
194      */
onCreateInputMethodSessionInterface()195     public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
196 
197     /**
198      * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
199      * calls on your input method.
200      */
201     @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)202     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
203     }
204 
205     @Override
onBind(Intent intent)206     final public IBinder onBind(Intent intent) {
207         if (mInputMethod == null) {
208             mInputMethod = onCreateInputMethodInterface();
209         }
210         return new IInputMethodWrapper(this, mInputMethod);
211     }
212 
213     /**
214      * Implement this to handle trackball events on your input method.
215      *
216      * @param event The motion event being received.
217      * @return True if the event was handled in this function, false otherwise.
218      * @see android.view.View#onTrackballEvent(MotionEvent)
219      */
onTrackballEvent(MotionEvent event)220     public boolean onTrackballEvent(MotionEvent event) {
221         return false;
222     }
223 
224     /**
225      * Implement this to handle generic motion events on your input method.
226      *
227      * @param event The motion event being received.
228      * @return True if the event was handled in this function, false otherwise.
229      * @see android.view.View#onGenericMotionEvent(MotionEvent)
230      */
onGenericMotionEvent(MotionEvent event)231     public boolean onGenericMotionEvent(MotionEvent event) {
232         return false;
233     }
234 
235     /**
236      * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
237      * permission to the content.
238      *
239      * <p>Default implementation does nothing.</p>
240      *
241      * @param inputContentInfo Content to be temporarily exposed from the input method to the
242      * application.
243      * This cannot be {@code null}.
244      * @param inputConnection {@link InputConnection} with which
245      * {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be
246      * called.
247      * @return {@code false} if we cannot allow a temporary access permission.
248      * @hide
249      */
exposeContent(@onNull InputContentInfo inputContentInfo, @NonNull InputConnection inputConnection)250     public void exposeContent(@NonNull InputContentInfo inputContentInfo,
251             @NonNull InputConnection inputConnection) {
252         return;
253     }
254 
255     /**
256      * Called when the user took some actions that should be taken into consideration to update the
257      * MRU list for input method rotation.
258      *
259      * @hide
260      */
notifyUserActionIfNecessary()261     public void notifyUserActionIfNecessary() {
262     }
263 }
264