Refresh new extra data AccessibilityNodeInfo.ExtraRenderingInfo
Bug: 136404500
Test: atest AccessibilityTextActionTest AccessibilityNodeInfoTest
Change-Id: Ib1a8556d662d31ffbf3366e1482588ba058530cd
diff --git a/api/current.txt b/api/current.txt
index 24478f4..d7dd2ad 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -55621,6 +55621,7 @@
method public CharSequence getContentDescription();
method public int getDrawingOrder();
method public CharSequence getError();
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
method public android.os.Bundle getExtras();
method public CharSequence getHintText();
method public int getInputType();
@@ -55773,6 +55774,7 @@
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
@@ -55860,6 +55862,12 @@
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
}
+ public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
+ method @Nullable public android.util.Size getLayoutParams();
+ method public float getTextSizeInPx();
+ method public int getTextSizeUnit();
+ }
+
public static final class AccessibilityNodeInfo.RangeInfo {
ctor public AccessibilityNodeInfo.RangeInfo(int, float, float, float);
method public float getCurrent();
@@ -60842,6 +60850,7 @@
method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleLeft();
method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleRight();
method @android.view.ViewDebug.ExportedProperty(category="text") public float getTextSize();
+ method public int getTextSizeUnit();
method public int getTotalPaddingBottom();
method public int getTotalPaddingEnd();
method public int getTotalPaddingLeft();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0367536..56f933c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3692,6 +3692,31 @@
}
childrenForAccessibility.clear();
}
+ info.setAvailableExtraData(Collections.singletonList(
+ AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param info The info to which to add the extra data. Never {@code null}.
+ * @param extraDataKey A key specifying the type of extra data to add to the info. The
+ * extra data should be added to the {@link Bundle} returned by
+ * the info's {@link AccessibilityNodeInfo#getExtras} method. Never
+ * {@code null}.
+ * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
+ * {@code null} if the service provided no arguments.
+ *
+ */
+ @Override
+ public void addExtraDataToAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info,
+ @NonNull String extraDataKey, @Nullable Bundle arguments) {
+ if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+ final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+ AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+ extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+ info.setExtraRenderingInfo(extraRenderingInfo);
+ }
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bf2de14..eb4f9db 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,8 +50,12 @@
import android.util.Log;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
+import android.util.Size;
+import android.util.TypedValue;
import android.view.TouchDelegate;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.util.CollectionUtils;
@@ -634,6 +638,25 @@
public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
"android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ /**
+ * Key used to request extra data for accessibility scanning tool's purposes.
+ * The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
+ * info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
+ * argument.
+ * <p>
+ * The data can be retrieved from the {@link ExtraRenderingInfo} returned by
+ * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutParams},
+ * {@link ExtraRenderingInfo#getTextSizeInPx()} and
+ * {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both
+ * {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by
+ * {@link TextView}.
+ *
+ * @see #refreshWithExtraData(String, Bundle)
+ */
+
+ public static final String EXTRA_DATA_RENDERING_INFO_KEY =
+ "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
+
/** @hide */
public static final String EXTRA_DATA_REQUESTED_KEY =
"android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
@@ -804,6 +827,8 @@
private TouchDelegateInfo mTouchDelegateInfo;
+ private ExtraRenderingInfo mExtraRenderingInfo;
+
private IBinder mLeashedChild;
private IBinder mLeashedParent;
private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
@@ -991,6 +1016,7 @@
* @param extraDataKey The extra data requested. Data that must be requested
* with this mechanism is generally expensive to retrieve, so should only be
* requested when needed. See
+ * {@link #EXTRA_DATA_RENDERING_INFO_KEY},
* {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
* {@link #getAvailableExtraData()}.
* @param args A bundle of arguments for the request. These depend on the particular request.
@@ -1547,6 +1573,7 @@
* {@link #refreshWithExtraData(String, Bundle)}.
*
* @return An unmodifiable list of keys corresponding to extra data that can be requested.
+ * @see #EXTRA_DATA_RENDERING_INFO_KEY
* @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
*/
public List<String> getAvailableExtraData() {
@@ -2375,6 +2402,32 @@
}
/**
+ * Gets the conformance info if the node is meant to be refreshed with extra data.
+ *
+ * @return The conformance info.
+ */
+ @Nullable
+ public ExtraRenderingInfo getExtraRenderingInfo() {
+ return mExtraRenderingInfo;
+ }
+
+ /**
+ * Sets the conformance info if the node is meant to be refreshed with extra data.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param extraRenderingInfo The conformance info.
+ * @hide
+ */
+ public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) {
+ enforceNotSealed();
+ mExtraRenderingInfo = extraRenderingInfo;
+ }
+
+ /**
* Gets if the content of this node is invalid. For example,
* a date is not well-formed.
*
@@ -3695,6 +3748,10 @@
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mExtraRenderingInfo, DEFAULT.mExtraRenderingInfo)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (mLeashedChild != DEFAULT.mLeashedChild) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -3833,6 +3890,12 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeValue(mExtraRenderingInfo.getLayoutParams());
+ parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx());
+ parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit());
+ }
+
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeStrongBinder(mLeashedChild);
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -3941,6 +4004,9 @@
if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = (other.mCollectionItemInfo != null)
? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
+ if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+ mExtraRenderingInfo = (other.mExtraRenderingInfo != null)
+ ? ExtraRenderingInfo.obtain(other.mExtraRenderingInfo) : null;
}
private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -3955,6 +4021,9 @@
mCollectionItemInfo = (cii == null) ? null
: new CollectionItemInfo(cii.mRowIndex, cii.mRowSpan, cii.mColumnIndex,
cii.mColumnSpan, cii.mHeading, cii.mSelected);
+ ExtraRenderingInfo ti = other.mExtraRenderingInfo;
+ mExtraRenderingInfo = (ti == null) ? null
+ : new ExtraRenderingInfo(ti);
}
/**
@@ -4083,6 +4152,14 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+ mExtraRenderingInfo = ExtraRenderingInfo.obtain();
+ mExtraRenderingInfo.mLayoutParams = (Size) parcel.readValue(null);
+ mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
+ mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
+ }
+
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
mLeashedChild = parcel.readStrongBinder();
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5679,6 +5756,134 @@
}
/**
+ * Class with information of a view useful to evaluate accessibility needs. Developers can
+ * refresh the node with the key {@link #EXTRA_DATA_RENDERING_INFO_KEY} to fetch the text size
+ * and unit if it is {@link TextView} and the height and the width of layout params from
+ * {@link ViewGroup} or {@link TextView}.
+ *
+ * @see #EXTRA_DATA_RENDERING_INFO_KEY
+ * @see #refreshWithExtraData(String, Bundle)
+ */
+ public static final class ExtraRenderingInfo {
+ private static final int UNDEFINED_VALUE = -1;
+ private static final int MAX_POOL_SIZE = 20;
+ private static final SynchronizedPool<ExtraRenderingInfo> sPool =
+ new SynchronizedPool<>(MAX_POOL_SIZE);
+
+ private Size mLayoutParams;
+ private float mTextSizeInPx = UNDEFINED_VALUE;
+ private int mTextSizeUnit = UNDEFINED_VALUE;
+
+ /**
+ * Obtains a pooled instance.
+ * @hide
+ */
+ @NonNull
+ public static ExtraRenderingInfo obtain() {
+ final ExtraRenderingInfo info = sPool.acquire();
+ if (info == null) {
+ return new ExtraRenderingInfo(null);
+ }
+ return info;
+ }
+
+ /** Obtains a pooled instance that is a clone of another one. */
+ private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
+ ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
+ extraRenderingInfo.mLayoutParams = other.mLayoutParams;
+ extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
+ extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
+ return extraRenderingInfo;
+ }
+
+ /**
+ * Creates a new conformance info of a view, and this new instance is initialized from
+ * the given <code>other</code>.
+ *
+ * @param other The instance to clone.
+ */
+ private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) {
+ if (other != null) {
+ mLayoutParams = other.mLayoutParams;
+ mTextSizeInPx = other.mTextSizeInPx;
+ mTextSizeUnit = other.mTextSizeUnit;
+ }
+ }
+
+ /**
+ * @return a {@link Size} stores layout height and layout width of the view,
+ * or null otherwise.
+ */
+ public @Nullable Size getLayoutParams() {
+ return mLayoutParams;
+ }
+
+ /**
+ * Sets layout width and layout height of the view.
+ *
+ * @param width The layout width.
+ * @param height The layout height.
+ * @hide
+ */
+ public void setLayoutParams(int width, int height) {
+ mLayoutParams = new Size(width, height);
+ }
+
+ /**
+ * @return the text size of a {@code TextView}, or -1 otherwise.
+ */
+ public float getTextSizeInPx() {
+ return mTextSizeInPx;
+ }
+
+ /**
+ * Sets text size of the view.
+ *
+ * @param textSizeInPx The text size in pixels.
+ * @hide
+ */
+ public void setTextSizeInPx(float textSizeInPx) {
+ mTextSizeInPx = textSizeInPx;
+ }
+
+ /**
+ * @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
+ * {@code TextView}, or -1 otherwise.
+ *
+ * @see TypedValue#TYPE_DIMENSION
+ */
+ public int getTextSizeUnit() {
+ return mTextSizeUnit;
+ }
+
+ /**
+ * Sets text size unit of the view.
+ *
+ * @param textSizeUnit The text size unit.
+ * @hide
+ */
+ public void setTextSizeUnit(int textSizeUnit) {
+ mTextSizeUnit = textSizeUnit;
+ }
+
+ /**
+ * Recycles this instance.
+ *
+ * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ */
+ void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ private void clear() {
+ mLayoutParams = null;
+ mTextSizeInPx = UNDEFINED_VALUE;
+ mTextSizeUnit = UNDEFINED_VALUE;
+ }
+ }
+
+ /**
* @see android.os.Parcelable.Creator
*/
public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0182975..815cc5c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -727,6 +728,7 @@
@UnsupportedAppUsage
private Layout mLayout;
private boolean mLocalesChanged = false;
+ private int mTextSizeUnit = -1;
// True if setKeyListener() has been explicitly called
private boolean mListenerChanged = false;
@@ -3842,6 +3844,7 @@
ColorStateList mTextColorHint = null;
ColorStateList mTextColorLink = null;
int mTextSize = -1;
+ int mTextSizeUnit = -1;
LocaleList mTextLocales = null;
String mFontFamily = null;
Typeface mFontTypeface = null;
@@ -3869,6 +3872,7 @@
+ " mTextColorHint:" + mTextColorHint + "\n"
+ " mTextColorLink:" + mTextColorLink + "\n"
+ " mTextSize:" + mTextSize + "\n"
+ + " mTextSizeUnit:" + mTextSizeUnit + "\n"
+ " mTextLocales:" + mTextLocales + "\n"
+ " mFontFamily:" + mFontFamily + "\n"
+ " mFontTypeface:" + mFontTypeface + "\n"
@@ -3980,6 +3984,7 @@
case com.android.internal.R.styleable.TextAppearance_textSize:
attributes.mTextSize =
appearance.getDimensionPixelSize(attr, attributes.mTextSize);
+ attributes.mTextSizeUnit = appearance.peekValue(attr).getComplexUnit();
break;
case com.android.internal.R.styleable.TextAppearance_textLocale:
final String localeString = appearance.getString(attr);
@@ -4073,6 +4078,7 @@
}
if (attributes.mTextSize != -1) {
+ mTextSizeUnit = attributes.mTextSizeUnit;
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
@@ -4295,6 +4301,7 @@
r = c.getResources();
}
+ mTextSizeUnit = unit;
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
shouldRequestLayout);
}
@@ -4315,6 +4322,17 @@
}
/**
+ * Gets the text size unit defined by the developer. It may be specified in resources or be
+ * passed as the unit argument of {@link #setTextSize(int, float)} at runtime.
+ *
+ * @return the dimension type of the text size unit originally defined.
+ * @see TypedValue#TYPE_DIMENSION
+ */
+ public int getTextSizeUnit() {
+ return mTextSizeUnit;
+ }
+
+ /**
* Gets the extent by which text should be stretched horizontally.
* This will usually be 1.0.
* @return The horizontal scale factor.
@@ -11769,8 +11787,14 @@
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
- info.setAvailableExtraData(
- Arrays.asList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY));
+ info.setAvailableExtraData(Arrays.asList(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ ));
+ } else {
+ info.setAvailableExtraData(Arrays.asList(
+ EXTRA_DATA_RENDERING_INFO_KEY
+ ));
}
if (isFocused()) {
@@ -11824,11 +11848,7 @@
@Override
public void addExtraDataToAccessibilityNodeInfo(
AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
- // The only extra data we support requires arguments.
- if (arguments == null) {
- return;
- }
- if (extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+ if (arguments != null && extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
int positionInfoStartIndex = arguments.getInt(
EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
int positionInfoLength = arguments.getInt(
@@ -11856,6 +11876,15 @@
}
}
info.getExtras().putParcelableArray(extraDataKey, boundingRects);
+ return;
+ }
+ if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+ final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+ AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+ extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+ extraRenderingInfo.setTextSizeInPx(getTextSize());
+ extraRenderingInfo.setTextSizeUnit(getTextSizeUnit());
+ info.setExtraRenderingInfo(extraRenderingInfo);
}
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index ade1e0d..79e7c50 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 38;
+ private static final int NUM_MARSHALLED_PROPERTIES = 39;
/**
* The number of properties that are purposely not marshalled