329 lines
12 KiB
Java
329 lines
12 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2007-2008 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||
|
* the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
|
* License for the specific language governing permissions and limitations under
|
||
|
* the License.
|
||
|
*/
|
||
|
|
||
|
package android.inputmethodservice;
|
||
|
|
||
|
import android.annotation.MainThread;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.content.Context;
|
||
|
import android.content.Intent;
|
||
|
import android.content.res.Configuration;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.RemoteException;
|
||
|
import android.view.KeyEvent;
|
||
|
import android.view.MotionEvent;
|
||
|
import android.view.WindowManager;
|
||
|
import android.view.WindowManagerGlobal;
|
||
|
import android.view.inputmethod.InputMethod;
|
||
|
import android.view.inputmethod.InputMethodSession;
|
||
|
import android.window.WindowProviderService;
|
||
|
|
||
|
import com.android.internal.annotations.VisibleForTesting;
|
||
|
|
||
|
import java.io.FileDescriptor;
|
||
|
import java.io.PrintWriter;
|
||
|
|
||
|
/**
|
||
|
* AbstractInputMethodService provides a abstract base class for input methods.
|
||
|
* Normal input method implementations will not derive from this directly,
|
||
|
* instead building on top of {@link InputMethodService} or another more
|
||
|
* complete base class. Be sure to read {@link InputMethod} for more
|
||
|
* information on the basics of writing input methods.
|
||
|
*
|
||
|
* <p>This class combines a Service (representing the input method component
|
||
|
* to the system with the InputMethod interface that input methods must
|
||
|
* implement. This base class takes care of reporting your InputMethod from
|
||
|
* the service when clients bind to it, but provides no standard implementation
|
||
|
* of the InputMethod interface itself. Derived classes must implement that
|
||
|
* interface.</p>
|
||
|
*
|
||
|
* <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft
|
||
|
* input may not be the entire screen. For example, some devices may support to show the soft input
|
||
|
* on only half of screen.</p>
|
||
|
*
|
||
|
* <p>In that case, moving the soft input from one half screen to another will trigger a
|
||
|
* {@link android.content.res.Resources} update to match the new {@link Configuration} and
|
||
|
* this {@link AbstractInputMethodService} may also receive a
|
||
|
* {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes
|
||
|
* </p>
|
||
|
*
|
||
|
* @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration)
|
||
|
* @see Context#isUiContext Context#isUiContext to see the concept of UI Context.
|
||
|
*/
|
||
|
public abstract class AbstractInputMethodService extends WindowProviderService
|
||
|
implements KeyEvent.Callback {
|
||
|
private InputMethod mInputMethod;
|
||
|
|
||
|
/**
|
||
|
* @return {@link InputMethod} instance returned from {@link #onCreateInputMethodInterface()}.
|
||
|
* {@code null} if {@link #onCreateInputMethodInterface()} is not yet called.
|
||
|
* @hide
|
||
|
*/
|
||
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||
|
@Nullable
|
||
|
public final InputMethod getInputMethodInternal() {
|
||
|
return mInputMethod;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Keep the strong reference to {@link InputMethodServiceInternal} to ensure that it will not be
|
||
|
* garbage-collected until {@link AbstractInputMethodService} gets garbage-collected.
|
||
|
*
|
||
|
* <p>This is necessary because {@link RemoteInputConnection} internally uses
|
||
|
* {@link java.lang.ref.WeakReference} to hold {@link InputMethodServiceInternal}.</p>
|
||
|
*/
|
||
|
@Nullable
|
||
|
private InputMethodServiceInternal mInputMethodServiceInternal;
|
||
|
|
||
|
final KeyEvent.DispatcherState mDispatcherState
|
||
|
= new KeyEvent.DispatcherState();
|
||
|
|
||
|
/**
|
||
|
* Base class for derived classes to implement their {@link InputMethod}
|
||
|
* interface. This takes care of basic maintenance of the input method,
|
||
|
* but most behavior must be implemented in a derived class.
|
||
|
*/
|
||
|
public abstract class AbstractInputMethodImpl implements InputMethod {
|
||
|
/**
|
||
|
* Instantiate a new client session for the input method, by calling
|
||
|
* back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
|
||
|
* AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
|
||
|
*/
|
||
|
@MainThread
|
||
|
public void createSession(SessionCallback callback) {
|
||
|
callback.sessionCreated(onCreateInputMethodSessionInterface());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Take care of enabling or disabling an existing session by calling its
|
||
|
* {@link AbstractInputMethodSessionImpl#revokeSelf()
|
||
|
* AbstractInputMethodSessionImpl.setEnabled()} method.
|
||
|
*/
|
||
|
@MainThread
|
||
|
public void setSessionEnabled(InputMethodSession session, boolean enabled) {
|
||
|
((AbstractInputMethodSessionImpl)session).setEnabled(enabled);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Take care of killing an existing session by calling its
|
||
|
* {@link AbstractInputMethodSessionImpl#revokeSelf()
|
||
|
* AbstractInputMethodSessionImpl.revokeSelf()} method.
|
||
|
*/
|
||
|
@MainThread
|
||
|
public void revokeSession(InputMethodSession session) {
|
||
|
((AbstractInputMethodSessionImpl)session).revokeSelf();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Base class for derived classes to implement their {@link InputMethodSession}
|
||
|
* interface. This takes care of basic maintenance of the session,
|
||
|
* but most behavior must be implemented in a derived class.
|
||
|
*/
|
||
|
public abstract class AbstractInputMethodSessionImpl implements InputMethodSession {
|
||
|
boolean mEnabled = true;
|
||
|
boolean mRevoked;
|
||
|
|
||
|
/**
|
||
|
* Check whether this session has been enabled by the system. If not
|
||
|
* enabled, you should not execute any calls on to it.
|
||
|
*/
|
||
|
public boolean isEnabled() {
|
||
|
return mEnabled;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether this session has been revoked by the system. Revoked
|
||
|
* session is also always disabled, so there is generally no need to
|
||
|
* explicitly check for this.
|
||
|
*/
|
||
|
public boolean isRevoked() {
|
||
|
return mRevoked;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Change the enabled state of the session. This only works if the
|
||
|
* session has not been revoked.
|
||
|
*/
|
||
|
public void setEnabled(boolean enabled) {
|
||
|
if (!mRevoked) {
|
||
|
mEnabled = enabled;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Revoke the session from the client. This disabled the session, and
|
||
|
* prevents it from ever being enabled again.
|
||
|
*/
|
||
|
public void revokeSelf() {
|
||
|
mRevoked = true;
|
||
|
mEnabled = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Take care of dispatching incoming key events to the appropriate
|
||
|
* callbacks on the service, and tell the client when this is done.
|
||
|
*/
|
||
|
@Override
|
||
|
public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
|
||
|
boolean handled = event.dispatch(AbstractInputMethodService.this,
|
||
|
mDispatcherState, this);
|
||
|
if (callback != null) {
|
||
|
callback.finishedEvent(seq, handled);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Take care of dispatching incoming trackball events to the appropriate
|
||
|
* callbacks on the service, and tell the client when this is done.
|
||
|
*/
|
||
|
@Override
|
||
|
public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
|
||
|
boolean handled = onTrackballEvent(event);
|
||
|
if (callback != null) {
|
||
|
callback.finishedEvent(seq, handled);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Take care of dispatching incoming generic motion events to the appropriate
|
||
|
* callbacks on the service, and tell the client when this is done.
|
||
|
*/
|
||
|
@Override
|
||
|
public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) {
|
||
|
boolean handled = onGenericMotionEvent(event);
|
||
|
if (callback != null) {
|
||
|
callback.finishedEvent(seq, handled);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
|
||
|
* for used for processing events from the target application.
|
||
|
* Normally you will not need to use this directly, but
|
||
|
* just use the standard high-level event callbacks like {@link #onKeyDown}.
|
||
|
*/
|
||
|
public KeyEvent.DispatcherState getKeyDispatcherState() {
|
||
|
return mDispatcherState;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called by the framework during initialization, when the InputMethod
|
||
|
* interface for this service needs to be created.
|
||
|
*/
|
||
|
public abstract AbstractInputMethodImpl onCreateInputMethodInterface();
|
||
|
|
||
|
/**
|
||
|
* Called by the framework when a new InputMethodSession interface is
|
||
|
* needed for a new client of the input method.
|
||
|
*/
|
||
|
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
|
||
|
|
||
|
/**
|
||
|
* Implement this to handle {@link android.os.Binder#dump Binder.dump()}
|
||
|
* calls on your input method.
|
||
|
*/
|
||
|
@Override
|
||
|
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
final public IBinder onBind(Intent intent) {
|
||
|
if (mInputMethod == null) {
|
||
|
mInputMethod = onCreateInputMethodInterface();
|
||
|
}
|
||
|
if (mInputMethodServiceInternal == null) {
|
||
|
mInputMethodServiceInternal = createInputMethodServiceInternal();
|
||
|
}
|
||
|
return new IInputMethodWrapper(mInputMethodServiceInternal, mInputMethod);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used to inject custom {@link InputMethodServiceInternal}.
|
||
|
*
|
||
|
* @return the {@link InputMethodServiceInternal} to be used.
|
||
|
*/
|
||
|
@NonNull
|
||
|
InputMethodServiceInternal createInputMethodServiceInternal() {
|
||
|
return new InputMethodServiceInternal() {
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@NonNull
|
||
|
@Override
|
||
|
public Context getContext() {
|
||
|
return AbstractInputMethodService.this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||
|
AbstractInputMethodService.this.dump(fd, fout, args);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Implement this to handle trackball events on your input method.
|
||
|
*
|
||
|
* @param event The motion event being received.
|
||
|
* @return True if the event was handled in this function, false otherwise.
|
||
|
* @see android.view.View#onTrackballEvent(MotionEvent)
|
||
|
*/
|
||
|
public boolean onTrackballEvent(MotionEvent event) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Implement this to handle generic motion events on your input method.
|
||
|
*
|
||
|
* @param event The motion event being received.
|
||
|
* @return True if the event was handled in this function, false otherwise.
|
||
|
* @see android.view.View#onGenericMotionEvent(MotionEvent)
|
||
|
*/
|
||
|
public boolean onGenericMotionEvent(MotionEvent event) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public final int getWindowType() {
|
||
|
return WindowManager.LayoutParams.TYPE_INPUT_METHOD;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
@Nullable
|
||
|
public final Bundle getWindowContextOptions() {
|
||
|
return super.getWindowContextOptions();
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public final int getInitialDisplayId() {
|
||
|
try {
|
||
|
return WindowManagerGlobal.getWindowManagerService().getImeDisplayId();
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
}
|