summaryrefslogtreecommitdiffstats
path: root/core/java/android/view/inputmethod
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:44:00 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:44:00 -0800
commitd24b8183b93e781080b2c16c487e60d51c12da31 (patch)
treefbb89154858984eb8e41556da7e9433040d55cd4 /core/java/android/view/inputmethod
parentf1e484acb594a726fb57ad0ae4cfe902c7f35858 (diff)
downloadframeworks_base-d24b8183b93e781080b2c16c487e60d51c12da31.zip
frameworks_base-d24b8183b93e781080b2c16c487e60d51c12da31.tar.gz
frameworks_base-d24b8183b93e781080b2c16c487e60d51c12da31.tar.bz2
auto import from //branches/cupcake/...@130745
Diffstat (limited to 'core/java/android/view/inputmethod')
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java480
-rw-r--r--core/java/android/view/inputmethod/DefaultInputMethod.java239
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java47
-rw-r--r--core/java/android/view/inputmethod/ExtractedText.java20
-rw-r--r--core/java/android/view/inputmethod/ExtractedTextRequest.java9
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java45
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java107
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java19
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java300
-rw-r--r--core/java/android/view/inputmethod/MutableInputConnectionWrapper.java38
10 files changed, 761 insertions, 543 deletions
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index a6ce293..56c6c92 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -17,35 +17,365 @@
package android.view.inputmethod;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.NoCopySpan;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.method.MetaKeyKeyListener;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewRoot;
+class ComposingText implements NoCopySpan {
+}
+
/**
* Base class for implementors of the InputConnection interface, taking care
- * of implementing common system-oriented parts of the functionality.
+ * of most of the common behavior for providing a connection to an Editable.
+ * Implementors of this class will want to be sure to implement
+ * {@link #getEditable} to provide access to their own editable object.
*/
-public abstract class BaseInputConnection implements InputConnection {
+public class BaseInputConnection implements InputConnection {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "BaseInputConnection";
+ static final Object COMPOSING = new ComposingText();
+
final InputMethodManager mIMM;
final Handler mH;
final View mTargetView;
+ final boolean mDummyMode;
+
+ private Object[] mDefaultComposingSpans;
+
+ Editable mEditable;
+ KeyCharacterMap mKeyCharacterMap;
- BaseInputConnection(InputMethodManager mgr) {
+ BaseInputConnection(InputMethodManager mgr, boolean dummyMode) {
mIMM = mgr;
mTargetView = null;
mH = null;
+ mDummyMode = dummyMode;
}
- public BaseInputConnection(View targetView) {
+ public BaseInputConnection(View targetView, boolean dummyMode) {
mIMM = (InputMethodManager)targetView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
mH = targetView.getHandler();
mTargetView = targetView;
+ mDummyMode = dummyMode;
+ }
+
+ public static final void removeComposingSpans(Spannable text) {
+ text.removeSpan(COMPOSING);
+ Object[] sps = text.getSpans(0, text.length(), Object.class);
+ if (sps != null) {
+ for (int i=sps.length-1; i>=0; i--) {
+ Object o = sps[i];
+ if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) {
+ text.removeSpan(o);
+ }
+ }
+ }
+ }
+
+ public static void setComposingSpans(Spannable text) {
+ final Object[] sps = text.getSpans(0, text.length(), Object.class);
+ if (sps != null) {
+ for (int i=sps.length-1; i>=0; i--) {
+ final Object o = sps[i];
+ if (o == COMPOSING) {
+ text.removeSpan(o);
+ continue;
+ }
+ final int fl = text.getSpanFlags(o);
+ if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK))
+ != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
+ text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+ (fl&Spanned.SPAN_POINT_MARK_MASK)
+ | Spanned.SPAN_COMPOSING
+ | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ }
+
+ text.setSpan(COMPOSING, 0, text.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+ }
+
+ public static int getComposingSpanStart(Spannable text) {
+ return text.getSpanStart(COMPOSING);
+ }
+
+ public static int getComposingSpanEnd(Spannable text) {
+ return text.getSpanEnd(COMPOSING);
+ }
+
+ /**
+ * Return the target of edit operations. The default implementation
+ * returns its own fake editable that is just used for composing text;
+ * subclasses that are real text editors should override this and
+ * supply their own.
+ */
+ public Editable getEditable() {
+ if (mEditable == null) {
+ mEditable = Editable.Factory.getInstance().newEditable("");
+ Selection.setSelection(mEditable, 0);
+ }
+ return mEditable;
}
/**
+ * Default implementation does nothing.
+ */
+ public boolean beginBatchEdit() {
+ return false;
+ }
+
+ /**
+ * Default implementation does nothing.
+ */
+ public boolean endBatchEdit() {
+ return false;
+ }
+
+ /**
+ * Default implementation uses
+ * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
+ * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
+ */
+ public boolean clearMetaKeyStates(int states) {
+ final Editable content = getEditable();
+ if (content == null) return false;
+ MetaKeyKeyListener.clearMetaKeyState(content, states);
+ return true;
+ }
+
+ /**
+ * Default implementation does nothing.
+ */
+ public boolean commitCompletion(CompletionInfo text) {
+ return false;
+ }
+
+ /**
+ * Default implementation replaces any existing composing text with
+ * the given text. In addition, only if dummy mode, a key event is
+ * sent for the new text and the current editable buffer cleared.
+ */
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ if (DEBUG) Log.v(TAG, "commitText " + text);
+ replaceText(text, newCursorPosition, false);
+ sendCurrentText();
+ return true;
+ }
+
+ /**
+ * The default implementation performs the deletion around the current
+ * selection position of the editable text.
+ */
+ public boolean deleteSurroundingText(int leftLength, int rightLength) {
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength
+ + " / " + rightLength);
+ final Editable content = getEditable();
+ if (content == null) return false;
+
+ beginBatchEdit();
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ // ignore the composing text.
+ int ca = getComposingSpanStart(content);
+ int cb = getComposingSpanEnd(content);
+ if (cb < ca) {
+ int tmp = ca;
+ ca = cb;
+ cb = tmp;
+ }
+ if (ca != -1 && cb != -1) {
+ if (ca < a) a = ca;
+ if (cb > b) b = cb;
+ }
+
+ int deleted = 0;
+
+ if (leftLength > 0) {
+ int start = a - leftLength;
+ if (start < 0) start = 0;
+ content.delete(start, a);
+ deleted = a - start;
+ }
+
+ if (rightLength > 0) {
+ b = b - deleted;
+
+ int end = b + rightLength;
+ if (end > content.length()) end = content.length();
+
+ content.delete(b, end);
+ }
+
+ endBatchEdit();
+
+ return true;
+ }
+
+ /**
+ * The default implementation removes the composing state from the
+ * current editable text. In addition, only if dummy mode, a key event is
+ * sent for the new text and the current editable buffer cleared.
+ */
+ public boolean finishComposingText() {
+ if (DEBUG) Log.v(TAG, "finishComposingText");
+ final Editable content = getEditable();
+ if (content != null) {
+ beginBatchEdit();
+ removeComposingSpans(content);
+ endBatchEdit();
+ sendCurrentText();
+ }
+ return true;
+ }
+
+ /**
+ * The default implementation uses TextUtils.getCapsMode to get the
+ * cursor caps mode for the current selection position in the editable
+ * text, unless in dummy mode in which case 0 is always returned.
+ */
+ public int getCursorCapsMode(int reqModes) {
+ if (mDummyMode) return 0;
+
+ final Editable content = getEditable();
+ if (content == null) return 0;
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ return TextUtils.getCapsMode(content, a, reqModes);
+ }
+
+ /**
+ * The default implementation always returns null.
+ */
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ return null;
+ }
+
+ /**
+ * The default implementation returns the given amount of text from the
+ * current cursor position in the buffer.
+ */
+ public CharSequence getTextBeforeCursor(int length, int flags) {
+ final Editable content = getEditable();
+ if (content == null) return null;
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ if (length > a) {
+ length = a;
+ }
+
+ if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+ return content.subSequence(a - length, a);
+ }
+ return TextUtils.substring(content, a - length, a);
+ }
+
+ /**
+ * The default implementation returns the given amount of text from the
+ * current cursor position in the buffer.
+ */
+ public CharSequence getTextAfterCursor(int length, int flags) {
+ final Editable content = getEditable();
+ if (content == null) return null;
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ if (b + length > content.length()) {
+ length = content.length() - b;
+ }
+
+
+ if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+ return content.subSequence(b, b + length);
+ }
+ return TextUtils.substring(content, b, b + length);
+ }
+
+ /**
+ * The default implementation does nothing.
+ */
+ public boolean performContextMenuAction(int id) {
+ return false;
+ }
+
+ /**
+ * The default implementation does nothing.
+ */
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return false;
+ }
+
+ /**
+ * The default implementation places the given text into the editable,
+ * replacing any existing composing text. The new text is marked as
+ * in a composing state with the composing style.
+ */
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ if (DEBUG) Log.v(TAG, "setComposingText " + text);
+ replaceText(text, newCursorPosition, true);
+ return true;
+ }
+
+ /**
+ * The default implementation changes the selection position in the
+ * current editable text.
+ */
+ public boolean setSelection(int start, int end) {
+ if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
+ final Editable content = getEditable();
+ if (content == null) return false;
+ Selection.setSelection(content, start, end);
+ return true;
+ }
+
+ /**
* Provides standard implementation for sending a key event to the window
* attached to the input connection's view.
*/
@@ -82,4 +412,144 @@ public abstract class BaseInputConnection implements InputConnection {
mIMM.updateStatusIcon(resId, packageName);
return true;
}
+
+ private void sendCurrentText() {
+ if (!mDummyMode) {
+ return;
+ }
+
+ Editable content = getEditable();
+ if (content != null) {
+ if (content.length() == 1) {
+ // If it's 1 character, we have a chance of being
+ // able to generate normal key events...
+ if (mKeyCharacterMap == null) {
+ mKeyCharacterMap = KeyCharacterMap.load(
+ KeyCharacterMap.BUILT_IN_KEYBOARD);
+ }
+ char[] chars = new char[1];
+ content.getChars(0, 1, chars, 0);
+ KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
+ if (events != null) {
+ for (int i=0; i<events.length; i++) {
+ if (DEBUG) Log.v(TAG, "Sending: " + events[i]);
+ sendKeyEvent(events[i]);
+ }
+ content.clear();
+ return;
+ }
+ }
+
+ // Otherwise, revert to the special key event containing
+ // the actual characters.
+ KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(),
+ content.toString(), KeyCharacterMap.BUILT_IN_KEYBOARD, 0);
+ sendKeyEvent(event);
+ content.clear();
+ }
+ }
+
+ private void replaceText(CharSequence text, int newCursorPosition,
+ boolean composing) {
+ final Editable content = getEditable();
+ if (content == null) {
+ return;
+ }
+
+ beginBatchEdit();
+
+ // delete composing text set previously.
+ int a = getComposingSpanStart(content);
+ int b = getComposingSpanEnd(content);
+
+ if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b);
+
+ if (b < a) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ if (a != -1 && b != -1) {
+ removeComposingSpans(content);
+ } else {
+ a = Selection.getSelectionStart(content);
+ b = Selection.getSelectionEnd(content);
+ if (a >=0 && b>= 0 && a != b) {
+ if (b < a) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+ }
+ }
+
+ if (composing) {
+ Spannable sp = null;
+ if (!(text instanceof Spannable)) {
+ sp = new SpannableStringBuilder(text);
+ text = sp;
+ if (mDefaultComposingSpans == null) {
+ Context context;
+ if (mTargetView != null) {
+ context = mTargetView.getContext();
+ } else if (mIMM.mServedView != null) {
+ context = mIMM.mServedView.getContext();
+ } else {
+ context = null;
+ }
+ if (context != null) {
+ TypedArray ta = context.getTheme()
+ .obtainStyledAttributes(new int[] {
+ com.android.internal.R.attr.candidatesTextStyleSpans
+ });
+ CharSequence style = ta.getText(0);
+ ta.recycle();
+ if (style != null && style instanceof Spanned) {
+ mDefaultComposingSpans = ((Spanned)style).getSpans(
+ 0, style.length(), Object.class);
+ }
+ }
+ }
+ if (mDefaultComposingSpans != null) {
+ for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
+ sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ } else {
+ sp = (Spannable)text;
+ }
+ setComposingSpans(sp);
+ }
+
+ // Adjust newCursorPosition to be relative the start of the text.
+ newCursorPosition += a;
+
+ if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \""
+ + text + "\", composing=" + composing
+ + ", type=" + text.getClass().getCanonicalName());
+
+ if (DEBUG) {
+ LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
+ lp.println("Current text:");
+ TextUtils.dumpSpans(content, lp, " ");
+ lp.println("Composing text:");
+ TextUtils.dumpSpans(text, lp, " ");
+ }
+
+ content.replace(a, b, text);
+ if (newCursorPosition < 0) newCursorPosition = 0;
+ if (newCursorPosition > content.length())
+ newCursorPosition = content.length();
+ Selection.setSelection(content, newCursorPosition);
+
+ if (DEBUG) {
+ LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
+ lp.println("Final text:");
+ TextUtils.dumpSpans(content, lp, " ");
+ }
+
+ endBatchEdit();
+ }
}
diff --git a/core/java/android/view/inputmethod/DefaultInputMethod.java b/core/java/android/view/inputmethod/DefaultInputMethod.java
deleted file mode 100644
index 073b01c..0000000
--- a/core/java/android/view/inputmethod/DefaultInputMethod.java
+++ /dev/null
@@ -1,239 +0,0 @@
-package android.view.inputmethod;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethod;
-import com.android.internal.view.IInputMethodCallback;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InputConnectionWrapper;
-
-/**
- * This is the default input method that runs in the same context of the
- * application that requests text input. It does nothing but returns false for
- * any key events, so that all key events will be processed by the key listener
- * of the focused text box.
- * {@hide}
- */
-public class DefaultInputMethod implements InputMethod, InputMethodSession {
- private static IInputMethod sInstance = new SimpleInputMethod(
- new DefaultInputMethod());
-
- private static InputMethodInfo sProperty = new InputMethodInfo(
- "android.text.inputmethod", DefaultInputMethod.class.getName(),
- "Default", "android.text.inputmethod.defaultImeSettings");
-
- private InputConnection mInputConnection;
-
- public static IInputMethod getInstance() {
- return sInstance;
- }
-
- public static InputMethodInfo getMetaInfo() {
- return sProperty;
- }
-
- public void bindInput(InputBinding binding) {
- mInputConnection = binding.getConnection();
- }
-
- public void unbindInput() {
- }
-
- public void createSession(SessionCallback callback) {
- callback.sessionCreated(this);
- }
-
- public void setSessionEnabled(InputMethodSession session, boolean enabled) {
- }
-
- public void revokeSession(InputMethodSession session) {
- }
-
- public void finishInput() {
- mInputConnection.hideStatusIcon();
- }
-
- public void displayCompletions(CompletionInfo[] completions) {
- }
-
- public void updateExtractedText(int token, ExtractedText text) {
- }
-
- public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
- }
-
- public void updateCursor(Rect newCursor) {
- }
-
- public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
- callback.finishedEvent(seq, false);
- }
-
- public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
- callback.finishedEvent(seq, false);
- }
-
- public void restartInput(EditorInfo attribute) {
- }
-
- public void attachToken(IBinder token) {
- }
-
- public void startInput(EditorInfo attribute) {
- mInputConnection
- .showStatusIcon("android", com.android.internal.R.drawable.ime_qwerty);
- }
-
- public void appPrivateCommand(String action, Bundle data) {
- }
-
- public void hideSoftInput() {
- }
-
- public void showSoftInput(int flags) {
- }
-}
-
-// ----------------------------------------------------------------------
-
-class SimpleInputMethod extends IInputMethod.Stub {
- final InputMethod mInputMethod;
-
- static class Session extends IInputMethodSession.Stub {
- final InputMethodSession mSession;
-
- Session(InputMethodSession session) {
- mSession = session;
- }
-
- public void finishInput() {
- mSession.finishInput();
- }
-
- public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
- mSession.updateSelection(oldSelStart, oldSelEnd,
- newSelStart, newSelEnd, candidatesStart, candidatesEnd);
- }
-
- public void updateCursor(Rect newCursor) {
- mSession.updateCursor(newCursor);
- }
-
- static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
- final IInputMethodCallback mCb;
- InputMethodEventCallbackWrapper(IInputMethodCallback cb) {
- mCb = cb;
- }
- public void finishedEvent(int seq, boolean handled) {
- try {
- mCb.finishedEvent(seq, handled);
- } catch (RemoteException e) {
- }
- }
- }
-
- public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
- mSession.dispatchKeyEvent(seq, event,
- new InputMethodEventCallbackWrapper(callback));
- }
-
- public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) {
- mSession.dispatchTrackballEvent(seq, event,
- new InputMethodEventCallbackWrapper(callback));
- }
-
- public void displayCompletions(CompletionInfo[] completions) {
- mSession.displayCompletions(completions);
- }
-
- public void updateExtractedText(int token, ExtractedText text) {
- mSession.updateExtractedText(token, text);
- }
-
- public void appPrivateCommand(String action, Bundle data) {
- mSession.appPrivateCommand(action, data);
- }
- }
-
- public SimpleInputMethod(InputMethod inputMethod) {
- mInputMethod = inputMethod;
- }
-
- public InputMethod getInternalInputMethod() {
- return mInputMethod;
- }
-
- public void attachToken(IBinder token) {
- mInputMethod.attachToken(token);
- }
-
- public void bindInput(InputBinding binding) {
- InputConnectionWrapper ic = new InputConnectionWrapper(
- IInputContext.Stub.asInterface(binding.getConnectionToken()));
- InputBinding nu = new InputBinding(ic, binding);
- mInputMethod.bindInput(nu);
- }
-
- public void unbindInput() {
- mInputMethod.unbindInput();
- }
-
- public void restartInput(EditorInfo attribute) {
- mInputMethod.restartInput(attribute);
- }
-
- public void startInput(EditorInfo attribute) {
- mInputMethod.startInput(attribute);
- }
-
- static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
- final IInputMethodCallback mCb;
- InputMethodSessionCallbackWrapper(IInputMethodCallback cb) {
- mCb = cb;
- }
-
- public void sessionCreated(InputMethodSession session) {
- try {
- mCb.sessionCreated(new Session(session));
- } catch (RemoteException e) {
- }
- }
- }
-
- public void createSession(IInputMethodCallback callback) throws RemoteException {
- mInputMethod.createSession(new InputMethodSessionCallbackWrapper(callback));
- }
-
- public void setSessionEnabled(IInputMethodSession session, boolean enabled) throws RemoteException {
- try {
- InputMethodSession ls = ((Session)session).mSession;
- mInputMethod.setSessionEnabled(ls, enabled);
- } catch (ClassCastException e) {
- Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e);
- }
- }
-
- public void revokeSession(IInputMethodSession session) throws RemoteException {
- try {
- InputMethodSession ls = ((Session)session).mSession;
- mInputMethod.revokeSession(ls);
- } catch (ClassCastException e) {
- Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e);
- }
- }
-
- public void showSoftInput(boolean blah) {
- }
-
- public void hideSoftInput() {
- }
-}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c050691..b2f26d7 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -5,6 +5,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.InputType;
import android.text.TextUtils;
+import android.util.Printer;
/**
* An EditorInfo describes several attributes of a text editing object
@@ -21,7 +22,7 @@ public class EditorInfo implements InputType, Parcelable {
* @see #TYPE_MASK_VARIATION
* @see #TYPE_MASK_FLAGS
*/
- public int inputType = TYPE_CLASS_TEXT;
+ public int inputType = TYPE_NULL;
/**
* A string supplying additional information about the content type that
@@ -71,6 +72,26 @@ public class EditorInfo implements InputType, Parcelable {
public CharSequence label;
/**
+ * Name of the package that owns this editor.
+ */
+ public String packageName;
+
+ /**
+ * Identifier for the editor's field. This is optional, and may be
+ * 0. By default it is filled in with the result of
+ * {@link android.view.View#getId() View.getId()} on the View that
+ * is being edited.
+ */
+ public int fieldId;
+
+ /**
+ * Additional name for the editor's field. This can supply additional
+ * name information for the field. By default it is null. The actual
+ * contents have no meaning.
+ */
+ public String fieldName;
+
+ /**
* Any extra data to supply to the input method. This is for extended
* communication with specific input methods; the name fields in the
* bundle should be scoped (such as "com.mydomain.im.SOME_FIELD") so
@@ -81,6 +102,24 @@ public class EditorInfo implements InputType, Parcelable {
public Bundle extras;
/**
+ * Write debug output of this object.
+ */
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "inputType=0x" + Integer.toHexString(inputType)
+ + " privateContentType=" + privateContentType);
+ pw.println(prefix + "initialSelStart=" + initialSelStart
+ + " initialSelEnd=" + initialSelEnd
+ + " initialCapsMode=0x"
+ + Integer.toHexString(initialCapsMode));
+ pw.println(prefix + "hintText=" + hintText
+ + " label=" + label);
+ pw.println(prefix + "packageName=" + packageName
+ + " fieldId=" + fieldId
+ + " fieldName=" + fieldName);
+ pw.println(prefix + "extras=" + extras);
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -94,6 +133,9 @@ public class EditorInfo implements InputType, Parcelable {
dest.writeInt(initialCapsMode);
TextUtils.writeToParcel(hintText, dest, flags);
TextUtils.writeToParcel(label, dest, flags);
+ dest.writeString(packageName);
+ dest.writeInt(fieldId);
+ dest.writeString(fieldName);
dest.writeBundle(extras);
}
@@ -110,6 +152,9 @@ public class EditorInfo implements InputType, Parcelable {
res.initialCapsMode = source.readInt();
res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ res.packageName = source.readString();
+ res.fieldId = source.readInt();
+ res.fieldName = source.readString();
res.extras = source.readBundle();
return res;
}
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
index 0ca3c79..e5d3cae 100644
--- a/core/java/android/view/inputmethod/ExtractedText.java
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -19,6 +19,22 @@ public class ExtractedText implements Parcelable {
public int startOffset;
/**
+ * If the content is a report of a partial text change, this is the
+ * offset where the change starts and it runs until
+ * {@link #partialEndOffset}. If the content is the full text, this
+ * field is -1.
+ */
+ public int partialStartOffset;
+
+ /**
+ * If the content is a report of a partial text change, this is the offset
+ * where the change ends. Note that the actual text may be larger or
+ * smaller than the difference between this and {@link #partialEndOffset},
+ * meaning a reduction or increase, respectively, in the total text.
+ */
+ public int partialEndOffset;
+
+ /**
* The offset where the selection currently starts within the extracted
* text. The real selection start position is at
* <var>startOffset</var>+<var>selectionStart</var>.
@@ -52,6 +68,8 @@ public class ExtractedText implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
TextUtils.writeToParcel(text, dest, flags);
dest.writeInt(startOffset);
+ dest.writeInt(partialStartOffset);
+ dest.writeInt(partialEndOffset);
dest.writeInt(selectionStart);
dest.writeInt(selectionEnd);
dest.writeInt(flags);
@@ -65,6 +83,8 @@ public class ExtractedText implements Parcelable {
ExtractedText res = new ExtractedText();
res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.startOffset = source.readInt();
+ res.partialStartOffset = source.readInt();
+ res.partialEndOffset = source.readInt();
res.selectionStart = source.readInt();
res.selectionEnd = source.readInt();
res.flags = source.readInt();
diff --git a/core/java/android/view/inputmethod/ExtractedTextRequest.java b/core/java/android/view/inputmethod/ExtractedTextRequest.java
index d962329..e84b094 100644
--- a/core/java/android/view/inputmethod/ExtractedTextRequest.java
+++ b/core/java/android/view/inputmethod/ExtractedTextRequest.java
@@ -16,6 +16,13 @@ public class ExtractedTextRequest implements Parcelable {
public int token;
/**
+ * Additional request flags, having the same possible values as the
+ * flags parameter of {@link InputConnection#getTextBeforeCursor
+ * InputConnection.getTextBeforeCursor()}.
+ */
+ public int flags;
+
+ /**
* Hint for the maximum number of lines to return.
*/
public int hintMaxLines;
@@ -33,6 +40,7 @@ public class ExtractedTextRequest implements Parcelable {
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(token);
+ dest.writeInt(this.flags);
dest.writeInt(hintMaxLines);
dest.writeInt(hintMaxChars);
}
@@ -45,6 +53,7 @@ public class ExtractedTextRequest implements Parcelable {
public ExtractedTextRequest createFromParcel(Parcel source) {
ExtractedTextRequest res = new ExtractedTextRequest();
res.token = source.readInt();
+ res.flags = source.readInt();
res.hintMaxLines = source.readInt();
res.hintMaxChars = source.readInt();
return res;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index bd7b050..8c30d3f 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -32,6 +32,21 @@ import android.view.KeyEvent;
*/
public interface InputConnection {
/**
+ * Flag for use with {@link #getTextAfterCursor} and
+ * {@link #getTextBeforeCursor} to have style information returned along
+ * with the text. If not set, you will receive only the raw text. If
+ * set, you may receive a complex CharSequence of both text and style
+ * spans.
+ */
+ static final int GET_TEXT_WITH_STYLES = 0x0001;
+
+ /**
+ * Flag for use with {@link #getExtractedText} to indicate you would
+ * like to receive updates when the extracted text changes.
+ */
+ public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
+
+ /**
* Get <var>n</var> characters of text before the current cursor position.
*
* <p>This method may fail either if the input connection has become invalid
@@ -40,11 +55,13 @@ public interface InputConnection {
* In either case, a null is returned.
*
* @param n The expected length of the text.
+ * @param flags Supplies additional options controlling how the text is
+ * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
*
* @return Returns the text before the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextBeforeCursor(int n);
+ public CharSequence getTextBeforeCursor(int n, int flags);
/**
* Get <var>n</var> characters of text after the current cursor position.
@@ -55,11 +72,13 @@ public interface InputConnection {
* In either case, a null is returned.
*
* @param n The expected length of the text.
+ * @param flags Supplies additional options controlling how the text is
+ * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
*
* @return Returns the text after the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextAfterCursor(int n);
+ public CharSequence getTextAfterCursor(int n, int flags);
/**
* Retrieve the current capitalization mode in effect at the current
@@ -82,8 +101,6 @@ public interface InputConnection {
*/
public int getCursorCapsMode(int reqModes);
- public static final int EXTRACTED_TEXT_MONITOR = 0x0001;
-
/**
* Retrieve the current text in the input connection's editor, and monitor
* for any changes to it. This function returns with the current text,
@@ -97,7 +114,7 @@ public interface InputConnection {
*
* @param request Description of how the text should be returned.
* @param flags Additional options to control the client, either 0 or
- * {@link #EXTRACTED_TEXT_MONITOR}.
+ * {@link #GET_EXTRACTED_TEXT_MONITOR}.
*
* @return Returns an ExtractedText object describing the state of the
* text view and containing the extracted text itself.
@@ -141,7 +158,7 @@ public interface InputConnection {
/**
* Have the text editor finish whatever composing text is currently
- * active. This simple leaves the text as-is, removing any special
+ * active. This simply leaves the text as-is, removing any special
* composing styling or other state that was around it. The cursor
* position remains unchanged.
*/
@@ -177,6 +194,22 @@ public interface InputConnection {
public boolean commitCompletion(CompletionInfo text);
/**
+ * Set the selection of the text editor. To set the cursor position,
+ * start and end should have the same value.
+ */
+ public boolean setSelection(int start, int end);
+
+ /**
+ * Perform a context menu action on the field. The given id may be one of:
+ * {@link android.R.id#selectAll},
+ * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText},
+ * {@link android.R.id#cut}, {@link android.R.id#copy},
+ * {@link android.R.id#paste}, {@link android.R.id#copyUrl},
+ * or {@link android.R.id#switchInputMethod}
+ */
+ public boolean performContextMenuAction(int id);
+
+ /**
* Tell the editor that you are starting a batch of editor operations.
* The editor will try to avoid sending you updates about its state
* until {@link #endBatchEdit} is called.
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
deleted file mode 100644
index f65b2a1..0000000
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.view.inputmethod;
-
-import android.os.Bundle;
-import android.view.KeyEvent;
-
-/**
- * Wrapper around InputConnection interface, calling through to another
- * implementation of it.
- */
-public class InputConnectionWrapper implements InputConnection {
- InputConnection mBase;
-
- /**
- * Create a new wrapper around an existing InputConnection implementation.
- */
- public InputConnectionWrapper(InputConnection base) {
- mBase = base;
- }
-
- /**
- * Return the base InputConnection that this class is wrapping.
- */
- InputConnection getBase() {
- return mBase;
- }
-
- public CharSequence getTextBeforeCursor(int n) {
- return mBase.getTextBeforeCursor(n);
- }
-
- public CharSequence getTextAfterCursor(int n) {
- return mBase.getTextAfterCursor(n);
- }
-
- public int getCursorCapsMode(int reqModes) {
- return mBase.getCursorCapsMode(reqModes);
- }
-
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags) {
- return mBase.getExtractedText(request, flags);
- }
-
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return mBase.deleteSurroundingText(leftLength, rightLength);
- }
-
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return mBase.setComposingText(text, newCursorPosition);
- }
-
- public boolean finishComposingText() {
- return mBase.finishComposingText();
- }
-
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return mBase.commitText(text, newCursorPosition);
- }
-
- public boolean commitCompletion(CompletionInfo text) {
- return mBase.commitCompletion(text);
- }
-
- public boolean beginBatchEdit() {
- return mBase.beginBatchEdit();
- }
-
- public boolean endBatchEdit() {
- return mBase.endBatchEdit();
- }
-
- public boolean sendKeyEvent(KeyEvent event) {
- return mBase.sendKeyEvent(event);
- }
-
- public boolean clearMetaKeyStates(int states) {
- return mBase.clearMetaKeyStates(states);
- }
-
- public boolean performPrivateCommand(String action, Bundle data) {
- return mBase.performPrivateCommand(action, data);
- }
-
- public boolean showStatusIcon(String packageName, int resId) {
- return mBase.showStatusIcon(packageName, resId);
- }
-
- public boolean hideStatusIcon() {
- return mBase.hideStatusIcon();
- }
-}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index c0e6590..740dca8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -113,12 +113,15 @@ public interface InputMethod {
* is ready for this input method to process received events and send result
* text back to the application.
*
- * @param attribute The attribute of the text box (typically, a EditText)
+ * @param inputConnection Optional specific input connection for
+ * communicating with the text box; if null, you should use the generic
+ * bound input connection.
+ * @param info Information about the text box (typically, an EditText)
* that requests input.
*
* @see EditorInfo
*/
- public void startInput(EditorInfo attribute);
+ public void startInput(InputConnection inputConnection, EditorInfo info);
/**
* This method is called when the state of this input method needs to be
@@ -128,12 +131,15 @@ public interface InputMethod {
* Typically, this method is called when the input focus is moved from one
* text box to another.
*
+ * @param inputConnection Optional specific input connection for
+ * communicating with the text box; if null, you should use the generic
+ * bound input connection.
* @param attribute The attribute of the text box (typically, a EditText)
* that requests input.
*
* @see EditorInfo
*/
- public void restartInput(EditorInfo attribute);
+ public void restartInput(InputConnection inputConnection, EditorInfo attribute);
/**
* Create a new {@link InputMethodSession} that can be handed to client
@@ -173,6 +179,13 @@ public interface InputMethod {
public static final int SHOW_EXPLICIT = 0x00001;
/**
+ * Flag for {@link #showSoftInput(int)}: this show has been forced to
+ * happen by the user. If set, the input method should remain visible
+ * until deliberated dismissed by the user in its UI.
+ */
+ public static final int SHOW_FORCED = 0x00002;
+
+ /**
* Request that any soft input part of the input method be shown to the user.
*
* @param flags Provide additional information about the show request.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a9a9594..99d5aa5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -26,11 +26,14 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewRoot;
+import com.android.internal.os.HandlerCaller;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodCallback;
@@ -39,7 +42,11 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Central system API to the overall input method framework (IMF) architecture,
@@ -199,8 +206,7 @@ public final class InputMethodManager {
// global lock.
final H mH;
- // The currently active input connection.
- final MutableInputConnectionWrapper mInputConnectionWrapper;
+ // Our generic input connection if the current target does not have its own.
final IInputContext mIInputContext;
/**
@@ -208,11 +214,6 @@ public final class InputMethodManager {
*/
boolean mActive = false;
- /**
- * The current base input connection, used when mActive is true.
- */
- InputConnection mCurrentInputConnection;
-
// -----------------------------------------------------------
/**
@@ -270,7 +271,7 @@ public final class InputMethodManager {
// -----------------------------------------------------------
- static final int MSG_CHECK_FOCUS = 1;
+ static final int MSG_DUMP = 1;
class H extends Handler {
H(Looper looper) {
@@ -280,85 +281,55 @@ public final class InputMethodManager {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_CHECK_FOCUS:
- checkFocus();
+ case MSG_DUMP: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ try {
+ doDump((FileDescriptor)args.arg1,
+ (PrintWriter)args.arg2, (String[])args.arg3);
+ } catch (RuntimeException e) {
+ ((PrintWriter)args.arg2).println("Exception: " + e);
+ }
+ synchronized (args.arg4) {
+ ((CountDownLatch)args.arg4).countDown();
+ }
return;
+ }
}
}
}
- static class NoOpInputConnection implements InputConnection {
-
- public boolean clearMetaKeyStates(int states) {
- return false;
- }
-
- public boolean beginBatchEdit() {
- return false;
- }
-
- public boolean endBatchEdit() {
- return false;
- }
-
- public boolean commitCompletion(CompletionInfo text) {
- return false;
- }
-
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return false;
- }
-
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return false;
- }
-
- public int getCursorCapsMode(int reqModes) {
- return 0;
- }
-
- public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
- return null;
- }
-
- public CharSequence getTextAfterCursor(int n) {
- return null;
+ class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
+ public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
+ super(mainLooper, conn);
}
- public CharSequence getTextBeforeCursor(int n) {
- return null;
- }
-
- public boolean hideStatusIcon() {
- return false;
- }
-
- public boolean performPrivateCommand(String action, Bundle data) {
- return false;
- }
-
- public boolean sendKeyEvent(KeyEvent event) {
- return false;
- }
-
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return false;
- }
-
- public boolean finishComposingText() {
- return false;
- }
-
- public boolean showStatusIcon(String packageName, int resId) {
- return false;
+ public boolean isActive() {
+ return mActive;
}
}
- final NoOpInputConnection mNoOpInputConnection = new NoOpInputConnection();
-
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
- public void setUsingInputMethod(boolean state) {
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ // No need to check for dump permission, since we only give this
+ // interface to the system.
+ CountDownLatch latch = new CountDownLatch(1);
+ HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
+ sargs.arg1 = fd;
+ sargs.arg2 = fout;
+ sargs.arg3 = args;
+ sargs.arg4 = latch;
+ mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
+ try {
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ fout.println("Timeout waiting for dump");
+ }
+ } catch (InterruptedException e) {
+ fout.println("Interrupted waiting for dump");
+ }
+ }
+
+ public void setUsingInputMethod(boolean state) {
}
public void onBindMethod(InputBindResult res) {
@@ -403,62 +374,17 @@ public final class InputMethodManager {
public void setActive(boolean active) {
mActive = active;
- mInputConnectionWrapper.setBaseInputConnection(active
- ? mCurrentInputConnection : mNoOpInputConnection);
}
};
- final InputConnection mDummyInputConnection = new BaseInputConnection(this) {
- public boolean beginBatchEdit() {
- return false;
- }
- public boolean endBatchEdit() {
- return false;
- }
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return false;
- }
- public boolean commitCompletion(CompletionInfo text) {
- return false;
- }
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return false;
- }
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags) {
- return null;
- }
- public CharSequence getTextAfterCursor(int n) {
- return null;
- }
- public CharSequence getTextBeforeCursor(int n) {
- return null;
- }
- public int getCursorCapsMode(int reqModes) {
- return 0;
- }
- public boolean clearMetaKeyStates(int states) {
- return false;
- }
- public boolean performPrivateCommand(String action, Bundle data) {
- return false;
- }
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return false;
- }
- public boolean finishComposingText() {
- return false;
- }
- };
+ final InputConnection mDummyInputConnection = new BaseInputConnection(this, true);
InputMethodManager(IInputMethodManager service, Looper looper) {
mService = service;
mMainLooper = looper;
mH = new H(looper);
- mInputConnectionWrapper = new MutableInputConnectionWrapper(mNoOpInputConnection);
- mIInputContext = new IInputConnectionWrapper(looper,
- mInputConnectionWrapper);
- setCurrentInputConnection(mDummyInputConnection);
+ mIInputContext = new ControlledInputConnectionWrapper(looper,
+ mDummyInputConnection);
if (mInstance == null) {
mInstance = this;
@@ -563,22 +489,12 @@ public final class InputMethodManager {
}
/**
- * Record the desired input connection, but only set it if mActive is true.
- */
- void setCurrentInputConnection(InputConnection connection) {
- mCurrentInputConnection = connection;
- mInputConnectionWrapper.setBaseInputConnection(mActive
- ? connection : mNoOpInputConnection);
- }
-
- /**
* Reset all of the state associated with a served view being connected
* to an input method
*/
void clearConnectionLocked() {
mCurrentTextBoxAttribute = null;
mServedInputConnection = null;
- setCurrentInputConnection(mDummyInputConnection);
}
/**
@@ -659,13 +575,20 @@ public final class InputMethodManager {
}
/**
- * Flag for {@link #showSoftInput} to indicate that the this is an implicit
+ * Flag for {@link #showSoftInput} to indicate that this is an implicit
* request to show the input window, not as the result of a direct request
* by the user. The window may not be shown in this case.
*/
public static final int SHOW_IMPLICIT = 0x0001;
/**
+ * Flag for {@link #showSoftInput} to indicate that the user has forced
+ * the input method open (such as by long-pressing menu) so it should
+ * not be closed until they explicitly do so.
+ */
+ public static final int SHOW_FORCED = 0x0002;
+
+ /**
* Explicitly request that the current input method's soft input area be
* shown to the user, if needed. Call this if the user interacts with
* your view in such a way that they have expressed they would like to
@@ -689,6 +612,14 @@ public final class InputMethodManager {
}
}
+ /** @hide */
+ public void showSoftInputUnchecked(int flags) {
+ try {
+ mService.showSoftInput(mClient, flags);
+ } catch (RemoteException e) {
+ }
+ }
+
/**
* Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
* input window should only be hidden if it was not explicitly shown
@@ -697,6 +628,13 @@ public final class InputMethodManager {
public static final int HIDE_IMPLICIT_ONLY = 0x0001;
/**
+ * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
+ * input window should normally be hidden, unless it was originally
+ * shown with {@link #SHOW_FORCED}.
+ */
+ public static final int HIDE_NOT_ALWAYS = 0x0002;
+
+ /**
* Request to hide the soft input window from the context of the window
* that is currently accepting input. This should be called as a result
* of the user doing some actually than fairly explicitly requests to
@@ -779,6 +717,8 @@ public final class InputMethodManager {
// do its stuff.
// Life is good: let's hook everything up!
EditorInfo tba = new EditorInfo();
+ tba.packageName = view.getContext().getPackageName();
+ tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba);
if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
@@ -801,22 +741,23 @@ public final class InputMethodManager {
mCurrentTextBoxAttribute = tba;
mServedConnecting = false;
mServedInputConnection = ic;
+ IInputContext servedContext;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
- setCurrentInputConnection(ic);
+ servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
} else {
- setCurrentInputConnection(mDummyInputConnection);
+ servedContext = null;
}
try {
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ ic + " tba=" + tba + " initial=" + initial);
- InputBindResult res = mService.startInput(mClient, tba, initial,
- mCurMethod == null);
+ InputBindResult res = mService.startInput(mClient,
+ servedContext, tba, initial, mCurMethod == null);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
@@ -885,10 +826,20 @@ public final class InputMethodManager {
+ " winFocus=" + view.hasWindowFocus());
if (mServedView == view) {
ic = mServedInputConnection;
- if (view.hasWindowFocus()) {
+ // The following code would auto-hide the IME if we end up
+ // with no more views with focus. This can happen, however,
+ // whenever we go into touch mode, so it ends up hiding
+ // at times when we don't really want it to. For now it
+ // seems better to just turn it all off.
+ if (false && view.hasWindowFocus()) {
mLastServedView = view;
- mH.removeMessages(MSG_CHECK_FOCUS);
- mH.sendEmptyMessage(MSG_CHECK_FOCUS);
+ Handler vh = view.getHandler();
+ if (vh != null) {
+ // This will result in a call to checkFocus() below.
+ vh.removeMessages(ViewRoot.CHECK_FOCUS);
+ vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS,
+ view));
+ }
}
}
}
@@ -898,8 +849,14 @@ public final class InputMethodManager {
}
}
- void checkFocus() {
+ /**
+ * @hide
+ */
+ public void checkFocus(View view) {
synchronized (mH) {
+ if (view != mLastServedView) {
+ return;
+ }
if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
+ " last=" + mLastServedView);
if (mServedView == mLastServedView) {
@@ -915,7 +872,7 @@ public final class InputMethodManager {
void closeCurrentInput() {
try {
- mService.hideSoftInput(mClient, 0);
+ mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS);
} catch (RemoteException e) {
}
}
@@ -1006,6 +963,32 @@ public final class InputMethodManager {
}
/**
+ * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
+ * InputMethodSession.appPrivateCommand()} on the current Input Method.
+ * @param view Optional View that is sending the command, or null if
+ * you want to send the command regardless of the view that is attached
+ * to the input method.
+ * @param action Name of the command to be performed. This <em>must</em>
+ * be a scoped name, i.e. prefixed with a package name you own, so that
+ * different developers will not create conflicting commands.
+ * @param data Any data to include with the command.
+ */
+ public void sendAppPrivateCommand(View view, String action, Bundle data) {
+ synchronized (mH) {
+ if ((view != null && mServedView != view)
+ || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ return;
+ }
+ try {
+ if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
+ mCurMethod.appPrivateCommand(action, data);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ }
+ }
+
+ /**
* Force switch to a new input method component. This can only be called
* from the currently active input method, as validated by the given token.
* @param token Supplies the identifying token given to an input method
@@ -1048,7 +1031,7 @@ public final class InputMethodManager {
synchronized (mH) {
if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
- if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
+ if (mCurMethod == null) {
try {
callback.finishedEvent(seq, false);
} catch (RemoteException e) {
@@ -1116,4 +1099,33 @@ public final class InputMethodManager {
}
}
}
+
+ void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ final Printer p = new PrintWriterPrinter(fout);
+ p.println("Input method client state for " + this + ":");
+
+ p.println(" mService=" + mService);
+ p.println(" mMainLooper=" + mMainLooper);
+ p.println(" mIInputContext=" + mIInputContext);
+ p.println(" mActive=" + mActive
+ + " mBindSequence=" + mBindSequence
+ + " mCurId=" + mCurId);
+ p.println(" mCurMethod=" + mCurMethod);
+ p.println(" mServedView=" + mServedView);
+ p.println(" mLastServedView=" + mLastServedView);
+ p.println(" mServedConnecting=" + mServedConnecting);
+ if (mCurrentTextBoxAttribute != null) {
+ p.println(" mCurrentTextBoxAttribute:");
+ mCurrentTextBoxAttribute.dump(p, " ");
+ } else {
+ p.println(" mCurrentTextBoxAttribute: null");
+ }
+ p.println(" mServedInputConnection=" + mServedInputConnection);
+ p.println(" mCompletions=" + mCompletions);
+ p.println(" mCursorRect=" + mCursorRect);
+ p.println(" mCursorSelStart=" + mCursorSelStart
+ + " mCursorSelEnd=" + mCursorSelEnd
+ + " mCursorCandStart=" + mCursorCandStart
+ + " mCursorCandEnd=" + mCursorCandEnd);
+ }
}
diff --git a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java b/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java
deleted file mode 100644
index 025a059..0000000
--- a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.view.inputmethod;
-
-
-/**
- * Special version of {@link InputConnectionWrapper} that allows the base
- * input connection to be modified after it is initially set.
- */
-public class MutableInputConnectionWrapper extends InputConnectionWrapper {
- public MutableInputConnectionWrapper(InputConnection base) {
- super(base);
- }
-
- /**
- * Change the base InputConnection for this wrapper. All calls will then be
- * delegated to the base input connection.
- *
- * @param base The new base InputConnection for this wrapper.
- */
- public void setBaseInputConnection(InputConnection base) {
- mBase = base;
- }
-}