Add a new API CursorAnchorInfo#getTextAppearanceInfo().

Add a new group of information related to text appearance in
CursorAnchorInfo, which is extracted from TextView.

Bug: 231894524

Test: atest android.view.inputmethod.cts.CursorAnchorInfoTest#testBuilder
atest android.view.inputmethod.cts.CursorAnchorInfoTest#testEquality
atest android.view.inputmethod.cts.InputMethodServiceTest#testOnUpdateCursorAnchorInfo

Change-Id: I8b96f055506b0a426dcfc6334db867dda7073868
This commit is contained in:
czq
2022-09-15 16:29:43 +08:00
parent d4e00f7110
commit 50f792e09d
6 changed files with 634 additions and 21 deletions

View File

@@ -53245,6 +53245,7 @@ package android.view.inputmethod {
method public android.graphics.Matrix getMatrix();
method public int getSelectionEnd();
method public int getSelectionStart();
method @Nullable public android.view.inputmethod.TextAppearanceInfo getTextAppearanceInfo();
method @NonNull public java.util.List<android.graphics.RectF> getVisibleLineBounds();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.CursorAnchorInfo> CREATOR;
@@ -53265,6 +53266,7 @@ package android.view.inputmethod {
method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float, int);
method public android.view.inputmethod.CursorAnchorInfo.Builder setMatrix(android.graphics.Matrix);
method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
method @NonNull public android.view.inputmethod.CursorAnchorInfo.Builder setTextAppearanceInfo(@Nullable android.view.inputmethod.TextAppearanceInfo);
}
public final class DeleteGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
@@ -53519,6 +53521,7 @@ package android.view.inputmethod {
field public static final int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 8; // 0x8
field public static final int CURSOR_UPDATE_FILTER_EDITOR_BOUNDS = 4; // 0x4
field public static final int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 16; // 0x10
field public static final int CURSOR_UPDATE_FILTER_TEXT_APPEARANCE = 64; // 0x40
field public static final int CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS = 32; // 0x20
field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
@@ -53822,6 +53825,35 @@ package android.view.inputmethod {
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
public final class TextAppearanceInfo implements android.os.Parcelable {
ctor public TextAppearanceInfo(@NonNull android.widget.TextView);
method public int describeContents();
method @Nullable public String getFontFamilyName();
method @Nullable public String getFontFeatureSettings();
method @Nullable public String getFontVariationSettings();
method public float getLetterSpacing();
method public int getLineBreakStyle();
method public int getLineBreakWordStyle();
method public int getMaxLength();
method @Px public float getShadowDx();
method @Px public float getShadowDy();
method @Px public float getShadowRadius();
method @ColorInt public int getTextColor();
method @ColorInt public int getTextColorHighlight();
method @ColorInt public int getTextColorHint();
method @Nullable public android.content.res.ColorStateList getTextColorLink();
method @IntRange(from=0xffffffff, to=android.graphics.fonts.FontStyle.FONT_WEIGHT_MAX) public int getTextFontWeight();
method @NonNull public android.os.LocaleList getTextLocales();
method public float getTextScaleX();
method @Px public float getTextSize();
method public int getTextStyle();
method public boolean isAllCaps();
method public boolean isElegantTextHeight();
method public boolean isFallbackLineSpacing();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextAppearanceInfo> CREATOR;
}
public final class TextAttribute implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.os.PersistableBundle getExtras();

View File

@@ -20,12 +20,14 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Layout;
import android.text.SpannedString;
import android.text.TextUtils;
import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
@@ -110,7 +112,7 @@ public final class CursorAnchorInfo implements Parcelable {
/**
* Container of rectangular position of Editor in the local coordinates that will be transformed
* with the transformation matrix when rendered on the screen.
* @see {@link EditorBoundsInfo}.
* @see EditorBoundsInfo
*/
private final EditorBoundsInfo mEditorBoundsInfo;
@@ -121,6 +123,12 @@ public final class CursorAnchorInfo implements Parcelable {
@NonNull
private final float[] mMatrixValues;
/**
* Information about text appearance in the editor for use by {@link InputMethodService}.
*/
@Nullable
private final TextAppearanceInfo mTextAppearanceInfo;
/**
* A list of visible line bounds stored in a float array. This array is divided into segment of
* four where each element in the segment represents left, top, right respectively and bottom
@@ -157,10 +165,11 @@ public final class CursorAnchorInfo implements Parcelable {
mInsertionMarkerTop = source.readFloat();
mInsertionMarkerBaseline = source.readFloat();
mInsertionMarkerBottom = source.readFloat();
mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader(), android.view.inputmethod.SparseRectFArray.class);
mCharacterBoundsArray = source.readTypedObject(SparseRectFArray.CREATOR);
mEditorBoundsInfo = source.readTypedObject(EditorBoundsInfo.CREATOR);
mMatrixValues = source.createFloatArray();
mVisibleLineBounds = source.createFloatArray();
mTextAppearanceInfo = source.readTypedObject(TextAppearanceInfo.CREATOR);
}
/**
@@ -181,10 +190,11 @@ public final class CursorAnchorInfo implements Parcelable {
dest.writeFloat(mInsertionMarkerTop);
dest.writeFloat(mInsertionMarkerBaseline);
dest.writeFloat(mInsertionMarkerBottom);
dest.writeParcelable(mCharacterBoundsArray, flags);
dest.writeTypedObject(mCharacterBoundsArray, flags);
dest.writeTypedObject(mEditorBoundsInfo, flags);
dest.writeFloatArray(mMatrixValues);
dest.writeFloatArray(mVisibleLineBounds);
dest.writeTypedObject(mTextAppearanceInfo, flags);
}
@Override
@@ -262,6 +272,11 @@ public final class CursorAnchorInfo implements Parcelable {
return false;
}
}
if (!Objects.equals(mTextAppearanceInfo, that.mTextAppearanceInfo)) {
return false;
}
return true;
}
@@ -270,16 +285,17 @@ public final class CursorAnchorInfo implements Parcelable {
return "CursorAnchorInfo{mHashCode=" + mHashCode
+ " mSelection=" + mSelectionStart + "," + mSelectionEnd
+ " mComposingTextStart=" + mComposingTextStart
+ " mComposingText=" + Objects.toString(mComposingText)
+ " mComposingText=" + mComposingText
+ " mInsertionMarkerFlags=" + mInsertionMarkerFlags
+ " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal
+ " mInsertionMarkerTop=" + mInsertionMarkerTop
+ " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
+ " mInsertionMarkerBottom=" + mInsertionMarkerBottom
+ " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
+ " mCharacterBoundsArray=" + mCharacterBoundsArray
+ " mEditorBoundsInfo=" + mEditorBoundsInfo
+ " mVisibleLineBounds=" + getVisibleLineBounds()
+ " mMatrix=" + Arrays.toString(mMatrixValues)
+ " mTextAppearanceInfo=" + mTextAppearanceInfo
+ "}";
}
@@ -303,6 +319,7 @@ public final class CursorAnchorInfo implements Parcelable {
private boolean mMatrixInitialized = false;
private float[] mVisibleLineBounds = new float[LINE_BOUNDS_INITIAL_SIZE * 4];
private int mVisibleLineBoundsCount = 0;
private TextAppearanceInfo mTextAppearanceInfo = null;
/**
* Sets the text range of the selection. Calling this can be skipped if there is no
@@ -415,6 +432,17 @@ public final class CursorAnchorInfo implements Parcelable {
return this;
}
/**
* Set the information related to text appearance, which is extracted from the original
* {@link TextView}.
* @param textAppearanceInfo {@link TextAppearanceInfo} of TextView.
*/
@NonNull
public Builder setTextAppearanceInfo(@Nullable TextAppearanceInfo textAppearanceInfo) {
mTextAppearanceInfo = textAppearanceInfo;
return this;
}
/**
* Add the bounds of a visible text line of the current editor.
*
@@ -504,6 +532,7 @@ public final class CursorAnchorInfo implements Parcelable {
}
mEditorBoundsInfo = null;
clearVisibleLineBounds();
mTextAppearanceInfo = null;
}
}
@@ -524,7 +553,8 @@ public final class CursorAnchorInfo implements Parcelable {
builder.mInsertionMarkerHorizontal, builder.mInsertionMarkerTop,
builder.mInsertionMarkerBaseline, builder.mInsertionMarkerBottom,
characterBoundsArray, builder.mEditorBoundsInfo, matrixValues,
Arrays.copyOf(builder.mVisibleLineBounds, builder.mVisibleLineBoundsCount));
Arrays.copyOf(builder.mVisibleLineBounds, builder.mVisibleLineBoundsCount),
builder.mTextAppearanceInfo);
}
private CursorAnchorInfo(int selectionStart, int selectionEnd, int composingTextStart,
@@ -533,7 +563,8 @@ public final class CursorAnchorInfo implements Parcelable {
float insertionMarkerBaseline, float insertionMarkerBottom,
@Nullable SparseRectFArray characterBoundsArray,
@Nullable EditorBoundsInfo editorBoundsInfo,
@NonNull float[] matrixValues, @Nullable float[] visibleLineBounds) {
@NonNull float[] matrixValues, @Nullable float[] visibleLineBounds,
@Nullable TextAppearanceInfo textAppearanceInfo) {
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
mComposingTextStart = composingTextStart;
@@ -547,6 +578,7 @@ public final class CursorAnchorInfo implements Parcelable {
mEditorBoundsInfo = editorBoundsInfo;
mMatrixValues = matrixValues;
mVisibleLineBounds = visibleLineBounds;
mTextAppearanceInfo = textAppearanceInfo;
// To keep hash function simple, we only use some complex objects for hash.
int hashCode = Objects.hashCode(mComposingText);
@@ -573,7 +605,7 @@ public final class CursorAnchorInfo implements Parcelable {
original.mInsertionMarkerTop, original.mInsertionMarkerBaseline,
original.mInsertionMarkerBottom, original.mCharacterBoundsArray,
original.mEditorBoundsInfo, computeMatrixValues(parentMatrix, original),
original.mVisibleLineBounds);
original.mVisibleLineBounds, original.mTextAppearanceInfo);
}
/**
@@ -740,6 +772,16 @@ public final class CursorAnchorInfo implements Parcelable {
return mEditorBoundsInfo;
}
/**
* Returns {@link TextAppearanceInfo} for the current editor, or {@code null} if IME is not
* subscribed with {@link InputConnection#CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}
* or {@link InputConnection#CURSOR_UPDATE_MONITOR}.
*/
@Nullable
public TextAppearanceInfo getTextAppearanceInfo() {
return mTextAppearanceInfo;
}
/**
* 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.

View File

@@ -1052,8 +1052,10 @@ public interface InputConnection {
* used together with {@link #CURSOR_UPDATE_MONITOR}.
* <p>
* Note by default all of {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS} and
* {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} are included but specifying them can
* {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}, and
* {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}, are included but specifying them can
* filter-out others.
* It can be CPU intensive to include all, filtering specific info is recommended.
* </p>
@@ -1071,7 +1073,8 @@ public interface InputConnection {
* <p>
* Note by default all of {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS} and
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}, and
* {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}, are included but specifying them can
* filter-out others.
* It can be CPU intensive to include all, filtering specific info is recommended.
@@ -1087,6 +1090,7 @@ public interface InputConnection {
* <p>
* This flag can be used together with filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE},
* {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} and update flags
* {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
* </p>
@@ -1102,8 +1106,8 @@ public interface InputConnection {
* <p>
* This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} and update flags
* {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
* {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}, {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}
* and update flags {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
* </p>
*/
int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 1 << 3;
@@ -1118,8 +1122,8 @@ public interface InputConnection {
* <p>
* This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS} and update flags {@link #CURSOR_UPDATE_IMMEDIATE}
* and {@link #CURSOR_UPDATE_MONITOR}.
* {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS}
* and update flags {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
* </p>
*/
int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 1 << 4;
@@ -1133,13 +1137,28 @@ public interface InputConnection {
* {@link InputConnection#requestCursorUpdates(int)} again with this flag off.
* <p>
* This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}
* and update flags {@link #CURSOR_UPDATE_IMMEDIATE}
* and {@link #CURSOR_UPDATE_MONITOR}.
* {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
* {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE} and update flags
* {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
* </p>
*/
int CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS = 1 << 5;
/**
* The editor is requested to call
* {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
* with new text appearance info {@link CursorAnchorInfo#getTextAppearanceInfo()}}
* whenever cursor/anchor position is changed. To disable monitoring, call
* {@link InputConnection#requestCursorUpdates(int)} again with this flag off.
* <p>
* This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
* {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
* {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS} and update flags
* {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
* </p>
*/
int CURSOR_UPDATE_FILTER_TEXT_APPEARANCE = 1 << 6;
/**
* @hide
*/
@@ -1153,7 +1172,8 @@ public interface InputConnection {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {CURSOR_UPDATE_FILTER_EDITOR_BOUNDS, CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS,
CURSOR_UPDATE_FILTER_INSERTION_MARKER, CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
CURSOR_UPDATE_FILTER_INSERTION_MARKER, CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS,
CURSOR_UPDATE_FILTER_TEXT_APPEARANCE},
flag = true, prefix = { "CURSOR_UPDATE_FILTER_" })
@interface CursorUpdateFilter{}

View File

@@ -0,0 +1,509 @@
/*
* Copyright (C) 2022 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.annotation.ColorInt;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
import android.content.res.ColorStateList;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.fonts.FontStyle;
import android.graphics.text.LineBreakConfig;
import android.inputmethodservice.InputMethodService;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.InputFilter;
import android.widget.TextView;
import java.util.Objects;
/**
* Information about text appearance in an editor, passed through
* {@link CursorAnchorInfo} for use by {@link InputMethodService}.
*
* @see TextView
* @see Paint
* @see CursorAnchorInfo.Builder#setTextAppearanceInfo(TextAppearanceInfo)
* @see CursorAnchorInfo#getTextAppearanceInfo()
*/
public final class TextAppearanceInfo implements Parcelable {
/**
* The text size (in pixels) for current {@link TextView}.
*/
private final @Px float mTextSize;
/**
* The LocaleList of the text.
*/
@NonNull private final LocaleList mTextLocales;
/**
* The font family name if the {@link Typeface} of the text is created from a system font
* family, otherwise this value should be null.
*/
@Nullable private final String mSystemFontFamilyName;
/**
* The weight of the text.
*/
private final @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int mTextFontWeight;
/**
* The style (normal, bold, italic, bold|italic) of the text, see {@link Typeface}.
*/
private final @Typeface.Style int mTextStyle;
/**
* Whether the transformation method applied to the current {@link TextView} is set to
* ALL CAPS.
*/
private final boolean mAllCaps;
/**
* The horizontal offset (in pixels) of the text shadow.
*/
private final @Px float mShadowDx;
/**
* The vertical offset (in pixels) of the text shadow.
*/
private final @Px float mShadowDy;
/**
* The blur radius (in pixels) of the text shadow.
*/
private final @Px float mShadowRadius;
/**
* The elegant text height, especially for less compacted complex script text.
*/
private final boolean mElegantTextHeight;
/**
* Whether to expand linespacing based on fallback fonts.
*/
private final boolean mFallbackLineSpacing;
/**
* The text letter-spacing (in ems), which determines the spacing between characters.
*/
private final float mLetterSpacing;
/**
* The font feature settings.
*/
@Nullable private final String mFontFeatureSettings;
/**
* The font variation settings.
*/
@Nullable private final String mFontVariationSettings;
/**
* The line-break strategies for text wrapping.
*/
private final @LineBreakConfig.LineBreakStyle int mLineBreakStyle;
/**
* The line-break word strategies for text wrapping.
*/
private final @LineBreakConfig.LineBreakWordStyle int mLineBreakWordStyle;
/**
* The extent by which text should be stretched horizontally. Returns 1.0 if not specified.
*/
private final float mTextScaleX;
/**
* The color of the text selection highlight.
*/
private final @ColorInt int mTextColorHighlight;
/**
* The current text color.
*/
private final @ColorInt int mTextColor;
/**
* The current color of the hint text.
*/
private final @ColorInt int mTextColorHint;
/**
* The text color for links.
*/
@Nullable private final ColorStateList mTextColorLink;
/**
* The max length of text.
*/
private final int mMaxLength;
public TextAppearanceInfo(@NonNull TextView textView) {
mTextSize = textView.getTextSize();
mTextLocales = textView.getTextLocales();
Typeface typeface = textView.getPaint().getTypeface();
String systemFontFamilyName = null;
int textFontWeight = -1;
if (typeface != null) {
systemFontFamilyName = typeface.getSystemFontFamilyName();
textFontWeight = typeface.getWeight();
}
mSystemFontFamilyName = systemFontFamilyName;
mTextFontWeight = textFontWeight;
mTextStyle = textView.getTypefaceStyle();
mAllCaps = textView.isAllCaps();
mShadowRadius = textView.getShadowRadius();
mShadowDx = textView.getShadowDx();
mShadowDy = textView.getShadowDy();
mElegantTextHeight = textView.isElegantTextHeight();
mFallbackLineSpacing = textView.isFallbackLineSpacing();
mLetterSpacing = textView.getLetterSpacing();
mFontFeatureSettings = textView.getFontFeatureSettings();
mFontVariationSettings = textView.getFontVariationSettings();
mLineBreakStyle = textView.getLineBreakStyle();
mLineBreakWordStyle = textView.getLineBreakWordStyle();
mTextScaleX = textView.getTextScaleX();
mTextColorHighlight = textView.getHighlightColor();
mTextColor = textView.getCurrentTextColor();
mTextColorHint = textView.getCurrentHintTextColor();
mTextColorLink = textView.getLinkTextColors();
int maxLength = -1;
for (InputFilter filter: textView.getFilters()) {
if (filter instanceof InputFilter.LengthFilter) {
maxLength = ((InputFilter.LengthFilter) filter).getMax();
// There is at most one LengthFilter.
break;
}
}
mMaxLength = maxLength;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeFloat(mTextSize);
mTextLocales.writeToParcel(dest, flags); // NonNull
dest.writeBoolean(mAllCaps);
dest.writeString8(mSystemFontFamilyName);
dest.writeInt(mTextFontWeight);
dest.writeInt(mTextStyle);
dest.writeFloat(mShadowDx);
dest.writeFloat(mShadowDy);
dest.writeFloat(mShadowRadius);
dest.writeBoolean(mElegantTextHeight);
dest.writeBoolean(mFallbackLineSpacing);
dest.writeFloat(mLetterSpacing);
dest.writeString8(mFontFeatureSettings);
dest.writeString8(mFontVariationSettings);
dest.writeInt(mLineBreakStyle);
dest.writeInt(mLineBreakWordStyle);
dest.writeFloat(mTextScaleX);
dest.writeInt(mTextColorHighlight);
dest.writeInt(mTextColor);
dest.writeInt(mTextColorHint);
dest.writeTypedObject(mTextColorLink, flags);
dest.writeInt(mMaxLength);
}
private TextAppearanceInfo(@NonNull Parcel in) {
mTextSize = in.readFloat();
mTextLocales = LocaleList.CREATOR.createFromParcel(in);
mAllCaps = in.readBoolean();
mSystemFontFamilyName = in.readString8();
mTextFontWeight = in.readInt();
mTextStyle = in.readInt();
mShadowDx = in.readFloat();
mShadowDy = in.readFloat();
mShadowRadius = in.readFloat();
mElegantTextHeight = in.readBoolean();
mFallbackLineSpacing = in.readBoolean();
mLetterSpacing = in.readFloat();
mFontFeatureSettings = in.readString8();
mFontVariationSettings = in.readString8();
mLineBreakStyle = in.readInt();
mLineBreakWordStyle = in.readInt();
mTextScaleX = in.readFloat();
mTextColorHighlight = in.readInt();
mTextColor = in.readInt();
mTextColorHint = in.readInt();
mTextColorLink = in.readTypedObject(ColorStateList.CREATOR);
mMaxLength = in.readInt();
}
@NonNull
public static final Creator<TextAppearanceInfo> CREATOR = new Creator<TextAppearanceInfo>() {
@Override
public TextAppearanceInfo createFromParcel(@NonNull Parcel in) {
return new TextAppearanceInfo(in);
}
@Override
public TextAppearanceInfo[] newArray(int size) {
return new TextAppearanceInfo[size];
}
};
/**
* Returns the text size (in pixels) for current {@link TextView}.
*/
public @Px float getTextSize() {
return mTextSize;
}
/**
* Returns the LocaleList of the text.
*/
@NonNull
public LocaleList getTextLocales() {
return mTextLocales;
}
/**
* Returns the font family name if the {@link Typeface} of the text is created from a
* system font family. Returns null if no {@link Typeface} is specified, or it is not created
* from a system font family.
*/
@Nullable
public String getFontFamilyName() {
return mSystemFontFamilyName;
}
/**
* Returns the weight of the text. Returns -1 when no {@link Typeface} is specified.
*/
public @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int getTextFontWeight() {
return mTextFontWeight;
}
/**
* Returns the style (normal, bold, italic, bold|italic) of the text. Returns
* {@link Typeface#NORMAL} when no {@link Typeface} is specified. See {@link Typeface} for
* more information.
*/
public @Typeface.Style int getTextStyle() {
return mTextStyle;
}
/**
* Returns whether the transformation method applied to the current {@link TextView} is set to
* ALL CAPS.
*/
public boolean isAllCaps() {
return mAllCaps;
}
/**
* Returns the horizontal offset (in pixels) of the text shadow.
*/
public @Px float getShadowDx() {
return mShadowDx;
}
/**
* Returns the vertical offset (in pixels) of the text shadow.
*/
public @Px float getShadowDy() {
return mShadowDy;
}
/**
* Returns the blur radius (in pixels) of the text shadow.
*/
public @Px float getShadowRadius() {
return mShadowRadius;
}
/**
* Returns {@code true} if the elegant height metrics flag is set. This setting selects font
* variants that have not been compacted to fit Latin-based vertical metrics, and also increases
* top and bottom bounds to provide more space.
*/
public boolean isElegantTextHeight() {
return mElegantTextHeight;
}
/**
* Returns whether to expand linespacing based on fallback fonts.
*
* @see TextView#setFallbackLineSpacing(boolean)
*/
public boolean isFallbackLineSpacing() {
return mFallbackLineSpacing;
}
/**
* Returns the text letter-spacing, which determines the spacing between characters.
* The value is in 'EM' units. Normally, this value is 0.0.
*/
public float getLetterSpacing() {
return mLetterSpacing;
}
/**
* Returns the font feature settings. Returns null if not specified.
*
* @see Paint#getFontFeatureSettings()
*/
@Nullable
public String getFontFeatureSettings() {
return mFontFeatureSettings;
}
/**
* Returns the font variation settings. Returns null if no variation is specified.
*
* @see Paint#getFontVariationSettings()
*/
@Nullable
public String getFontVariationSettings() {
return mFontVariationSettings;
}
/**
* Returns the line-break strategies for text wrapping.
*
* @see TextView#setLineBreakStyle(int)
*/
public @LineBreakConfig.LineBreakStyle int getLineBreakStyle() {
return mLineBreakStyle;
}
/**
* Returns the line-break word strategies for text wrapping.
*
* @see TextView#setLineBreakWordStyle(int)
*/
public @LineBreakConfig.LineBreakWordStyle int getLineBreakWordStyle() {
return mLineBreakWordStyle;
}
/**
* Returns the extent by which text should be stretched horizontally. Returns 1.0 if not
* specified.
*/
public float getTextScaleX() {
return mTextScaleX;
}
/**
* Returns the color of the text selection highlight.
*/
public @ColorInt int getTextColorHighlight() {
return mTextColorHighlight;
}
/**
* Returns the current text color.
*/
public @ColorInt int getTextColor() {
return mTextColor;
}
/**
* Returns the current color of the hint text.
*/
public @ColorInt int getTextColorHint() {
return mTextColorHint;
}
/**
* Returns the text color for links.
*/
@Nullable
public ColorStateList getTextColorLink() {
return mTextColorLink;
}
/**
* Returns the max length of text, which is used to set an input filter to constrain the text
* length to the specified number. Returns -1 when there is no {@link InputFilter.LengthFilter}
* in the Editor.
*/
public int getMaxLength() {
return mMaxLength;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TextAppearanceInfo)) return false;
TextAppearanceInfo that = (TextAppearanceInfo) o;
return Float.compare(that.mTextSize, mTextSize) == 0
&& mTextFontWeight == that.mTextFontWeight && mTextStyle == that.mTextStyle
&& mAllCaps == that.mAllCaps && Float.compare(that.mShadowDx, mShadowDx) == 0
&& Float.compare(that.mShadowDy, mShadowDy) == 0 && Float.compare(
that.mShadowRadius, mShadowRadius) == 0 && mMaxLength == that.mMaxLength
&& mElegantTextHeight == that.mElegantTextHeight
&& mFallbackLineSpacing == that.mFallbackLineSpacing && Float.compare(
that.mLetterSpacing, mLetterSpacing) == 0 && mLineBreakStyle == that.mLineBreakStyle
&& mLineBreakWordStyle == that.mLineBreakWordStyle
&& mTextColorHighlight == that.mTextColorHighlight && mTextColor == that.mTextColor
&& mTextColorLink.getDefaultColor() == that.mTextColorLink.getDefaultColor()
&& mTextColorHint == that.mTextColorHint && Objects.equals(
mTextLocales, that.mTextLocales) && Objects.equals(mSystemFontFamilyName,
that.mSystemFontFamilyName) && Objects.equals(mFontFeatureSettings,
that.mFontFeatureSettings) && Objects.equals(mFontVariationSettings,
that.mFontVariationSettings) && Float.compare(that.mTextScaleX, mTextScaleX) == 0;
}
@Override
public int hashCode() {
return Objects.hash(mTextSize, mTextLocales, mSystemFontFamilyName, mTextFontWeight,
mTextStyle, mAllCaps, mShadowDx, mShadowDy, mShadowRadius, mElegantTextHeight,
mFallbackLineSpacing, mLetterSpacing, mFontFeatureSettings, mFontVariationSettings,
mLineBreakStyle, mLineBreakWordStyle, mTextScaleX, mTextColorHighlight, mTextColor,
mTextColorHint, mTextColorLink, mMaxLength);
}
@Override
public String toString() {
return "TextAppearanceInfo{"
+ "mTextSize=" + mTextSize
+ ", mTextLocales=" + mTextLocales
+ ", mSystemFontFamilyName='" + mSystemFontFamilyName + '\''
+ ", mTextFontWeight=" + mTextFontWeight
+ ", mTextStyle=" + mTextStyle
+ ", mAllCaps=" + mAllCaps
+ ", mShadowDx=" + mShadowDx
+ ", mShadowDy=" + mShadowDy
+ ", mShadowRadius=" + mShadowRadius
+ ", mElegantTextHeight=" + mElegantTextHeight
+ ", mFallbackLineSpacing=" + mFallbackLineSpacing
+ ", mLetterSpacing=" + mLetterSpacing
+ ", mFontFeatureSettings='" + mFontFeatureSettings + '\''
+ ", mFontVariationSettings='" + mFontVariationSettings + '\''
+ ", mLineBreakStyle=" + mLineBreakStyle
+ ", mLineBreakWordStyle=" + mLineBreakWordStyle
+ ", mTextScaleX=" + mTextScaleX
+ ", mTextColorHighlight=" + mTextColorHighlight
+ ", mTextColor=" + mTextColor
+ ", mTextColorHint=" + mTextColorHint
+ ", mTextColorLink=" + mTextColorLink
+ ", mMaxLength=" + mMaxLength
+ '}';
}
}

View File

@@ -125,6 +125,7 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.TextAppearanceInfo;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationManager;
import android.widget.AdapterView.OnItemClickListener;
@@ -4630,14 +4631,17 @@ public class Editor {
(filter & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0;
boolean includeVisibleLineBounds =
(filter & InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS) != 0;
boolean includeTextAppearance =
(filter & InputConnection.CURSOR_UPDATE_FILTER_TEXT_APPEARANCE) != 0;
boolean includeAll =
(!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker
&& !includeVisibleLineBounds);
&& !includeVisibleLineBounds && !includeTextAppearance);
includeEditorBounds |= includeAll;
includeCharacterBounds |= includeAll;
includeInsertionMarker |= includeAll;
includeVisibleLineBounds |= includeAll;
includeTextAppearance |= includeAll;
final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder;
builder.reset();
@@ -4757,6 +4761,10 @@ public class Editor {
}
}
if (includeTextAppearance) {
TextAppearanceInfo textAppearanceInfo = new TextAppearanceInfo(mTextView);
builder.setTextAppearanceInfo(textAppearanceInfo);
}
imm.updateCursorAnchorInfo(mTextView, builder.build());
// Drop the immediate flag if any.

View File

@@ -231,7 +231,9 @@ public final class EditableInputConnection extends BaseInputConnection
| InputConnection.CURSOR_UPDATE_MONITOR;
final int knownFilterFlags = InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS
| InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER
| InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS;
| InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS
| InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS
| InputConnection.CURSOR_UPDATE_FILTER_TEXT_APPEARANCE;
// It is possible that any other bit is used as a valid flag in a future release.
// We should reject the entire request in such a case.