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