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