am e6d58c4c: Merge "Introduce new API for floating window support"

* commit 'e6d58c4ce6dedc082cbc13aa494e793a78022aa6':
  Introduce new API for floating window support
This commit is contained in:
Yohei Yukawa
2014-05-09 10:47:09 +00:00
committed by Android Git Automerger
13 changed files with 1249 additions and 13 deletions

View File

@@ -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(

View File

@@ -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.
*/

View File

@@ -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;

View 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;

View 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;
}
}

View File

@@ -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.

View File

@@ -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);
}

View 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;
}
}

View File

@@ -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);
}