am e6d58c4c: Merge "Introduce new API for floating window support"
* commit 'e6d58c4ce6dedc082cbc13aa494e793a78022aa6': Introduce new API for floating window support
This commit is contained in:
@@ -36,6 +36,7 @@ import android.view.MotionEvent;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.InputMethodSession;
|
||||
import android.view.inputmethod.CursorAnchorInfo;
|
||||
|
||||
class IInputMethodSessionWrapper extends IInputMethodSession.Stub
|
||||
implements HandlerCaller.Callback {
|
||||
@@ -46,6 +47,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
|
||||
private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
|
||||
private static final int DO_UPDATE_SELECTION = 90;
|
||||
private static final int DO_UPDATE_CURSOR = 95;
|
||||
private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99;
|
||||
private static final int DO_APP_PRIVATE_COMMAND = 100;
|
||||
private static final int DO_TOGGLE_SOFT_INPUT = 105;
|
||||
private static final int DO_FINISH_SESSION = 110;
|
||||
@@ -108,6 +110,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
|
||||
mInputMethodSession.updateCursor((Rect)msg.obj);
|
||||
return;
|
||||
}
|
||||
case DO_UPDATE_CURSOR_ANCHOR_INFO: {
|
||||
mInputMethodSession.updateCursorAnchorInfo((CursorAnchorInfo)msg.obj);
|
||||
return;
|
||||
}
|
||||
case DO_APP_PRIVATE_COMMAND: {
|
||||
SomeArgs args = (SomeArgs)msg.obj;
|
||||
mInputMethodSession.appPrivateCommand((String)args.arg1,
|
||||
@@ -180,6 +186,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
|
||||
mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
|
||||
mCaller.executeOrSendMessage(
|
||||
mCaller.obtainMessageO(DO_UPDATE_CURSOR_ANCHOR_INFO, cursorAnchorInfo));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appPrivateCommand(String action, Bundle data) {
|
||||
mCaller.executeOrSendMessage(
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.view.WindowManager;
|
||||
import android.view.WindowManager.BadTokenException;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.CursorAnchorInfo;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
@@ -545,6 +546,17 @@ public class InputMethodService extends AbstractInputMethodService {
|
||||
public void toggleSoftInput(int showFlags, int hideFlags) {
|
||||
InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call {@link InputMethodService#onUpdateCursorAnchorInfo
|
||||
* InputMethodService.onUpdateCursorAnchorInfo()}.
|
||||
*/
|
||||
public void updateCursorAnchorInfo(CursorAnchorInfo info) {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
InputMethodService.this.onUpdateCursorAnchorInfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1716,6 +1728,17 @@ public class InputMethodService extends AbstractInputMethodService {
|
||||
// Intentionally empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the application has reported a new location of its text insertion point and
|
||||
* characters in the composition string. This is only called if explicitly requested by the
|
||||
* input method. The default implementation does nothing.
|
||||
* @param cursorAnchorInfo The positional information of the text insertion point and the
|
||||
* composition string.
|
||||
*/
|
||||
public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
|
||||
// Intentionally empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cursor/anthor monitor mode.
|
||||
*/
|
||||
|
||||
@@ -88,16 +88,15 @@ public final class CorrectionInfo implements Parcelable {
|
||||
/**
|
||||
* Used to make this class parcelable.
|
||||
*/
|
||||
public static final Parcelable.Creator<CorrectionInfo> CREATOR
|
||||
= new Parcelable.Creator<CorrectionInfo>() {
|
||||
public CorrectionInfo createFromParcel(Parcel source) {
|
||||
return new CorrectionInfo(source);
|
||||
}
|
||||
|
||||
public CorrectionInfo[] newArray(int size) {
|
||||
return new CorrectionInfo[size];
|
||||
}
|
||||
};
|
||||
public static final Parcelable.Creator<CorrectionInfo> CREATOR =
|
||||
new Parcelable.Creator<CorrectionInfo>() {
|
||||
public CorrectionInfo createFromParcel(Parcel source) {
|
||||
return new CorrectionInfo(source);
|
||||
}
|
||||
public CorrectionInfo[] newArray(int size) {
|
||||
return new CorrectionInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
||||
19
core/java/android/view/inputmethod/CursorAnchorInfo.aidl
Normal file
19
core/java/android/view/inputmethod/CursorAnchorInfo.aidl
Normal file
@@ -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 CursorAnchorInfo;
|
||||
449
core/java/android/view/inputmethod/CursorAnchorInfo.java
Normal file
449
core/java/android/view/inputmethod/CursorAnchorInfo.java
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* 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.graphics.Matrix;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.Layout;
|
||||
import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Positional information about the text insertion point and characters in the composition string.
|
||||
*
|
||||
* <p>This class encapsulates locations of the text insertion point and the composition string in
|
||||
* the screen coordinates so that IMEs can render their UI components near where the text is
|
||||
* actually inserted.</p>
|
||||
*/
|
||||
public final class CursorAnchorInfo implements Parcelable {
|
||||
private final int mSelectionStart;
|
||||
private final int mSelectionEnd;
|
||||
private final int mCandidatesStart;
|
||||
private final int mCandidatesEnd;
|
||||
|
||||
/**
|
||||
* Horizontal position of the insertion marker, in the local coordinates that will be
|
||||
* transformed with the transformation matrix when rendered on the screen. This should be
|
||||
* calculated or compatible with {@link Layout#getPrimaryHorizontal(int)}. This can be
|
||||
* {@code java.lang.Float.NaN} when no value is specified.
|
||||
*/
|
||||
private final float mInsertionMarkerHorizontal;
|
||||
/**
|
||||
* Vertical position of the insertion marker, in the local coordinates that will be
|
||||
* transformed with the transformation matrix when rendered on the screen. This should be
|
||||
* calculated or compatible with {@link Layout#getLineTop(int)}. This can be
|
||||
* {@code java.lang.Float.NaN} when no value is specified.
|
||||
*/
|
||||
private final float mInsertionMarkerTop;
|
||||
/**
|
||||
* Vertical position of the insertion marker, in the local coordinates that will be
|
||||
* transformed with the transformation matrix when rendered on the screen. This should be
|
||||
* calculated or compatible with {@link Layout#getLineBaseline(int)}. This can be
|
||||
* {@code java.lang.Float.NaN} when no value is specified.
|
||||
*/
|
||||
private final float mInsertionMarkerBaseline;
|
||||
/**
|
||||
* Vertical position of the insertion marker, in the local coordinates that will be
|
||||
* transformed with the transformation matrix when rendered on the screen. This should be
|
||||
* calculated or compatible with {@link Layout#getLineBottom(int)}. This can be
|
||||
* {@code java.lang.Float.NaN} when no value is specified.
|
||||
*/
|
||||
private final float mInsertionMarkerBottom;
|
||||
|
||||
/**
|
||||
* Container of rectangular position of characters, keyed with character index in a unit of
|
||||
* Java chars, in the local coordinates that will be transformed with the transformation matrix
|
||||
* when rendered on the screen.
|
||||
*/
|
||||
private final SparseRectFArray mCharacterRects;
|
||||
|
||||
/**
|
||||
* Transformation matrix that is applied to any positional information of this class to
|
||||
* transform local coordinates into screen coordinates.
|
||||
*/
|
||||
private final Matrix mMatrix;
|
||||
|
||||
public CursorAnchorInfo(final Parcel source) {
|
||||
mSelectionStart = source.readInt();
|
||||
mSelectionEnd = source.readInt();
|
||||
mCandidatesStart = source.readInt();
|
||||
mCandidatesEnd = source.readInt();
|
||||
mInsertionMarkerHorizontal = source.readFloat();
|
||||
mInsertionMarkerTop = source.readFloat();
|
||||
mInsertionMarkerBaseline = source.readFloat();
|
||||
mInsertionMarkerBottom = source.readFloat();
|
||||
mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader());
|
||||
mMatrix = new Matrix();
|
||||
mMatrix.setValues(source.createFloatArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(mSelectionStart);
|
||||
dest.writeInt(mSelectionEnd);
|
||||
dest.writeInt(mCandidatesStart);
|
||||
dest.writeInt(mCandidatesEnd);
|
||||
dest.writeFloat(mInsertionMarkerHorizontal);
|
||||
dest.writeFloat(mInsertionMarkerTop);
|
||||
dest.writeFloat(mInsertionMarkerBaseline);
|
||||
dest.writeFloat(mInsertionMarkerBottom);
|
||||
dest.writeParcelable(mCharacterRects, flags);
|
||||
final float[] matrixArray = new float[9];
|
||||
mMatrix.getValues(matrixArray);
|
||||
dest.writeFloatArray(matrixArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
// TODO: Improve the hash function.
|
||||
final float floatHash = mSelectionStart + mSelectionEnd + mCandidatesStart + mCandidatesEnd
|
||||
+ mInsertionMarkerHorizontal + mInsertionMarkerTop + mInsertionMarkerBaseline
|
||||
+ mInsertionMarkerBottom;
|
||||
int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash);
|
||||
if (mCharacterRects != null) {
|
||||
hash += mCharacterRects.hashCode();
|
||||
}
|
||||
hash += mMatrix.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj){
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CursorAnchorInfo)) {
|
||||
return false;
|
||||
}
|
||||
final CursorAnchorInfo that = (CursorAnchorInfo) obj;
|
||||
if (hashCode() != that.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
if (mSelectionStart != that.mSelectionStart
|
||||
|| mSelectionEnd != that.mSelectionEnd
|
||||
|| mCandidatesStart != that.mCandidatesStart
|
||||
|| mCandidatesEnd != that.mCandidatesEnd) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(mCharacterRects, that.mCharacterRects)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(mMatrix, that.mMatrix)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd
|
||||
+ " mCandiadtes=" + mCandidatesStart + "," + mCandidatesEnd
|
||||
+ " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal
|
||||
+ " mInsertionMarkerTop=" + mInsertionMarkerTop
|
||||
+ " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
|
||||
+ " mInsertionMarkerBottom=" + mInsertionMarkerBottom
|
||||
+ " mCharacterRects=" + (mCharacterRects != null ? mCharacterRects : "null")
|
||||
+ " mMatrix=" + mMatrix
|
||||
+ "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
|
||||
*/
|
||||
public static final class CursorAnchorInfoBuilder {
|
||||
/**
|
||||
* Sets the text range of the selection. Calling this can be skipped if there is no
|
||||
* selection.
|
||||
*/
|
||||
public CursorAnchorInfoBuilder setSelectionRange(final int newStart, final int newEnd) {
|
||||
mSelectionStart = newStart;
|
||||
mSelectionEnd = newEnd;
|
||||
return this;
|
||||
}
|
||||
private int mSelectionStart = -1;
|
||||
private int mSelectionEnd = -1;
|
||||
|
||||
/**
|
||||
* Sets the text range of the composition string. Calling this can be skipped if there is
|
||||
* no composition.
|
||||
*/
|
||||
public CursorAnchorInfoBuilder setCandidateRange(final int start, final int end) {
|
||||
mCandidateStart = start;
|
||||
mCandidateEnd = end;
|
||||
return this;
|
||||
}
|
||||
private int mCandidateStart = -1;
|
||||
private int mCandidateEnd = -1;
|
||||
|
||||
/**
|
||||
* Sets the location of the text insertion point (zero width cursor) as a rectangle in
|
||||
* local coordinates. Calling this can be skipped when there is no text insertion point;
|
||||
* however if there is an insertion point, editors must call this method.
|
||||
* @param horizontalPosition horizontal position of the insertion marker, in the local
|
||||
* coordinates that will be transformed with the transformation matrix when rendered on the
|
||||
* screen. This should be calculated or compatible with
|
||||
* {@link Layout#getPrimaryHorizontal(int)}.
|
||||
* @param lineTop vertical position of the insertion marker, in the local coordinates that
|
||||
* will be transformed with the transformation matrix when rendered on the screen. This
|
||||
* should be calculated or compatible with {@link Layout#getLineTop(int)}.
|
||||
* @param lineBaseline vertical position of the insertion marker, in the local coordinates
|
||||
* that will be transformed with the transformation matrix when rendered on the screen. This
|
||||
* should be calculated or compatible with {@link Layout#getLineBaseline(int)}.
|
||||
* @param lineBottom vertical position of the insertion marker, in the local coordinates
|
||||
* that will be transformed with the transformation matrix when rendered on the screen. This
|
||||
* should be calculated or compatible with {@link Layout#getLineBottom(int)}.
|
||||
*/
|
||||
public CursorAnchorInfoBuilder setInsertionMarkerLocation(
|
||||
final float horizontalPosition, final float lineTop, final float lineBaseline,
|
||||
final float lineBottom){
|
||||
mInsertionMarkerHorizontal = horizontalPosition;
|
||||
mInsertionMarkerTop = lineTop;
|
||||
mInsertionMarkerBaseline = lineBaseline;
|
||||
mInsertionMarkerBottom = lineBottom;
|
||||
return this;
|
||||
}
|
||||
private float mInsertionMarkerHorizontal = Float.NaN;
|
||||
private float mInsertionMarkerTop = Float.NaN;
|
||||
private float mInsertionMarkerBaseline = Float.NaN;
|
||||
private float mInsertionMarkerBottom = Float.NaN;
|
||||
|
||||
/**
|
||||
* Adds the bounding box of the character specified with the index.
|
||||
* <p>
|
||||
* Editor authors should not call this method for characters that are invisible.
|
||||
* </p>
|
||||
*
|
||||
* @param index index of the character in Java chars units. Must be specified in
|
||||
* ascending order across successive calls.
|
||||
* @param leadingEdgeX x coordinate of the leading edge of the character in local
|
||||
* coordinates, that is, left edge for LTR text and right edge for RTL text.
|
||||
* @param leadingEdgeY y coordinate of the leading edge of the character in local
|
||||
* coordinates.
|
||||
* @param trailingEdgeX x coordinate of the trailing edge of the character in local
|
||||
* coordinates, that is, right edge for LTR text and left edge for RTL text.
|
||||
* @param trailingEdgeY y coordinate of the trailing edge of the character in local
|
||||
* coordinates.
|
||||
* @throws IllegalArgumentException If the index is a negative value, or not greater than
|
||||
* all of the previously called indices.
|
||||
*/
|
||||
public CursorAnchorInfoBuilder addCharacterRect(final int index,
|
||||
final float leadingEdgeX, final float leadingEdgeY, final float trailingEdgeX,
|
||||
final float trailingEdgeY) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("index must not be a negative integer.");
|
||||
}
|
||||
if (mCharacterRectBuilder == null) {
|
||||
mCharacterRectBuilder = new SparseRectFArrayBuilder();
|
||||
}
|
||||
mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX,
|
||||
trailingEdgeY);
|
||||
return this;
|
||||
}
|
||||
private SparseRectFArrayBuilder mCharacterRectBuilder = null;
|
||||
|
||||
/**
|
||||
* Sets the matrix that transforms local coordinates into screen coordinates.
|
||||
* @param matrix transformation matrix from local coordinates into screen coordinates. null
|
||||
* is interpreted as an identity matrix.
|
||||
*/
|
||||
public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) {
|
||||
if (matrix != null) {
|
||||
mMatrix = matrix;
|
||||
} else {
|
||||
mMatrix = Matrix.IDENTITY_MATRIX;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
private Matrix mMatrix = Matrix.IDENTITY_MATRIX;
|
||||
|
||||
/**
|
||||
* @return {@link CursorAnchorInfo} using parameters in this
|
||||
* {@link CursorAnchorInfoBuilder}.
|
||||
*/
|
||||
public CursorAnchorInfo build() {
|
||||
return new CursorAnchorInfo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state so that this instance can be reused to build another
|
||||
* instance of {@link CursorAnchorInfo}.
|
||||
*/
|
||||
public void reset() {
|
||||
mSelectionStart = -1;
|
||||
mSelectionEnd = -1;
|
||||
mCandidateStart = -1;
|
||||
mCandidateEnd = -1;
|
||||
mInsertionMarkerHorizontal = Float.NaN;
|
||||
mInsertionMarkerTop = Float.NaN;
|
||||
mInsertionMarkerBaseline = Float.NaN;
|
||||
mInsertionMarkerBottom = Float.NaN;
|
||||
mMatrix = Matrix.IDENTITY_MATRIX;
|
||||
if (mCharacterRectBuilder != null) {
|
||||
mCharacterRectBuilder.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) {
|
||||
mSelectionStart = builder.mSelectionStart;
|
||||
mSelectionEnd = builder.mSelectionEnd;
|
||||
mCandidatesStart = builder.mCandidateStart;
|
||||
mCandidatesEnd = builder.mCandidateEnd;
|
||||
mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
|
||||
mInsertionMarkerTop = builder.mInsertionMarkerTop;
|
||||
mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
|
||||
mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
|
||||
mCharacterRects = builder.mCharacterRectBuilder != null ?
|
||||
builder.mCharacterRectBuilder.build() : null;
|
||||
mMatrix = builder.mMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index where the selection starts.
|
||||
* @return -1 if there is no selection.
|
||||
*/
|
||||
public int getSelectionStart() {
|
||||
return mSelectionStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index where the selection ends.
|
||||
* @return -1 if there is no selection.
|
||||
*/
|
||||
public int getSelectionEnd() {
|
||||
return mSelectionEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index where the composition starts.
|
||||
* @return -1 if there is no composition.
|
||||
*/
|
||||
public int getCandidatesStart() {
|
||||
return mCandidatesStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index where the composition ends.
|
||||
* @return -1 if there is no composition.
|
||||
*/
|
||||
public int getCandidatesEnd() {
|
||||
return mCandidatesEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the horizontal start of the insertion marker, in the local coordinates that will
|
||||
* be transformed with {@link #getMatrix()} when rendered on the screen.
|
||||
* @return x coordinate that is compatible with {@link Layout#getPrimaryHorizontal(int)}.
|
||||
* Pay special care to RTL/LTR handling.
|
||||
* {@code java.lang.Float.NaN} if not specified.
|
||||
* @see Layout#getPrimaryHorizontal(int)
|
||||
*/
|
||||
public float getInsertionMarkerHorizontal() {
|
||||
return mInsertionMarkerHorizontal;
|
||||
}
|
||||
/**
|
||||
* Returns the vertical top position of the insertion marker, in the local coordinates that
|
||||
* will be transformed with {@link #getMatrix()} when rendered on the screen.
|
||||
* @return y coordinate that is compatible with {@link Layout#getLineTop(int)}.
|
||||
* {@code java.lang.Float.NaN} if not specified.
|
||||
*/
|
||||
public float getInsertionMarkerTop() {
|
||||
return mInsertionMarkerTop;
|
||||
}
|
||||
/**
|
||||
* Returns the vertical baseline position of the insertion marker, in the local coordinates
|
||||
* that will be transformed with {@link #getMatrix()} when rendered on the screen.
|
||||
* @return y coordinate that is compatible with {@link Layout#getLineBaseline(int)}.
|
||||
* {@code java.lang.Float.NaN} if not specified.
|
||||
*/
|
||||
public float getInsertionMarkerBaseline() {
|
||||
return mInsertionMarkerBaseline;
|
||||
}
|
||||
/**
|
||||
* Returns the vertical bottom position of the insertion marker, in the local coordinates
|
||||
* that will be transformed with {@link #getMatrix()} when rendered on the screen.
|
||||
* @return y coordinate that is compatible with {@link Layout#getLineBottom(int)}.
|
||||
* {@code java.lang.Float.NaN} if not specified.
|
||||
*/
|
||||
public float getInsertionMarkerBottom() {
|
||||
return mInsertionMarkerBottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance of {@link RectF} that indicates the location of the character
|
||||
* specified with the index.
|
||||
* <p>
|
||||
* Note that coordinates are not necessarily contiguous or even monotonous, especially when
|
||||
* RTL text and LTR text are mixed.
|
||||
* </p>
|
||||
* @param index index of the character in a Java chars.
|
||||
* @return a new instance of {@link RectF} that represents the location of the character in
|
||||
* local coordinates. null if the character is invisible or the application did not provide
|
||||
* the location. Note that the {@code left} field can be greater than the {@code right} field
|
||||
* if the character is in RTL text.
|
||||
*/
|
||||
// TODO: Prepare a document about the expected behavior for surrogate pairs, combining
|
||||
// characters, and non-graphical chars.
|
||||
public RectF getCharacterRect(final int index) {
|
||||
if (mCharacterRects == null) {
|
||||
return null;
|
||||
}
|
||||
return mCharacterRects.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
|
||||
* matrix that is to be applied other positional data in this class.
|
||||
* @return a new instance (copy) of the transformation matrix.
|
||||
*/
|
||||
public Matrix getMatrix() {
|
||||
return new Matrix(mMatrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to make this class parcelable.
|
||||
*/
|
||||
public static final Parcelable.Creator<CursorAnchorInfo> CREATOR
|
||||
= new Parcelable.Creator<CursorAnchorInfo>() {
|
||||
@Override
|
||||
public CursorAnchorInfo createFromParcel(Parcel source) {
|
||||
return new CursorAnchorInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorAnchorInfo[] newArray(int size) {
|
||||
return new CursorAnchorInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import android.view.InputEventSender;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -321,6 +322,7 @@ public final class InputMethodManager {
|
||||
* The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
|
||||
*/
|
||||
private final int[] mViewTopLeft = new int[2];
|
||||
private final CursorAnchorInfoBuilder mCursorAnchorInfoBuilder = new CursorAnchorInfoBuilder();
|
||||
|
||||
// -----------------------------------------------------------
|
||||
|
||||
@@ -1435,7 +1437,7 @@ public final class InputMethodManager {
|
||||
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
|
||||
|| mCursorCandStart != candidatesStart
|
||||
|| mCursorCandEnd != candidatesEnd) {
|
||||
@@ -1555,6 +1557,31 @@ public final class InputMethodManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report positional change of the text insertion point and/or characters in the composition
|
||||
* string.
|
||||
*/
|
||||
public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) {
|
||||
if (view == null || cursorAnchorInfo == null) {
|
||||
return;
|
||||
}
|
||||
checkFocus();
|
||||
synchronized (mH) {
|
||||
if ((mServedView != view &&
|
||||
(mServedView == null || !mServedView.checkInputConnectionProxy(view)))
|
||||
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo");
|
||||
|
||||
try {
|
||||
mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "IME died: " + mCurId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
|
||||
* InputMethodSession.appPrivateCommand()} on the current Input Method.
|
||||
|
||||
@@ -165,7 +165,7 @@ public interface InputMethodSession {
|
||||
public void appPrivateCommand(String action, Bundle data);
|
||||
|
||||
/**
|
||||
* Toggle the soft input window.
|
||||
* Toggle the soft input window.
|
||||
* Applications can toggle the state of the soft input window.
|
||||
* @param showFlags Provides additional operating flags. May be
|
||||
* 0 or have the {@link InputMethodManager#SHOW_IMPLICIT},
|
||||
@@ -175,4 +175,14 @@ public interface InputMethodSession {
|
||||
* {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
|
||||
*/
|
||||
public void toggleSoftInput(int showFlags, int hideFlags);
|
||||
|
||||
/**
|
||||
* This method is called when the cursor and/or the character position relevant to text input
|
||||
* is changed on the screen. This is not called by default. It will only be reported if
|
||||
* requested by the input method.
|
||||
*
|
||||
* @param cursorAnchorInfo Positional information relevant to text input, such as text
|
||||
* insertion point and composition string.
|
||||
*/
|
||||
public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo);
|
||||
}
|
||||
|
||||
265
core/java/android/view/inputmethod/SparseRectFArray.java
Normal file
265
core/java/android/view/inputmethod/SparseRectFArray.java
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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.graphics.RectF;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An implementation of SparseArray specialized for {@link android.graphics.RectF}.
|
||||
* <p>
|
||||
* As this is a sparse array, it represents an array of {@link RectF} most of which are null. This
|
||||
* class could be in some other packages like android.graphics or android.util but currently
|
||||
* belong to android.view.inputmethod because this class is hidden and used only in input method
|
||||
* framework.
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
public final class SparseRectFArray implements Parcelable {
|
||||
/**
|
||||
* The keys, in ascending order, of those {@link RectF} that are not null. For example,
|
||||
* {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}.
|
||||
* @see #mCoordinates
|
||||
*/
|
||||
private final int[] mKeys;
|
||||
|
||||
/**
|
||||
* Stores coordinates of the rectangles, in the order of
|
||||
* {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top},
|
||||
* {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom},
|
||||
* {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top},
|
||||
* {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom},
|
||||
* {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, ....
|
||||
*/
|
||||
private final float[] mCoordinates;
|
||||
|
||||
public SparseRectFArray(final Parcel source) {
|
||||
mKeys = source.createIntArray();
|
||||
mCoordinates = source.createFloatArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.writeIntArray(mKeys);
|
||||
dest.writeFloatArray(mCoordinates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// TODO: Improve the hash function.
|
||||
if (mKeys == null || mKeys.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
int hash = mKeys.length;
|
||||
// For performance reasons, only the first rectangle is used for the hash code now.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
hash *= 31;
|
||||
hash += mCoordinates[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj){
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SparseRectFArray)) {
|
||||
return false;
|
||||
}
|
||||
final SparseRectFArray that = (SparseRectFArray) obj;
|
||||
|
||||
return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mKeys == null || mCoordinates == null) {
|
||||
return "SparseRectFArray{}";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("SparseRectFArray{");
|
||||
for (int i = 0; i < mKeys.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
final int baseIndex = i * 4;
|
||||
sb.append(mKeys[i]);
|
||||
sb.append(":[");
|
||||
sb.append(mCoordinates[baseIndex + 0]);
|
||||
sb.append(",");
|
||||
sb.append(mCoordinates[baseIndex + 1]);
|
||||
sb.append("],[");
|
||||
sb.append(mCoordinates[baseIndex + 2]);
|
||||
sb.append(",");
|
||||
sb.append(mCoordinates[baseIndex + 3]);
|
||||
sb.append("]");
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe.
|
||||
* @hide
|
||||
*/
|
||||
public static final class SparseRectFArrayBuilder {
|
||||
/**
|
||||
* Throws {@link IllegalArgumentException} to make sure that this class is correctly used.
|
||||
* @param key key to be checked.
|
||||
*/
|
||||
private void checkIndex(final int key) {
|
||||
if (mCount == 0) {
|
||||
return;
|
||||
}
|
||||
if (mKeys[mCount - 1] >= key) {
|
||||
throw new IllegalArgumentException("key must be greater than all existing keys.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the internal array if necessary.
|
||||
*/
|
||||
private void ensureBufferSize() {
|
||||
if (mKeys == null) {
|
||||
mKeys = new int[INITIAL_SIZE];
|
||||
}
|
||||
if (mCoordinates == null) {
|
||||
mCoordinates = new float[INITIAL_SIZE * 4];
|
||||
}
|
||||
final int requiredIndexArraySize = mCount + 1;
|
||||
if (mKeys.length <= requiredIndexArraySize) {
|
||||
final int[] newArray = new int[requiredIndexArraySize * 2];
|
||||
System.arraycopy(mKeys, 0, newArray, 0, mCount);
|
||||
mKeys = newArray;
|
||||
}
|
||||
final int requiredCoordinatesArraySize = (mCount + 1) * 4;
|
||||
if (mCoordinates.length <= requiredCoordinatesArraySize) {
|
||||
final float[] newArray = new float[requiredCoordinatesArraySize * 2];
|
||||
System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4);
|
||||
mCoordinates = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the rectangle with an integer key.
|
||||
* @param key the key to be associated with the rectangle. It must be greater than all
|
||||
* existing keys that have been previously specified.
|
||||
* @param left left of the rectangle.
|
||||
* @param top top of the rectangle.
|
||||
* @param right right of the rectangle.
|
||||
* @param bottom bottom of the rectangle.
|
||||
* @return the receiver object itself for chaining method calls.
|
||||
* @throws IllegalArgumentException If the index is not greater than all of existing keys.
|
||||
*/
|
||||
public SparseRectFArrayBuilder append(final int key,
|
||||
final float left, final float top, final float right, final float bottom) {
|
||||
checkIndex(key);
|
||||
ensureBufferSize();
|
||||
final int baseCoordinatesIndex = mCount * 4;
|
||||
mCoordinates[baseCoordinatesIndex + 0] = left;
|
||||
mCoordinates[baseCoordinatesIndex + 1] = top;
|
||||
mCoordinates[baseCoordinatesIndex + 2] = right;
|
||||
mCoordinates[baseCoordinatesIndex + 3] = bottom;
|
||||
mKeys[mCount] = key;
|
||||
++mCount;
|
||||
return this;
|
||||
}
|
||||
private int mCount = 0;
|
||||
private int[] mKeys = null;
|
||||
private float[] mCoordinates = null;
|
||||
private static int INITIAL_SIZE = 16;
|
||||
|
||||
/**
|
||||
* @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}.
|
||||
*/
|
||||
public SparseRectFArray build() {
|
||||
return new SparseRectFArray(this);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (mCount == 0) {
|
||||
mKeys = null;
|
||||
mCoordinates = null;
|
||||
}
|
||||
mCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private SparseRectFArray(final SparseRectFArrayBuilder builder) {
|
||||
if (builder.mCount == 0) {
|
||||
mKeys = null;
|
||||
mCoordinates = null;
|
||||
} else {
|
||||
mKeys = new int[builder.mCount];
|
||||
mCoordinates = new float[builder.mCount * 4];
|
||||
System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount);
|
||||
System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4);
|
||||
}
|
||||
}
|
||||
|
||||
public RectF get(final int index) {
|
||||
if (mKeys == null) {
|
||||
return null;
|
||||
}
|
||||
if (index < 0) {
|
||||
return null;
|
||||
}
|
||||
final int arrayIndex = Arrays.binarySearch(mKeys, index);
|
||||
if (arrayIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
final int baseCoordIndex = arrayIndex * 4;
|
||||
return new RectF(mCoordinates[baseCoordIndex],
|
||||
mCoordinates[baseCoordIndex + 1],
|
||||
mCoordinates[baseCoordIndex + 2],
|
||||
mCoordinates[baseCoordIndex + 3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to make this class parcelable.
|
||||
*/
|
||||
public static final Parcelable.Creator<SparseRectFArray> CREATOR =
|
||||
new Parcelable.Creator<SparseRectFArray>() {
|
||||
@Override
|
||||
public SparseRectFArray createFromParcel(Parcel source) {
|
||||
return new SparseRectFArray(source);
|
||||
}
|
||||
@Override
|
||||
public SparseRectFArray[] newArray(int size) {
|
||||
return new SparseRectFArray[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.CursorAnchorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
|
||||
/**
|
||||
@@ -47,4 +48,6 @@ oneway interface IInputMethodSession {
|
||||
void toggleSoftInput(int showFlags, int hideFlags);
|
||||
|
||||
void finishSession();
|
||||
|
||||
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user