From 50f792e09d33bb4ce38c53b7e1b55176ebccb4bf Mon Sep 17 00:00:00 2001 From: czq Date: Thu, 15 Sep 2022 16:29:43 +0800 Subject: [PATCH] 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 --- core/api/current.txt | 32 ++ .../view/inputmethod/CursorAnchorInfo.java | 58 +- .../view/inputmethod/InputConnection.java | 42 +- .../view/inputmethod/TextAppearanceInfo.java | 509 ++++++++++++++++++ core/java/android/widget/Editor.java | 10 +- .../inputmethod/EditableInputConnection.java | 4 +- 6 files changed, 634 insertions(+), 21 deletions(-) create mode 100644 core/java/android/view/inputmethod/TextAppearanceInfo.java diff --git a/core/api/current.txt b/core/api/current.txt index e5cc625038ddf..4f32aaa22fc15 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -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 getVisibleLineBounds(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator 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 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 CREATOR; + } + public final class TextAttribute implements android.os.Parcelable { method public int describeContents(); method @NonNull public android.os.PersistableBundle getExtras(); diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index a8ed96eb383df..2d974dbc8b6ef 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -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. diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 7d268a925f60e..22465b9819121 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -1052,8 +1052,10 @@ public interface InputConnection { * used together with {@link #CURSOR_UPDATE_MONITOR}. *

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

@@ -1071,7 +1073,8 @@ public interface InputConnection { *

* 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 { *

* 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}. *

@@ -1102,8 +1106,8 @@ public interface InputConnection { *

* 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}. *

*/ int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 1 << 3; @@ -1118,8 +1122,8 @@ public interface InputConnection { *

* 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}. *

*/ int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 1 << 4; @@ -1133,13 +1137,28 @@ public interface InputConnection { * {@link InputConnection#requestCursorUpdates(int)} again with this flag off. *

* 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}. *

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

+ * 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}. + *

+ */ + 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{} diff --git a/core/java/android/view/inputmethod/TextAppearanceInfo.java b/core/java/android/view/inputmethod/TextAppearanceInfo.java new file mode 100644 index 0000000000000..1df4fc54089e9 --- /dev/null +++ b/core/java/android/view/inputmethod/TextAppearanceInfo.java @@ -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 CREATOR = new Creator() { + @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 + + '}'; + } +} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 8f590f89fa645..56524a2c01ef5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -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. diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java index f260d7dfc6a63..0bb9c2935710a 100644 --- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java +++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java @@ -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.