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

Commit 5d25f429 authored by Phil Weaver's avatar Phil Weaver Committed by Android (Google) Code Review
Browse files

Merge "Make a11y clickable span work after node recycle" into oc-dev

parents 45ff1d1a 23161e71
Loading
Loading
Loading
Loading
+23 −20
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@
package android.text.style;

import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
import static android.view.accessibility.AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID;
import static android.view.accessibility.AccessibilityNodeInfo.UNDEFINED_NODE_ID;
import static android.view.accessibility.AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;

import android.os.Bundle;
import android.os.Parcel;
@@ -24,13 +27,11 @@ import android.text.ParcelableSpan;
import android.text.Spanned;
import android.text.TextUtils;
import android.view.View;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;

import com.android.internal.R;

import java.lang.ref.WeakReference;


/**
 * {@link ClickableSpan} cannot be parceled, but accessibility services need to be able to cause
 * their callback handlers to be called. This class serves as a parcelable placeholder for the
@@ -47,10 +48,9 @@ public class AccessibilityClickableSpan extends ClickableSpan
    // The id of the span this one replaces
    private final int mOriginalClickableSpanId;

    // Only retain a weak reference to the node to avoid referencing cycles that could create memory
    // leaks.
    private WeakReference<AccessibilityNodeInfo> mAccessibilityNodeInfoRef;

    private int mWindowId = UNDEFINED_WINDOW_ID;
    private long mSourceNodeId = UNDEFINED_NODE_ID;
    private int mConnectionId = UNDEFINED_CONNECTION_ID;

    /**
     * @param originalClickableSpanId The id of the span this one replaces
@@ -110,13 +110,15 @@ public class AccessibilityClickableSpan extends ClickableSpan
    }

    /**
     * Set the accessibilityNodeInfo that this placeholder belongs to. This node is not
     * included in the parceling logic, and must be set to allow the onClick handler to function.
     * Configure this object to perform clicks on the view that contains the original span.
     *
     * @param accessibilityNodeInfo The info this span is part of
     * @param accessibilityNodeInfo The info corresponding to the view containing the original
     *                              span.
     */
    public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) {
        mAccessibilityNodeInfoRef = new WeakReference<>(accessibilityNodeInfo);
    public void copyConnectionDataFrom(AccessibilityNodeInfo accessibilityNodeInfo) {
        mConnectionId = accessibilityNodeInfo.getConnectionId();
        mWindowId = accessibilityNodeInfo.getWindowId();
        mSourceNodeId = accessibilityNodeInfo.getSourceNodeId();
    }

    /**
@@ -128,17 +130,18 @@ public class AccessibilityClickableSpan extends ClickableSpan
     */
    @Override
    public void onClick(View unused) {
        if (mAccessibilityNodeInfoRef == null) {
            return;
        }
        AccessibilityNodeInfo info = mAccessibilityNodeInfoRef.get();
        if (info == null) {
            return;
        }
        Bundle arguments = new Bundle();
        arguments.putParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN, this);

        info.performAction(R.id.accessibilityActionClickOnClickableSpan, arguments);
        if ((mWindowId == UNDEFINED_WINDOW_ID) || (mSourceNodeId == UNDEFINED_NODE_ID)
                || (mConnectionId == UNDEFINED_CONNECTION_ID)) {
            throw new RuntimeException(
                    "ClickableSpan for accessibility service not properly initialized");
        }

        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
        client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
                R.id.accessibilityActionClickOnClickableSpan, arguments);
    }

    public static final Parcelable.Creator<AccessibilityClickableSpan> CREATOR =
+2 −2
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ public class AccessibilityURLSpan extends URLSpan implements Parcelable {
     * Delegated to AccessibilityClickableSpan
     * @param accessibilityNodeInfo
     */
    public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) {
        mAccessibilityClickableSpan.setAccessibilityNodeInfo(accessibilityNodeInfo);
    public void copyConnectionDataFrom(AccessibilityNodeInfo accessibilityNodeInfo) {
        mAccessibilityClickableSpan.copyConnectionDataFrom(accessibilityNodeInfo);
    }
}
+18 −7
Original line number Diff line number Diff line
@@ -695,7 +695,7 @@ public class AccessibilityNodeInfo implements Parcelable {
    private boolean mSealed;

    // Data.
    private int mWindowId = UNDEFINED_ITEM_ID;
    private int mWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
    private long mSourceNodeId = UNDEFINED_NODE_ID;
    private long mParentNodeId = UNDEFINED_NODE_ID;
    private long mLabelForId = UNDEFINED_NODE_ID;
@@ -2417,12 +2417,12 @@ public class AccessibilityNodeInfo implements Parcelable {
            AccessibilityClickableSpan[] clickableSpans =
                    spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class);
            for (int i = 0; i < clickableSpans.length; i++) {
                clickableSpans[i].setAccessibilityNodeInfo(this);
                clickableSpans[i].copyConnectionDataFrom(this);
            }
            AccessibilityURLSpan[] urlSpans =
                    spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class);
            for (int i = 0; i < urlSpans.length; i++) {
                urlSpans[i].setAccessibilityNodeInfo(this);
                urlSpans[i].copyConnectionDataFrom(this);
            }
        }
        return mText;
@@ -2840,6 +2840,17 @@ public class AccessibilityNodeInfo implements Parcelable {
        mConnectionId = connectionId;
    }

    /**
     * Get the connection ID.
     *
     * @return The connection id
     *
     * @hide
     */
    public int getConnectionId() {
        return mConnectionId;
    }

    /**
     * {@inheritDoc}
     */
@@ -3354,7 +3365,7 @@ public class AccessibilityNodeInfo implements Parcelable {
        mLabeledById = UNDEFINED_NODE_ID;
        mTraversalBefore = UNDEFINED_NODE_ID;
        mTraversalAfter = UNDEFINED_NODE_ID;
        mWindowId = UNDEFINED_ITEM_ID;
        mWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
        mConnectionId = UNDEFINED_CONNECTION_ID;
        mMaxTextLength = -1;
        mMovementGranularities = 0;
@@ -3517,9 +3528,9 @@ public class AccessibilityNodeInfo implements Parcelable {
    }

    private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
        return (mWindowId != UNDEFINED_ITEM_ID
                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID
                && mConnectionId != UNDEFINED_CONNECTION_ID);
        return ((mWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
                && (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID)
                && (mConnectionId != UNDEFINED_CONNECTION_ID));
    }

    @Override