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

Commit d89905f0 authored by Phil Weaver's avatar Phil Weaver Committed by Eugene Susla
Browse files

Re-commit: Add accessibility support for tooltips

This re-commits I91265594c5ac3ecbc9ae487c7d227a460773f920
with a fix in parcelling logic

Allowing accessibility services to get the tooltip text
and show and hide tooltips.

Bug: 64836990
Test: Adding CTS tests for new APIs.
Change-Id: I4fb3c53c0e2b53fd9ecb59e034284eb575a87ed6
parent f80ebafa
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1809,6 +1809,7 @@ package android {
  public static final class R.id {
    ctor public R.id();
    field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
    field public static final int accessibilityActionHideTooltip = 16908357; // 0x1020045
    field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
    field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
    field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
@@ -1817,6 +1818,7 @@ package android {
    field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038
    field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
    field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
    field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044
    field public static final int addToDictionary = 16908330; // 0x102002a
    field public static final int autofill = 16908355; // 0x1020043
    field public static final int background = 16908288; // 0x1020000
@@ -48386,6 +48388,7 @@ package android.view.accessibility {
    method public java.lang.CharSequence getText();
    method public int getTextSelectionEnd();
    method public int getTextSelectionStart();
    method public java.lang.CharSequence getTooltipText();
    method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter();
    method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore();
    method public java.lang.String getViewIdResourceName();
@@ -48473,6 +48476,7 @@ package android.view.accessibility {
    method public void setSource(android.view.View, int);
    method public void setText(java.lang.CharSequence);
    method public void setTextSelection(int, int);
    method public void setTooltipText(java.lang.CharSequence);
    method public void setTraversalAfter(android.view.View);
    method public void setTraversalAfter(android.view.View, int);
    method public void setTraversalBefore(android.view.View);
@@ -48542,6 +48546,7 @@ package android.view.accessibility {
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_MOVE_WINDOW;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
@@ -48561,6 +48566,7 @@ package android.view.accessibility {
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN;
    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_TOOLTIP;
  }
  public static final class AccessibilityNodeInfo.CollectionInfo {
+39 −21
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
package android.view;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -8548,6 +8550,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        info.setLongClickable(isLongClickable());
        info.setContextClickable(isContextClickable());
        info.setLiveRegion(getAccessibilityLiveRegion());
        if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipText != null)) {
            info.setTooltipText(mTooltipInfo.mTooltipText);
            info.addAction((mTooltipInfo.mTooltipPopup == null)
                    ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP
                    : AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP);
        }
        // TODO: These make sense only if we are in an AdapterView but all
        // views can be selected. Maybe from accessibility perspective
@@ -8951,8 +8959,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            return;
        }
        mAccessibilityTraversalBeforeId = beforeId;
        notifyAccessibilityStateChanged(
                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
    }
    /**
@@ -8995,8 +9002,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            return;
        }
        mAccessibilityTraversalAfterId = afterId;
        notifyAccessibilityStateChanged(
                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
    }
    /**
@@ -9038,8 +9044,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                && mID == View.NO_ID) {
            mID = generateViewId();
        }
        notifyAccessibilityStateChanged(
                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
    }
    /**
@@ -10539,7 +10544,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        if (pflags3 != mPrivateFlags3) {
            mPrivateFlags3 = pflags3;
            notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
            notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
        }
    }
@@ -11367,8 +11372,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
            mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT)
                    & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
            notifyAccessibilityStateChanged(
                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
            notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
        }
    }
@@ -11427,8 +11431,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) {
                notifyAccessibilitySubtreeChanged();
            } else {
                notifyAccessibilityStateChanged(
                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
            }
        }
    }
@@ -11838,8 +11841,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                        || getAccessibilitySelectionEnd() != end)
                        && (start == end)) {
                    setAccessibilitySelection(start, end);
                    notifyAccessibilityStateChanged(
                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                    notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
                    return true;
                }
            } break;
@@ -11856,6 +11858,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    return true;
                }
            } break;
            case R.id.accessibilityActionShowTooltip: {
                if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipPopup != null)) {
                    // Tooltip already showing
                    return false;
                }
                return showLongClickTooltip(0, 0);
            }
            case R.id.accessibilityActionHideTooltip: {
                if ((mTooltipInfo == null) || (mTooltipInfo.mTooltipPopup == null)) {
                    // No tooltip showing
                    return false;
                }
                hideTooltip();
                return true;
            }
        }
        return false;
    }
@@ -13889,12 +13906,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                if (oldIncludeForAccessibility != includeForAccessibility()) {
                    notifyAccessibilitySubtreeChanged();
                } else {
                    notifyAccessibilityStateChanged(
                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                    notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
                }
            } else if ((changed & ENABLED_MASK) != 0) {
                notifyAccessibilityStateChanged(
                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
            }
        }
    }
@@ -21854,8 +21869,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            if (selected) {
                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
            } else {
                notifyAccessibilityStateChanged(
                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
            }
        }
    }
@@ -27034,6 +27048,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        final boolean fromTouch = (mPrivateFlags3 & PFLAG3_FINGER_DOWN) == PFLAG3_FINGER_DOWN;
        mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText);
        mAttachInfo.mTooltipHost = this;
        // The available accessibility actions have changed
        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
        return true;
    }
@@ -27052,6 +27068,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        if (mAttachInfo != null) {
            mAttachInfo.mTooltipHost = null;
        }
        // The available accessibility actions have changed
        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
    }
    private boolean showLongClickTooltip(int x, int y) {
@@ -27060,8 +27078,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return showTooltip(x, y, true);
    }
    private void showHoverTooltip() {
        showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false);
    private boolean showHoverTooltip() {
        return showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false);
    }
    boolean dispatchTooltipHoverEvent(MotionEvent event) {
+56 −0
Original line number Diff line number Diff line
@@ -727,6 +727,7 @@ public class AccessibilityNodeInfo implements Parcelable {
    private CharSequence mError;
    private CharSequence mPaneTitle;
    private CharSequence mContentDescription;
    private CharSequence mTooltipText;
    private String mViewIdResourceName;
    private ArrayList<String> mExtraDataKeys;

@@ -2654,6 +2655,34 @@ public class AccessibilityNodeInfo implements Parcelable {
                : contentDescription.subSequence(0, contentDescription.length());
    }

    /**
     * Gets the tooltip text of this node.
     *
     * @return The tooltip text.
     */
    @Nullable
    public CharSequence getTooltipText() {
        return mTooltipText;
    }

    /**
     * Sets the tooltip text of this node.
     * <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 tooltipText The tooltip text.
     *
     * @throws IllegalStateException If called from an AccessibilityService.
     */
    public void setTooltipText(@Nullable CharSequence tooltipText) {
        enforceNotSealed();
        mTooltipText = (tooltipText == null) ? null
                : tooltipText.subSequence(0, tooltipText.length());
    }

    /**
     * Sets the view for which the view represented by this info serves as a
     * label for accessibility purposes.
@@ -3209,6 +3238,10 @@ public class AccessibilityNodeInfo implements Parcelable {
            nonDefaultFields |= bitAt(fieldIndex);
        }
        fieldIndex++;
        if (!Objects.equals(mTooltipText, DEFAULT.mTooltipText)) {
            nonDefaultFields |= bitAt(fieldIndex);
        }
        fieldIndex++;
        if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) {
            nonDefaultFields |= bitAt(fieldIndex);
        }
@@ -3329,6 +3362,8 @@ public class AccessibilityNodeInfo implements Parcelable {
            parcel.writeCharSequence(mContentDescription);
        }
        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);

        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);

        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart);
@@ -3401,6 +3436,7 @@ public class AccessibilityNodeInfo implements Parcelable {
        mError = other.mError;
        mContentDescription = other.mContentDescription;
        mPaneTitle = other.mPaneTitle;
        mTooltipText = other.mTooltipText;
        mViewIdResourceName = other.mViewIdResourceName;

        if (mActions != null) mActions.clear();
@@ -3522,6 +3558,7 @@ public class AccessibilityNodeInfo implements Parcelable {
            mContentDescription = parcel.readCharSequence();
        }
        if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
        if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
        if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();

        if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt();
@@ -3684,6 +3721,10 @@ public class AccessibilityNodeInfo implements Parcelable {
                return "ACTION_SET_PROGRESS";
            case R.id.accessibilityActionContextClick:
                return "ACTION_CONTEXT_CLICK";
            case R.id.accessibilityActionShowTooltip:
                return "ACTION_SHOW_TOOLTIP";
            case R.id.accessibilityActionHideTooltip:
                return "ACTION_HIDE_TOOLTIP";
            default:
                return "ACTION_UNKNOWN";
        }
@@ -3797,6 +3838,7 @@ public class AccessibilityNodeInfo implements Parcelable {
        builder.append("; error: ").append(mError);
        builder.append("; maxTextLength: ").append(mMaxTextLength);
        builder.append("; contentDescription: ").append(mContentDescription);
        builder.append("; tooltipText: ").append(mTooltipText);
        builder.append("; viewIdResName: ").append(mViewIdResourceName);

        builder.append("; checkable: ").append(isCheckable());
@@ -4211,6 +4253,20 @@ public class AccessibilityNodeInfo implements Parcelable {
        public static final AccessibilityAction ACTION_MOVE_WINDOW =
                new AccessibilityAction(R.id.accessibilityActionMoveWindow);

        /**
         * Action to show a tooltip. A node should expose this action only for views with tooltip
         * text that but are not currently showing a tooltip.
         */
        public static final AccessibilityAction ACTION_SHOW_TOOLTIP =
                new AccessibilityAction(R.id.accessibilityActionShowTooltip);

        /**
         * Action to hide a tooltip. A node should expose this action only for views that are
         * currently showing a tooltip.
         */
        public static final AccessibilityAction ACTION_HIDE_TOOLTIP =
                new AccessibilityAction(R.id.accessibilityActionHideTooltip);

        private final int mActionId;
        private final CharSequence mLabel;

+5 −0
Original line number Diff line number Diff line
@@ -163,4 +163,9 @@
  <!-- Action used to manually trigger an autofill request -->
  <item type="id" name="autofill" />

    <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_TOOLTIP}. -->
    <item type="id" name="accessibilityActionShowTooltip" />

    <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_HIDE_TOOLTIP}. -->
    <item type="id" name="accessibilityActionHideTooltip" />
</resources>
+2 −0
Original line number Diff line number Diff line
@@ -2874,6 +2874,8 @@
    </public-group>

    <public-group type="id" first-id="0x01020044">
      <public name="accessibilityActionShowTooltip" />
      <public name="accessibilityActionHideTooltip" />
    </public-group>

    <public-group type="string" first-id="0x0104001b">