diff options
author | Yohei Yukawa <yukawa@google.com> | 2014-07-11 13:31:08 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-07-10 21:43:01 +0000 |
commit | 7c940e94c97ce3c86f2d54ad567127fc11cc8074 (patch) | |
tree | f64b0b0df27a2887e00bd52d630efd8440b6f34e | |
parent | c3b07a0c9ce9b1a2af644112e678f3963226fad2 (diff) | |
parent | 0023d0e0c4f5339b299d1eacbd4e7181c2fd271f (diff) | |
download | frameworks_base-7c940e94c97ce3c86f2d54ad567127fc11cc8074.zip frameworks_base-7c940e94c97ce3c86f2d54ad567127fc11cc8074.tar.gz frameworks_base-7c940e94c97ce3c86f2d54ad567127fc11cc8074.tar.bz2 |
Merge "Polish new IME API for L part 2: CursorAnchorInfo"
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; |