Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fe100ea authored by Yinglei Wang's avatar Yinglei Wang
Browse files

Implement supplemental description API

Design doc: https://docs.google.com/document/d/11IQj50EhE_UIIRmlpO32e5pA7v5DWBxnOF4JI6UOPTI/edit?usp=sharing

Test: CTS test added
Flag: android.view.accessibility.supplemental_description
Bug: 375266174

Change-Id: Ieba7d81ec7c833cdd39a97e3c4b35d2117b739e6
parent c6a025ef
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1609,6 +1609,7 @@ package android {
    field public static final int summaryColumn = 16843426; // 0x10102a2
    field public static final int summaryOff = 16843248; // 0x10101f0
    field public static final int summaryOn = 16843247; // 0x10101ef
    field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription;
    field public static final int supportedTypes = 16844369; // 0x1010651
    field public static final int supportsAssist = 16844016; // 0x10104f0
    field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
@@ -53061,6 +53062,7 @@ package android.view {
    method public android.animation.StateListAnimator getStateListAnimator();
    method protected int getSuggestedMinimumHeight();
    method protected int getSuggestedMinimumWidth();
    method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
    method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
    method @Deprecated public int getSystemUiVisibility();
    method public Object getTag();
@@ -53441,6 +53443,7 @@ package android.view {
    method public void setSoundEffectsEnabled(boolean);
    method public void setStateDescription(@Nullable CharSequence);
    method public void setStateListAnimator(android.animation.StateListAnimator);
    method @FlaggedApi("android.view.accessibility.supplemental_description") public void setSupplementalDescription(@Nullable CharSequence);
    method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
    method @Deprecated public void setSystemUiVisibility(int);
    method public void setTag(Object);
@@ -55047,6 +55050,7 @@ package android.view.accessibility {
    field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
    field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
    field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION = 32768; // 0x8000
    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
    field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityEvent> CREATOR;
@@ -55209,6 +55213,7 @@ package android.view.accessibility {
    method @Nullable public android.view.accessibility.AccessibilityNodeInfo getParent(int);
    method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
    method @Nullable public CharSequence getStateDescription();
    method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
    method public CharSequence getText();
    method public int getTextSelectionEnd();
    method public int getTextSelectionStart();
@@ -55317,6 +55322,7 @@ package android.view.accessibility {
    method public void setSource(android.view.View);
    method public void setSource(android.view.View, int);
    method public void setStateDescription(@Nullable CharSequence);
    method @FlaggedApi("android.view.accessibility.supplemental_description") public void setSupplementalDescription(@Nullable CharSequence);
    method public void setText(CharSequence);
    method public void setTextEntryKey(boolean);
    method public void setTextSelectable(boolean);
+93 −0
Original line number Diff line number Diff line
@@ -181,6 +181,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.Flags;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
@@ -4863,6 +4864,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private CharSequence mContentDescription;
    /**
     * Brief supplemental information for view and is primarily used for accessibility support.
     */
    private CharSequence mSupplementalDescription;
    /**
     * If this view represents a distinct part of the window, it can have a title that labels the
     * area.
@@ -6534,6 +6540,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                case R.styleable.View_contentSensitivity:
                    setContentSensitivity(a.getInt(attr, CONTENT_SENSITIVITY_AUTO));
                    break;
                default: {
                    if (Flags.supplementalDescription()) {
                        if (attr == com.android.internal.R.styleable.View_supplementalDescription) {
                            setSupplementalDescription(a.getString(attr));
                        }
                    }
                }
            }
        }
@@ -11805,6 +11818,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return mContentDescription;
    }
    /**
     * Returns the {@link View}'s supplemental description.
     * <p>
     * A supplemental description provides
     * brief supplemental information for this node, such as the purpose of the node when
     * that purpose is not conveyed within its textual representation. For example, if a
     * dropdown select has a purpose of setting font family, the supplemental description
     * could be "font family". If this node has children, its supplemental description serves
     * as additional information and is not intended to replace any existing information
     * in the subtree. This is different from the {@link #getContentDescription()} in that
     * this description is purely supplemental while a content description may be used
     * to replace a description for a node or its subtree that an assistive technology
     * would otherwise compute based on other properties of the node and its descendants.
     *
     * <p>
     * <strong>Note:</strong> Do not override this method, as it will have no
     * effect on the supplemental description presented to accessibility services.
     * You must call {@link #setSupplementalDescription(CharSequence)} to modify the
     * supplemental description.
     *
     * @return the supplemental description
     * @see #setSupplementalDescription(CharSequence)
     * @see #getContentDescription()
     * @attr ref android.R.styleable#View_supplementalDescription
     */
    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
    @ViewDebug.ExportedProperty(category = "accessibility")
    @InspectableProperty
    @Nullable
    public CharSequence getSupplementalDescription() {
        return mSupplementalDescription;
    }
    /**
     * Sets the {@link View}'s state description.
     * <p>
@@ -11891,6 +11937,53 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
    }
    /**
     * Sets the {@link View}'s supplemental description.
     * <p>
     * A supplemental description provides
     * brief supplemental information for this node, such as the purpose of the node when
     * that purpose is not conveyed within its textual representation. For example, if a
     * dropdown select has a purpose of setting font family, the supplemental description
     * could be "font family". If this node has children, its supplemental description serves
     * as additional information and is not intended to replace any existing information
     * in the subtree. This is different from the {@link #setContentDescription(CharSequence)}
     * in that this description is purely supplemental while a content description may be used
     * to replace a description for a node or its subtree that an assistive technology
     * would otherwise compute based on other properties of the node and its descendants.
     *
     * <p>
     * This should omit role or state. Role refers to the kind of user-interface element the View
     * is, such as a Button or Checkbox. State refers to a frequently changing property of the View,
     * such as an On/Off state of a button or the audio level of a volume slider.
     *
     * @param supplementalDescription The supplemental description.
     * @see #getSupplementalDescription()
     * @see #setContentDescription(CharSequence)
     * @see #setStateDescription(CharSequence) for state changes.
     * @attr ref android.R.styleable#View_supplementalDescription
     */
    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
    @RemotableViewMethod
    public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
        if (mSupplementalDescription == null) {
            if (supplementalDescription == null) {
                return;
            }
        } else if (mSupplementalDescription.equals(supplementalDescription)) {
            return;
        }
        mSupplementalDescription = supplementalDescription;
        final boolean nonEmptyDesc = supplementalDescription != null
                && !supplementalDescription.isEmpty();
        if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
            notifySubtreeAccessibilityStateChangedIfNeeded();
        } else {
            notifyViewAccessibilityStateChangedIfNeeded(
                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION);
        }
    }
    /**
     * Sets the id of a view that screen readers are requested to visit after this view.
     *
+23 −1
Original line number Diff line number Diff line
@@ -810,6 +810,20 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
    @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
    public static final int CONTENT_CHANGE_TYPE_EXPANDED = 1 << 14;

    /**
     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
     * The source node changed its supplemental description, which is returned by
     * {@link AccessibilityNodeInfo#getSupplementalDescription()}.
     * The view changing its supplemental description should call
     * {@link AccessibilityNodeInfo#setSupplementalDescription(CharSequence)} and
     * then send this event.
     *
     * @see AccessibilityNodeInfo#getSupplementalDescription()
     * @see AccessibilityNodeInfo#setSupplementalDescription(CharSequence)
     */
    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
    public static final int CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION = 1 << 15;

    // Speech state change types.

    /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
@@ -942,6 +956,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
                CONTENT_CHANGE_TYPE_CONTENT_INVALID,
                CONTENT_CHANGE_TYPE_ERROR,
                CONTENT_CHANGE_TYPE_ENABLED,
                CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION,
            })
    public @interface ContentChangeTypes {}

@@ -1222,7 +1237,14 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
                return "CONTENT_CHANGE_TYPE_CONTENT_INVALID";
            case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR";
            case CONTENT_CHANGE_TYPE_ENABLED: return "CONTENT_CHANGE_TYPE_ENABLED";
            default: return Integer.toHexString(type);
            default: {
                if (Flags.supplementalDescription()) {
                    if (type == CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION) {
                        return "CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION";
                    }
                }
                return Integer.toHexString(type);
            }
        }
    }

