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

am: 5d25f429bf

Change-Id: Ic25383f328c9dbe82241bc8a726fc5fa854b7811
This commit is contained in:
Phil Weaver
2017-04-20 16:22:43 +00:00
committed by android-build-merger
3 changed files with 43 additions and 29 deletions

View File

@@ -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 =

View File

@@ -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);
}
}

View File

@@ -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