summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYohei Yukawa <yukawa@google.com>2014-07-11 13:31:08 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-07-10 21:43:01 +0000
commit7c940e94c97ce3c86f2d54ad567127fc11cc8074 (patch)
treef64b0b0df27a2887e00bd52d630efd8440b6f34e
parentc3b07a0c9ce9b1a2af644112e678f3963226fad2 (diff)
parent0023d0e0c4f5339b299d1eacbd4e7181c2fd271f (diff)
downloadframeworks_base-7c940e94c97ce3c86f2d54ad567127fc11cc8074.zip
frameworks_base-7c940e94c97ce3c86f2d54ad567127fc11cc8074.tar.gz
frameworks_base-7c940e94c97ce3c86f2d54ad567127fc11cc8074.tar.bz2
Merge "Polish new IME API for L part 2: CursorAnchorInfo"
-rw-r--r--api/current.txt25
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java25
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java15
-rw-r--r--core/java/android/view/inputmethod/CursorAnchorInfoRequest.aidl19
-rw-r--r--core/java/android/view/inputmethod/CursorAnchorInfoRequest.java203
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java11
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java6
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java140
-rw-r--r--core/java/android/widget/AbsListView.java6
-rw-r--r--core/java/android/widget/Editor.java5
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java34
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl3
-rw-r--r--core/java/com/android/internal/view/IInputContextCallback.aidl1
-rw-r--r--core/java/com/android/internal/view/IInputMethodClient.aidl1
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl1
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java35
-rw-r--r--core/java/com/android/internal/widget/EditableInputConnection.java15
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java30
18 files changed, 467 insertions, 108 deletions
diff --git a/api/current.txt b/api/current.txt
index 830a63e..c5fc314 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13324,7 +13324,6 @@ package android.inputmethodservice {
method public void setBackDisposition(int);
method public void setCandidatesView(android.view.View);
method public void setCandidatesViewShown(boolean);
- method public void setCursorAnchorMonitorMode(int);
method public void setExtractView(android.view.View);
method public void setExtractViewShown(boolean);
method public void setInputView(android.view.View);
@@ -13336,8 +13335,6 @@ package android.inputmethodservice {
field public static final int BACK_DISPOSITION_DEFAULT = 0; // 0x0
field public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // 0x2
field public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // 0x1
- field public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 1; // 0x1
- field public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0; // 0x0
}
public class InputMethodService.InputMethodImpl extends android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl {
@@ -34976,6 +34973,7 @@ package android.view.inputmethod {
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
method public static final void removeComposingSpans(android.text.Spannable);
method public boolean reportFullscreenMode(boolean);
+ method public int requestCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfoRequest);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
method public static void setComposingSpans(android.text.Spannable);
@@ -35041,6 +35039,25 @@ package android.view.inputmethod {
method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
}
+ public final class CursorAnchorInfoRequest implements android.os.Parcelable {
+ ctor public CursorAnchorInfoRequest(int, int);
+ ctor public CursorAnchorInfoRequest(android.os.Parcel);
+ method public int describeContents();
+ method public int getRequestFlags();
+ method public int getRequestType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 2; // 0x2
+ field public static final int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 1; // 0x1
+ field public static final int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 2; // 0x2
+ field public static final int FLAG_CURSOR_RECT_MONITOR = 1; // 0x1
+ field public static final int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 4; // 0x4
+ field public static final int RESULT_NOT_HANDLED = 0; // 0x0
+ field public static final int RESULT_SCHEDULED = 1; // 0x1
+ field public static final int TYPE_CURSOR_ANCHOR_INFO = 1; // 0x1
+ field public static final int TYPE_CURSOR_RECT = 2; // 0x2
+ }
+
public class EditorInfo implements android.text.InputType android.os.Parcelable {
ctor public EditorInfo();
method public int describeContents();
@@ -35138,6 +35155,7 @@ package android.view.inputmethod {
method public abstract boolean performEditorAction(int);
method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle);
method public abstract boolean reportFullscreenMode(boolean);
+ method public abstract int requestCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfoRequest);
method public abstract boolean sendKeyEvent(android.view.KeyEvent);
method public abstract boolean setComposingRegion(int, int);
method public abstract boolean setComposingText(java.lang.CharSequence, int);
@@ -35165,6 +35183,7 @@ package android.view.inputmethod {
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
method public boolean reportFullscreenMode(boolean);
+ method public int requestCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfoRequest);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
method public boolean setComposingText(java.lang.CharSequence, int);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 3417de1..8423d09 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -53,6 +53,7 @@ import android.view.WindowManager.BadTokenException;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -251,18 +252,6 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public static final int IME_VISIBLE = 0x2;
- /**
- * The IME does not require cursor/anchor position.
- */
- public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0x0;
-
- /**
- * Passing this flag into a call to {@link #setCursorAnchorMonitorMode(int)} will result in
- * the cursor rectangle being provided in screen coordinates to subsequent
- * {@link #onUpdateCursor(Rect)} callbacks.
- */
- public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 0x1;
-
InputMethodManager mImm;
int mTheme = 0;
@@ -1722,8 +1711,9 @@ public class InputMethodService extends AbstractInputMethodService {
* Called when the application has reported a new location of its text cursor. This is only
* called if explicitly requested by the input method. The default implementation does nothing.
* @param newCursor The new cursor position, in screen coordinates if the input method calls
- * {@link #setCursorAnchorMonitorMode} with {@link #CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT}.
- * Otherwise, this is in local coordinates.
+ * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} with
+ * {@link CursorAnchorInfoRequest#FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES}. Otherwise,
+ * this is in local coordinates.
*/
public void onUpdateCursor(Rect newCursor) {
// Intentionally empty
@@ -1741,13 +1731,6 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Update the cursor/anthor monitor mode.
- */
- public void setCursorAnchorMonitorMode(int monitorMode) {
- mImm.setCursorAnchorMonitorMode(mToken, monitorMode);
- }
-
- /**
* Close this input method's soft input area, removing it from the display.
* The input method will continue running, but the user can no longer use
* it to generate input by touching the screen.
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index e1f40b7..338f3d2 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -429,6 +429,21 @@ public class BaseInputConnection implements InputConnection {
}
/**
+ * The default implementation is responsible for handling
+ * {@link CursorAnchorInfoRequest#TYPE_CURSOR_RECT}. In fact, for derived classes, calling
+ * {@code super.requestCursorAnchorInfo(request)} is the only way to handle
+ * {@link CursorAnchorInfoRequest#TYPE_CURSOR_RECT}.
+ */
+ public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
+ if (request != null && mIMM != null &&
+ request.getRequestType() == CursorAnchorInfoRequest.TYPE_CURSOR_RECT) {
+ mIMM.setCursorRectMonitorMode(request.getRequestFlags());
+ return CursorAnchorInfoRequest.RESULT_SCHEDULED;
+ }
+ return CursorAnchorInfoRequest.RESULT_NOT_HANDLED;
+ }
+
+ /**
* 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.
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfoRequest.aidl b/core/java/android/view/inputmethod/CursorAnchorInfoRequest.aidl
new file mode 100644
index 0000000..41ef7cc6
--- /dev/null
+++ b/core/java/android/view/inputmethod/CursorAnchorInfoRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 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;
+
+parcelable CursorAnchorInfoRequest;
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfoRequest.java b/core/java/android/view/inputmethod/CursorAnchorInfoRequest.java
new file mode 100644
index 0000000..e4c94f2
--- /dev/null
+++ b/core/java/android/view/inputmethod/CursorAnchorInfoRequest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 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.inputmethodservice.InputMethodService;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+
+/**
+ * Used to enable or disable event notification for
+ * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}. This class is also used to
+ * enable {@link InputMethodService#onUpdateCursor(android.graphics.Rect)} for existing editors
+ * that have not supported {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}.
+ */
+public final class CursorAnchorInfoRequest implements Parcelable {
+ private final int mRequestType;
+ private final int mRequestFlags;
+
+ /**
+ * Not handled by the editor.
+ */
+ public static final int RESULT_NOT_HANDLED = 0x00;
+ /**
+ * Request is scheduled in the editor task queue.
+ */
+ public static final int RESULT_SCHEDULED = 0x01;
+
+ /**
+ * The request is for {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}.
+ * This mechanism is powerful enough to retrieve fine-grained positional information of
+ * characters in the editor.
+ */
+ public static final int TYPE_CURSOR_ANCHOR_INFO = 0x01;
+ /**
+ * The editor is requested to call
+ * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
+ * whenever cursor/anchor position is changed. To disable monitoring, call
+ * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with
+ * {@link #TYPE_CURSOR_ANCHOR_INFO} and this flag off.
+ * <p>
+ * This flag can be used together with {@link #FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE}.
+ * </p>
+ */
+ public static final int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 0x01;
+ /**
+ * The editor is requested to call
+ * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
+ * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
+ * used together with {@link #FLAG_CURSOR_ANCHOR_INFO_MONITOR}.
+ */
+ public static final int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 0x02;
+
+ /**
+ * The request is for {@link InputMethodService#onUpdateCursor(android.graphics.Rect)}. This
+ * mechanism has been available since API Level 3 (CUPCAKE) but only the cursor rectangle can
+ * be retrieved with this mechanism.
+ */
+ public static final int TYPE_CURSOR_RECT = 0x02;
+ /**
+ * The editor is requested to call
+ * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)}
+ * whenever the cursor position is changed. To disable monitoring, call
+ * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with
+ * {@link #TYPE_CURSOR_RECT} and this flag off.
+ * <p>
+ * This flag can be used together with {@link #FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES}.
+ * </p>
+ */
+ public static final int FLAG_CURSOR_RECT_MONITOR = 0x01;
+ /**
+ * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} should be
+ * called back in screen coordinates. To receive cursor position in local coordinates, call
+ * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with
+ * {@link #TYPE_CURSOR_RECT} and this flag off.
+ */
+ public static final int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 0x02;
+ /**
+ * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} should be
+ * called back in screen coordinates after coordinate conversion with {@link View#getMatrix()}.
+ * To disable coordinate conversion with {@link View#getMatrix()} again, call
+ * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} with
+ * {@link #TYPE_CURSOR_RECT} and this flag off.
+ *
+ * <p>
+ * The flag is ignored if {@link #FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES} is off.
+ * </p>
+ */
+ public static final int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 0x04;
+
+ /**
+ * Constructs the object with request type and type-specific flags.
+ *
+ * @param requestType the type of this request. Currently {@link #TYPE_CURSOR_ANCHOR_INFO} or
+ * {@link #TYPE_CURSOR_RECT} is supported.
+ * @param requestFlags the flags for the given request type.
+ */
+ public CursorAnchorInfoRequest(int requestType, int requestFlags) {
+ mRequestType = requestType;
+ mRequestFlags = requestFlags;
+ }
+
+ /**
+ * Used to make this class parcelable.
+ *
+ * @param source the parcel from which the object is unmarshalled.
+ */
+ public CursorAnchorInfoRequest(Parcel source) {
+ mRequestType = source.readInt();
+ mRequestFlags = source.readInt();
+ }
+
+ /**
+ * @return the type of this request.
+ */
+ public int getRequestType() {
+ return mRequestType;
+ }
+
+ /**
+ * @return the flags that are specific to the type of this request.
+ */
+ public int getRequestFlags() {
+ return mRequestFlags;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mRequestType);
+ dest.writeInt(mRequestFlags);
+ }
+
+ @Override
+ public int hashCode(){
+ return mRequestType * 31 + mRequestFlags;
+ }
+
+ @Override
+ public boolean equals(Object obj){
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof CursorAnchorInfoRequest)) {
+ return false;
+ }
+ final CursorAnchorInfoRequest that = (CursorAnchorInfoRequest) obj;
+ if (hashCode() != that.hashCode()) {
+ return false;
+ }
+ return mRequestType != that.mRequestType && mRequestFlags == that.mRequestFlags;
+ }
+
+ @Override
+ public String toString() {
+ return "CursorAnchorInfoRequest{mRequestType=" + mRequestType
+ + " mRequestFlags=" + mRequestFlags
+ + "}";
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<CursorAnchorInfoRequest> CREATOR =
+ new Parcelable.Creator<CursorAnchorInfoRequest>() {
+ @Override
+ public CursorAnchorInfoRequest createFromParcel(Parcel source) {
+ return new CursorAnchorInfoRequest(source);
+ }
+
+ @Override
+ public CursorAnchorInfoRequest[] newArray(int size) {
+ return new CursorAnchorInfoRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 3537aec..dff91dc 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -723,4 +723,15 @@ public interface InputConnection {
* valid.
*/
public boolean performPrivateCommand(String action, Bundle data);
+
+ /**
+ * Called by the IME to ask the editor for calling back
+ * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} to
+ * notify cursor/anchor locations.
+ *
+ * @param request the details of the request.
+ * @return a result code that depends on {@link CursorAnchorInfoRequest#getRequestType()}. See
+ * {@link CursorAnchorInfoRequest} for details.
+ */
+ public int requestCursorAnchorInfo(CursorAnchorInfoRequest request);
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index a48473e..c831d7c 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -125,4 +125,8 @@ public class InputConnectionWrapper implements InputConnection {
public boolean performPrivateCommand(String action, Bundle data) {
return mTarget.performPrivateCommand(action, data);
}
-}
+
+ public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
+ return mTarget.requestCursorAnchorInfo(request);
+ }
+ }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ace8808..623b5f9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -25,8 +25,9 @@ import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.Rect;
-import android.inputmethodservice.InputMethodService;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -312,8 +313,9 @@ public final class InputMethodManager {
CompletionInfo[] mCompletions;
// Cursor position on the screen.
- Rect mTmpCursorRect = new Rect();
+ Rect mNextCursorRect = new Rect();
Rect mCursorRect = new Rect();
+ RectF mTempRectF = new RectF();
int mCursorSelStart;
int mCursorSelEnd;
int mCursorCandStart;
@@ -348,8 +350,13 @@ public final class InputMethodManager {
*/
private final int[] mViewTopLeft = new int[2];
+ /**
+ * The matrix to convert the view location into screen coordinates in {@link #updateCursor}.
+ */
+ private final Matrix mViewToScreenMatrix = new Matrix();
+
// -----------------------------------------------------------
-
+
/**
* Sequence number of this binding, as returned by the server.
*/
@@ -365,10 +372,28 @@ public final class InputMethodManager {
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
+ private static final int CURSOR_RECT_MONITOR_MODE_NONE = 0x0;
+
+ private static final int CURSOR_RECT_MONITOR_FLAG_MASK =
+ CursorAnchorInfoRequest.FLAG_CURSOR_RECT_MONITOR |
+ CursorAnchorInfoRequest.FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES |
+ CursorAnchorInfoRequest.FLAG_CURSOR_RECT_WITH_VIEW_MATRIX;
+
+ private static final int CURSOR_ANCHOR_INFO_MONITOR_MODE_NONE = 0x0;
+
+ private static final int CURSOR_ANCHOR_INFO_MONITOR_FLAG_MASK =
+ CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_MONITOR |
+ CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE;
+
+ /**
+ * The monitor mode for {@link #updateCursor(View, int, int, int, int)}.
+ */
+ private int mCursorRectMonitorMode = CURSOR_RECT_MONITOR_MODE_NONE;
+
/**
- * The current cursor/anchor monitor mode.
+ * The monitor mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
*/
- int mCursorAnchorMonitorMode = InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_NONE;
+ private int mCursorAnchorInfoMonitorMode = CURSOR_ANCHOR_INFO_MONITOR_MODE_NONE;
final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
@@ -382,7 +407,6 @@ public final class InputMethodManager {
static final int MSG_SEND_INPUT_EVENT = 5;
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
- static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 8;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
class H extends Handler {
@@ -422,6 +446,9 @@ public final class InputMethodManager {
return;
}
+ mCursorAnchorInfoMonitorMode = CURSOR_ANCHOR_INFO_MONITOR_MODE_NONE;
+ mCursorRectMonitorMode = CURSOR_RECT_MONITOR_MODE_NONE;
+
setInputChannelLocked(res.channel);
mCurMethod = res.method;
mCurId = res.id;
@@ -514,15 +541,6 @@ public final class InputMethodManager {
finishedInputEvent(msg.arg1, false, false);
return;
}
- case MSG_SET_CURSOR_ANCHOR_MONITOR_MODE: {
- synchronized (mH) {
- mCursorAnchorMonitorMode = msg.arg1;
- // Clear the cache.
- mCursorRect.setEmpty();
- mCursorAnchorInfo = null;
- }
- return;
- }
case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
synchronized (mH) {
mNextUserActionNotificationSequenceNumber = msg.arg1;
@@ -594,11 +612,6 @@ public final class InputMethodManager {
}
@Override
- public void setCursorAnchorMonitorMode(int monitorMode) {
- mH.sendMessage(mH.obtainMessage(MSG_SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, 0));
- }
-
- @Override
public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
sequenceNumber, 0));
@@ -1535,37 +1548,45 @@ public final class InputMethodManager {
return false;
}
synchronized (mH) {
- return (mCursorAnchorMonitorMode &
- InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT) != 0;
+ return (mCursorRectMonitorMode & CursorAnchorInfoRequest.FLAG_CURSOR_RECT_MONITOR) != 0;
}
}
/**
- * Returns true if the current input method wants to receive the cursor rectangle in
- * screen coordinates rather than local coordinates in the attached view.
+ * Updates the result of {@link #isWatchingCursor(View)}.
*
* @hide
*/
- public boolean usesScreenCoordinatesForCursorLocked() {
- // {@link InputMethodService#CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT} also means
- // that {@link InputMethodService#onUpdateCursor} should provide the cursor rectangle
- // in screen coordinates rather than local coordinates.
- return (mCursorAnchorMonitorMode &
- InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT) != 0;
+ public void setCursorRectMonitorMode(int flags) {
+ synchronized (mH) {
+ mCursorRectMonitorMode = (CURSOR_RECT_MONITOR_FLAG_MASK & flags);
+ }
}
/**
- * Set cursor/anchor monitor mode via {@link com.android.server.InputMethodManagerService}.
- * This is an internal method for {@link android.inputmethodservice.InputMethodService} and
- * should never be used from IMEs and applications.
+ * Returns true if the current input method wants to be notified when cursor/anchor location
+ * is changed.
*
* @hide
*/
- public void setCursorAnchorMonitorMode(IBinder imeToken, int monitorMode) {
- try {
- mService.setCursorAnchorMonitorMode(imeToken, monitorMode);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
+ public boolean isCursorAnchorInfoEnabled() {
+ synchronized (mH) {
+ final boolean isImmediate = (mCursorAnchorInfoMonitorMode &
+ CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0;
+ final boolean isMonitoring = (mCursorAnchorInfoMonitorMode &
+ CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_MONITOR) != 0;
+ return isImmediate || isMonitoring;
+ }
+ }
+
+ /**
+ * Updates the result of {@link #isWatchingCursor(View)}.
+ *
+ * @hide
+ */
+ public void setCursorAnchorInfoMonitorMode(int flags) {
+ synchronized (mH) {
+ mCursorAnchorInfoMonitorMode = (CURSOR_ANCHOR_INFO_MONITOR_FLAG_MASK & flags);
}
}
@@ -1581,16 +1602,32 @@ public final class InputMethodManager {
return;
}
if (DEBUG) Log.d(TAG, "updateCursor");
- mTmpCursorRect.set(left, top, right, bottom);
- if (!Objects.equals(mCursorRect, mTmpCursorRect)) {
+ final boolean usesScreenCoordinates = (mCursorRectMonitorMode &
+ CursorAnchorInfoRequest.FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES) != 0;
+ if (usesScreenCoordinates) {
+ view.getLocationOnScreen(mViewTopLeft);
+ final Matrix viewMatrix = view.getMatrix();
+ final boolean usesViewMatrix = (viewMatrix != null) && ((mCursorRectMonitorMode &
+ CursorAnchorInfoRequest.FLAG_CURSOR_RECT_WITH_VIEW_MATRIX) != 0);
+ if (usesViewMatrix) {
+ mTempRectF.set(left, top, right, bottom);
+ mViewToScreenMatrix.set(viewMatrix);
+ mViewToScreenMatrix.postTranslate(mViewTopLeft[0], mViewTopLeft[1]);
+ mViewToScreenMatrix.mapRect(mTempRectF);
+ mNextCursorRect.set((int)mTempRectF.left, (int)mTempRectF.top,
+ (int)mTempRectF.right, (int)mTempRectF.bottom);
+ } else {
+ mNextCursorRect.set(left + mViewTopLeft[0], top + mViewTopLeft[1],
+ right + mViewTopLeft[0], bottom + mViewTopLeft[1]);
+ }
+ } else {
+ mNextCursorRect.set(left, top, right, bottom);
+ }
+ if (!Objects.equals(mCursorRect, mNextCursorRect)) {
+ if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mNextCursorRect);
try {
- if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
- mCursorRect.set(mTmpCursorRect);
- if (usesScreenCoordinatesForCursorLocked()) {
- view.getLocationOnScreen(mViewTopLeft);
- mTmpCursorRect.offset(mViewTopLeft[0], mViewTopLeft[1]);
- }
- mCurMethod.updateCursor(mTmpCursorRect);
+ mCurMethod.updateCursor(mNextCursorRect);
+ mCursorRect.set(mNextCursorRect);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
@@ -1613,7 +1650,11 @@ public final class InputMethodManager {
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
- if (Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) {
+ // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
+ // not been changed from the previous call.
+ final boolean isImmediate = (mCursorAnchorInfoMonitorMode &
+ CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0;
+ if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) {
Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" + cursorAnchorInfo);
return;
}
@@ -1621,6 +1662,9 @@ public final class InputMethodManager {
try {
mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
mCursorAnchorInfo = cursorAnchorInfo;
+ // Clear immediate bit (if any).
+ mCursorAnchorInfoMonitorMode &=
+ ~CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE;
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3a1f6ed..9701c6f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -68,6 +68,7 @@ import android.view.animation.LinearInterpolator;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -5696,6 +5697,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public boolean performPrivateCommand(String action, Bundle data) {
return getTarget().performPrivateCommand(action, data);
}
+
+ @Override
+ public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
+ return getTarget().requestCursorAnchorInfo(request);
+ }
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 170a316..66c4b81 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3027,8 +3027,11 @@ public class Editor {
if (null == imm) {
return;
}
+ if (!imm.isActive(mTextView)) {
+ return;
+ }
// Skip if the IME has not requested the cursor/anchor position.
- if (!imm.isWatchingCursor(mTextView)) {
+ if (!imm.isCursorAnchorInfoEnabled()) {
return;
}
Layout layout = mTextView.getLayout();
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index c792d78..897381d 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -25,6 +25,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
@@ -54,6 +55,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_REPORT_FULLSCREEN_MODE = 100;
private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
private static final int DO_CLEAR_META_KEY_STATES = 130;
+ private static final int DO_REQUEST_CURSOR_ANCHOR_INFO = 140;
private WeakReference<InputConnection> mInputConnection;
@@ -175,6 +177,11 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
}
+ public void requestCursorAnchorInfo(CursorAnchorInfoRequest request, int seq,
+ IInputContextCallback callback) {
+ dispatchMessage(obtainMessageOSC(DO_REQUEST_CURSOR_ANCHOR_INFO, request, seq, callback));
+ }
+
void dispatchMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
@@ -420,6 +427,23 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
(Bundle)args.arg2);
return;
}
+ case DO_REQUEST_CURSOR_ANCHOR_INFO: {
+ SomeArgs args = (SomeArgs)msg.obj;
+ try {
+ InputConnection ic = mInputConnection.get();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
+ args.callback.setRequestCursorAnchorInfoResult(0, args.seq);
+ return;
+ }
+ args.callback.setRequestCursorAnchorInfoResult(
+ ic.requestCursorAnchorInfo((CursorAnchorInfoRequest)args.arg1),
+ args.seq);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e);
+ }
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -449,7 +473,15 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
args.seq = seq;
return mH.obtainMessage(what, arg1, arg2, args);
}
-
+
+ Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) {
+ SomeArgs args = new SomeArgs();
+ args.arg1 = arg1;
+ args.callback = callback;
+ args.seq = seq;
+ return mH.obtainMessage(what, 0, 0, args);
+ }
+
Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
IInputContextCallback callback) {
SomeArgs args = new SomeArgs();
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 719a24f..c06596a 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.ExtractedTextRequest;
import com.android.internal.view.IInputContextCallback;
@@ -73,4 +74,6 @@ import com.android.internal.view.IInputContextCallback;
void getSelectedText(int flags, int seq, IInputContextCallback callback);
+ void requestCursorAnchorInfo(in CursorAnchorInfoRequest request, int seq,
+ IInputContextCallback callback);
}
diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl
index 661066b..ab2fbdc 100644
--- a/core/java/com/android/internal/view/IInputContextCallback.aidl
+++ b/core/java/com/android/internal/view/IInputContextCallback.aidl
@@ -27,4 +27,5 @@ oneway interface IInputContextCallback {
void setCursorCapsMode(int capsMode, int seq);
void setExtractedText(in ExtractedText extractedText, int seq);
void setSelectedText(CharSequence selectedText, int seq);
+ void setRequestCursorAnchorInfoResult(int result, int seq);
}
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index b100d27..89d36ff 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -27,6 +27,5 @@ oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
void onUnbindMethod(int sequence);
void setActive(boolean active);
- void setCursorAnchorMonitorMode(int monitorMode);
void setUserActionNotificationSequenceNumber(int sequenceNumber);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b84c359..6f104dd 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -78,5 +78,4 @@ interface IInputMethodManager {
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
int getInputMethodWindowVisibleHeight();
oneway void notifyUserAction(int sequenceNumber);
- void setCursorAnchorMonitorMode(in IBinder token, int monitorMode);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 9024d8d..8535a98 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -23,6 +23,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
@@ -40,6 +41,7 @@ public class InputConnectionWrapper implements InputConnection {
public CharSequence mSelectedText;
public ExtractedText mExtractedText;
public int mCursorCapsMode;
+ public int mCursorAnchorInfoRequestResult;
// A 'pool' of one InputContextCallback. Each ICW request will attempt to gain
// exclusive access to this object.
@@ -152,7 +154,20 @@ public class InputConnectionWrapper implements InputConnection {
}
}
}
-
+
+ public void setRequestCursorAnchorInfoResult(int result, int seq) {
+ synchronized (this) {
+ if (seq == mSeq) {
+ mCursorAnchorInfoRequestResult = result;
+ mHaveValue = true;
+ notifyAll();
+ } else {
+ Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
+ + ") in setCursorAnchorInfoRequestResult, ignoring.");
+ }
+ }
+ }
+
/**
* Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
*
@@ -413,4 +428,22 @@ public class InputConnectionWrapper implements InputConnection {
return false;
}
}
+
+ public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
+ int value = CursorAnchorInfoRequest.RESULT_NOT_HANDLED;
+ try {
+ InputContextCallback callback = InputContextCallback.getInstance();
+ mIInputContext.requestCursorAnchorInfo(request, callback.mSeq, callback);
+ synchronized (callback) {
+ callback.waitForResultLocked();
+ if (callback.mHaveValue) {
+ value = callback.mCursorAnchorInfoRequestResult;
+ }
+ }
+ callback.dispose();
+ } catch (RemoteException e) {
+ return CursorAnchorInfoRequest.RESULT_NOT_HANDLED;
+ }
+ return value;
+ }
}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 4cbdf78..10a7794 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -25,6 +25,7 @@ import android.util.Log;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.widget.TextView;
@@ -185,4 +186,18 @@ public class EditableInputConnection extends BaseInputConnection {
return success;
}
+
+ @Override
+ public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
+ if (DEBUG) Log.v(TAG, "requestCursorAnchorInfo " + request);
+ final int result = super.requestCursorAnchorInfo(request);
+ if (mIMM != null && request != null && (request.getRequestType() ==
+ CursorAnchorInfoRequest.TYPE_CURSOR_ANCHOR_INFO)) {
+ mIMM.setCursorAnchorInfoMonitorMode(request.getRequestFlags());
+ // One-shot event is not yet fully supported.
+ // TODO: Support one-shot event correctly.
+ return CursorAnchorInfoRequest.RESULT_SCHEDULED;
+ }
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 8fb2e9f..d4f141d 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -148,7 +148,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_UNBIND_METHOD = 3000;
static final int MSG_BIND_METHOD = 3010;
static final int MSG_SET_ACTIVE = 3020;
- static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 3030;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -2338,26 +2337,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- @Override
- public void setCursorAnchorMonitorMode(IBinder token, int monitorMode) {
- if (DEBUG) {
- Slog.d(TAG, "setCursorAnchorMonitorMode: monitorMode=" + monitorMode);
- }
- if (!calledFromValidUser()) {
- return;
- }
- synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
- final int uid = Binder.getCallingUid();
- Slog.e(TAG, "Ignoring setCursorAnchorMonitorMode due to an invalid token. uid:"
- + uid + " token:" + token);
- return;
- }
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO(
- MSG_SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, mCurClient));
- }
- }
-
private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
@@ -2595,15 +2574,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ ((ClientState)msg.obj).uid);
}
return true;
- case MSG_SET_CURSOR_ANCHOR_MONITOR_MODE:
- try {
- ((ClientState)msg.obj).client.setCursorAnchorMonitorMode(msg.arg1);
- } catch (RemoteException e) {
- Slog.w(TAG, "Got RemoteException sending setCursorAnchorMonitorMode "
- + "notification to pid " + ((ClientState)msg.obj).pid
- + " uid " + ((ClientState)msg.obj).uid);
- }
- return true;
case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
final int sequenceNumber = msg.arg1;
final IInputMethodClient client = (IInputMethodClient)msg.obj;