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