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