+65 −0
Original line number Diff line number Diff line
@@ -1085,6 +1085,7 @@ public class AccessibilityNodeInfo implements Parcelable {
    private CharSequence mPaneTitle;
    private CharSequence mStateDescription;
    private CharSequence mContentDescription;
    private CharSequence mSupplementalDescription;
    private CharSequence mTooltipText;
    private String mViewIdResourceName;
    private String mUniqueId;
@@ -3686,6 +3687,27 @@ public class AccessibilityNodeInfo implements Parcelable {
        return mContentDescription;
    }

    /**
     * Gets the supplemental description of this node. A supplemental description provides
     * brief supplemental information for this node, such as the purpose of the node when
     * that purpose is not conveyed within its textual representation. For example, if a
     * dropdown select has a purpose of setting font family, the supplemental description
     * could be "font family". If this node has children, its supplemental description serves
     * as additional information and is not intended to replace any existing information
     * in the subtree. This is different from the {@link #getContentDescription()} in that
     * this description is purely supplemental while a content description may be used
     * to replace a description for a node or its subtree that an assistive technology
     * would otherwise compute based on other properties of the node and its descendants.
     *
     * @return The supplemental description.
     * @see #setSupplementalDescription(CharSequence)
     * @see #getContentDescription()
     */
    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
    @Nullable
    public CharSequence getSupplementalDescription() {
        return mSupplementalDescription;
    }

    /**
     * Sets the state description of this node.
@@ -3723,6 +3745,35 @@ public class AccessibilityNodeInfo implements Parcelable {
                : contentDescription.subSequence(0, contentDescription.length());
    }

    /**
     * Sets the supplemental description of this node. A supplemental description provides
     * brief supplemental information for this node, such as the purpose of the node when
     * that purpose is not conveyed within its textual representation. For example, if a
     * dropdown select has a purpose of setting font family, the supplemental description
     * could be "font family". If this node has children, its supplemental description serves
     * as additional information and is not intended to replace any existing information
     * in the subtree. This is different from the {@link #setContentDescription(CharSequence)}
     * in that this description is purely supplemental while a content description may be used
     * to replace a description for a node or its subtree that an assistive technology
     * would otherwise compute based on other properties of the node and its descendants.
     * <p>
     *   <strong>Note:</strong> Cannot be called from an
     *   {@link android.accessibilityservice.AccessibilityService}.
     *   This class is made immutable before being delivered to an AccessibilityService.
     *
     * @param supplementalDescription The supplemental description.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     * @see #getSupplementalDescription()
     * @see #setContentDescription(CharSequence)
     */
    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
    public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
        enforceNotSealed();
        mSupplementalDescription = (supplementalDescription == null) ? null
                : supplementalDescription.subSequence(0, supplementalDescription.length());
    }

    /**
     * Gets the tooltip text of this node.
     *
@@ -4657,6 +4708,10 @@ public class AccessibilityNodeInfo implements Parcelable {
            nonDefaultFields |= bitAt(fieldIndex);
        }
        fieldIndex++;
        if (!Objects.equals(mSupplementalDescription, DEFAULT.mSupplementalDescription)) {
            nonDefaultFields |= bitAt(fieldIndex);
        }
        fieldIndex++;
        if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) {
            nonDefaultFields |= bitAt(fieldIndex);
        }
@@ -4843,6 +4898,9 @@ public class AccessibilityNodeInfo implements Parcelable {
        if (isBitSet(nonDefaultFields, fieldIndex++)) {
            parcel.writeCharSequence(mContentDescription);
        }
        if (isBitSet(nonDefaultFields, fieldIndex++)) {
            parcel.writeCharSequence(mSupplementalDescription);
        }
        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mContainerTitle);
@@ -4950,6 +5008,7 @@ public class AccessibilityNodeInfo implements Parcelable {
        mError = other.mError;
        mStateDescription = other.mStateDescription;
        mContentDescription = other.mContentDescription;
        mSupplementalDescription = other.mSupplementalDescription;
        mPaneTitle = other.mPaneTitle;
        mTooltipText = other.mTooltipText;
        mContainerTitle = other.mContainerTitle;
@@ -5119,6 +5178,9 @@ public class AccessibilityNodeInfo implements Parcelable {
        if (isBitSet(nonDefaultFields, fieldIndex++)) {
            mContentDescription = parcel.readCharSequence();
        }
        if (isBitSet(nonDefaultFields, fieldIndex++)) {
            mSupplementalDescription = parcel.readCharSequence();
        }
        if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
        if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
        if (isBitSet(nonDefaultFields, fieldIndex++)) mContainerTitle = parcel.readCharSequence();
@@ -5483,6 +5545,9 @@ public class AccessibilityNodeInfo implements Parcelable {
        builder.append("; maxTextLength: ").append(mMaxTextLength);
        builder.append("; stateDescription: ").append(mStateDescription);
        builder.append("; contentDescription: ").append(mContentDescription);
        if (Flags.supplementalDescription()) {
            builder.append("; supplementalDescription: ").append(mSupplementalDescription);
        }
        builder.append("; tooltipText: ").append(mTooltipText);
        builder.append("; containerTitle: ").append(mContainerTitle);
        builder.append("; viewIdResName: ").append(mViewIdResourceName);
+7 −0
Original line number Diff line number Diff line
@@ -214,6 +214,13 @@ flag {
    }
}

flag {
    name: "supplemental_description"
    namespace: "accessibility"
    description: "Feature flag for supplemental description api"
    bug: "375266174"
}

flag {
    name: "support_multiple_labeledby"
    namespace: "accessibility"
Loading