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