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