Loading api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -48025,6 +48025,7 @@ package android.view { public class TouchDelegate { ctor public TouchDelegate(android.graphics.Rect, android.view.View); method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo(); method public boolean onTouchEvent(android.view.MotionEvent); field public static final int ABOVE = 1; // 0x1 field public static final int BELOW = 2; // 0x2 Loading Loading @@ -50007,6 +50008,7 @@ package android.view.accessibility { method public int getTextSelectionEnd(); method public int getTextSelectionStart(); method public java.lang.CharSequence getTooltipText(); method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo(); method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter(); method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore(); method public java.lang.String getViewIdResourceName(); Loading Loading @@ -50097,6 +50099,7 @@ package android.view.accessibility { method public void setTextEntryKey(boolean); method public void setTextSelection(int, int); method public void setTooltipText(java.lang.CharSequence); method public void setTouchDelegateInfo(android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo); method public void setTraversalAfter(android.view.View); method public void setTraversalAfter(android.view.View, int); method public void setTraversalBefore(android.view.View); Loading Loading @@ -50223,6 +50226,16 @@ package android.view.accessibility { field public static final int RANGE_TYPE_PERCENT = 2; // 0x2 } public static final class AccessibilityNodeInfo.TouchDelegateInfo implements android.os.Parcelable { ctor public AccessibilityNodeInfo.TouchDelegateInfo(java.util.Map<android.graphics.Region, android.view.View>); method public int describeContents(); method public android.graphics.Region getRegionAt(int); method public int getRegionCount(); method public android.view.accessibility.AccessibilityNodeInfo getTargetForRegion(android.graphics.Region); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo> CREATOR; } public abstract class AccessibilityNodeProvider { ctor public AccessibilityNodeProvider(); method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); api/test-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -1609,6 +1609,10 @@ package android.view.accessibility { method public void writeToParcelNoRecycle(android.os.Parcel, int); } public static final class AccessibilityNodeInfo.TouchDelegateInfo implements android.os.Parcelable { method public long getAccessibilityIdForRegion(android.graphics.Region); } public final class AccessibilityWindowInfo implements android.os.Parcelable { method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger); } Loading core/java/android/view/TouchDelegate.java +25 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,12 @@ package android.view; import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.graphics.Region; import android.util.ArrayMap; import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo; /** * Helper class to handle situations where you want a view to have a larger touch area than its Loading Loading @@ -77,6 +81,11 @@ public class TouchDelegate { private int mSlop; /** * Touch delegate information for accessibility */ private TouchDelegateInfo mTouchDelegateInfo; /** * Constructor * Loading Loading @@ -145,4 +154,20 @@ public class TouchDelegate { } return handled; } /** * Return a {@link TouchDelegateInfo} mapping from regions (in view coordinates) to * delegated views for accessibility usage. * * @return A TouchDelegateInfo. */ @NonNull public TouchDelegateInfo getTouchDelegateInfo() { if (mTouchDelegateInfo == null) { final ArrayMap<Region, View> targetMap = new ArrayMap<>(1); targetMap.put(new Region(mBounds), mDelegateView); mTouchDelegateInfo = new TouchDelegateInfo(targetMap); } return mTouchDelegateInfo; } } core/java/android/view/View.java +4 −0 Original line number Diff line number Diff line Loading @@ -8881,6 +8881,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, populateAccessibilityNodeInfoDrawingOrderInParent(info); info.setPaneTitle(mAccessibilityPaneTitle); info.setHeading(isAccessibilityHeading()); if (mTouchDelegate != null) { info.setTouchDelegateInfo(mTouchDelegate.getTouchDelegateInfo()); } } /** core/java/android/view/accessibility/AccessibilityNodeInfo.java +254 −21 Original line number Diff line number Diff line Loading @@ -23,10 +23,12 @@ import static java.util.Collections.EMPTY_LIST; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; Loading @@ -39,17 +41,21 @@ import android.text.style.AccessibilityClickableSpan; import android.text.style.AccessibilityURLSpan; import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongArray; import android.util.Pools.SynchronizedPool; import android.view.TouchDelegate; import android.view.View; import com.android.internal.R; import com.android.internal.util.CollectionUtils; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; Loading Loading @@ -748,6 +754,8 @@ public class AccessibilityNodeInfo implements Parcelable { private CollectionInfo mCollectionInfo; private CollectionItemInfo mCollectionItemInfo; private TouchDelegateInfo mTouchDelegateInfo; /** * Hide constructor from clients. */ Loading Loading @@ -810,7 +818,7 @@ public class AccessibilityNodeInfo implements Parcelable { public AccessibilityNodeInfo findFocus(int focus) { enforceSealed(); enforceValidFocusType(focus); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, Loading @@ -834,7 +842,7 @@ public class AccessibilityNodeInfo implements Parcelable { public AccessibilityNodeInfo focusSearch(int direction) { enforceSealed(); enforceValidFocusDirection(direction); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, Loading Loading @@ -866,7 +874,7 @@ public class AccessibilityNodeInfo implements Parcelable { @UnsupportedAppUsage public boolean refresh(Bundle arguments, boolean bypassCache) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading Loading @@ -967,7 +975,7 @@ public class AccessibilityNodeInfo implements Parcelable { if (mChildNodeIds == null) { return null; } if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } final long childId = mChildNodeIds.get(index); Loading Loading @@ -1271,7 +1279,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getTraversalBefore() { enforceSealed(); return getNodeForAccessibilityId(mTraversalBefore); return getNodeForAccessibilityId(mConnectionId, mWindowId, mTraversalBefore); } /** Loading Loading @@ -1332,7 +1340,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getTraversalAfter() { enforceSealed(); return getNodeForAccessibilityId(mTraversalAfter); return getNodeForAccessibilityId(mConnectionId, mWindowId, mTraversalAfter); } /** Loading Loading @@ -1489,7 +1497,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public boolean performAction(int action) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1512,7 +1520,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public boolean performAction(int action, Bundle arguments) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1536,7 +1544,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading Loading @@ -1567,7 +1575,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1584,7 +1592,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityWindowInfo getWindow() { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1603,7 +1611,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getParent() { enforceSealed(); return getNodeForAccessibilityId(mParentNodeId); return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId); } /** Loading Loading @@ -2783,7 +2791,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabelFor() { enforceSealed(); return getNodeForAccessibilityId(mLabelForId); return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabelForId); } /** Loading Loading @@ -2835,7 +2843,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabeledBy() { enforceSealed(); return getNodeForAccessibilityId(mLabeledById); return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabeledById); } /** Loading Loading @@ -2974,6 +2982,43 @@ public class AccessibilityNodeInfo implements Parcelable { return mExtras != null; } /** * Get the {@link TouchDelegateInfo} for touch delegate behavior with the represented view. * It is possible for the same node to be pointed to by several regions. Use * {@link TouchDelegateInfo#getRegionAt(int)} to get touch delegate target {@link Region}, and * {@link TouchDelegateInfo#getTargetForRegion(Region)} for {@link AccessibilityNodeInfo} from * the given region. * * @return {@link TouchDelegateInfo} or {@code null} if there are no touch delegates. */ @Nullable public TouchDelegateInfo getTouchDelegateInfo() { if (mTouchDelegateInfo != null) { mTouchDelegateInfo.setConnectionId(mConnectionId); mTouchDelegateInfo.setWindowId(mWindowId); } return mTouchDelegateInfo; } /** * Set touch delegate info if the represented view has a {@link TouchDelegate}. * <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 delegatedInfo {@link TouchDelegateInfo} returned from * {@link TouchDelegate#getTouchDelegateInfo()}. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setTouchDelegateInfo(@NonNull TouchDelegateInfo delegatedInfo) { enforceNotSealed(); mTouchDelegateInfo = delegatedInfo; } /** * Gets the value of a boolean property. * Loading Loading @@ -3340,6 +3385,10 @@ public class AccessibilityNodeInfo implements Parcelable { if (!Objects.equals(mCollectionItemInfo, DEFAULT.mCollectionItemInfo)) { nonDefaultFields |= bitAt(fieldIndex); } fieldIndex++; if (!Objects.equals(mTouchDelegateInfo, DEFAULT.mTouchDelegateInfo)) { nonDefaultFields |= bitAt(fieldIndex); } int totalFields = fieldIndex; parcel.writeLong(nonDefaultFields); Loading Loading @@ -3462,6 +3511,10 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0); } if (isBitSet(nonDefaultFields, fieldIndex++)) { mTouchDelegateInfo.writeToParcel(parcel, flags); } if (DEBUG) { fieldIndex--; if (totalFields != fieldIndex) { Loading Loading @@ -3543,6 +3596,10 @@ public class AccessibilityNodeInfo implements Parcelable { if (mCollectionItemInfo != null) mCollectionItemInfo.recycle(); mCollectionItemInfo = (other.mCollectionItemInfo != null) ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null; final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo; mTouchDelegateInfo = (otherInfo != null) ? new TouchDelegateInfo(otherInfo.mTargetMap, true) : null; } /** Loading Loading @@ -3665,6 +3722,10 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.readInt() == 1) : null; if (isBitSet(nonDefaultFields, fieldIndex++)) { mTouchDelegateInfo = TouchDelegateInfo.CREATOR.createFromParcel(parcel); } mSealed = sealed; } Loading Loading @@ -3813,10 +3874,11 @@ public class AccessibilityNodeInfo implements Parcelable { } } private boolean canPerformRequestOverConnection(long accessibilityNodeId) { return ((mWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) private static boolean canPerformRequestOverConnection(int connectionId, int windowId, long accessibilityNodeId) { return ((windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) && (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID) && (mConnectionId != UNDEFINED_CONNECTION_ID)); && (connectionId != UNDEFINED_CONNECTION_ID)); } @Override Loading Loading @@ -3919,13 +3981,14 @@ public class AccessibilityNodeInfo implements Parcelable { return builder.toString(); } private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) { if (!canPerformRequestOverConnection(accessibilityId)) { private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId, int windowId, long accessibilityId) { if (!canPerformRequestOverConnection(connectionId, windowId, accessibilityId)) { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS return client.findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); } Loading Loading @@ -4895,6 +4958,176 @@ public class AccessibilityNodeInfo implements Parcelable { } } /** * Class with information of touch delegated views and regions from {@link TouchDelegate} for * the {@link AccessibilityNodeInfo}. * * @see AccessibilityNodeInfo#setTouchDelegateInfo(TouchDelegateInfo) */ public static final class TouchDelegateInfo implements Parcelable { private ArrayMap<Region, Long> mTargetMap; // Two ids are initialized lazily in AccessibilityNodeInfo#getTouchDelegateInfo private int mConnectionId; private int mWindowId; /** * Create a new instance of {@link TouchDelegateInfo}. * * @param targetMap A map from regions (in view coordinates) to delegated views. * @throws IllegalArgumentException if targetMap is empty or {@code null} in * Regions or Views. */ public TouchDelegateInfo(@NonNull Map<Region, View> targetMap) { Preconditions.checkArgument(!targetMap.isEmpty() && !targetMap.containsKey(null) && !targetMap.containsValue(null)); mTargetMap = new ArrayMap<>(targetMap.size()); for (final Region region : targetMap.keySet()) { final View view = targetMap.get(region); mTargetMap.put(region, (long) view.getAccessibilityViewId()); } } /** * Create a new instance from target map. * * @param targetMap A map from regions (in view coordinates) to delegated views' * accessibility id. * @param doCopy True if shallow copy targetMap. * @throws IllegalArgumentException if targetMap is empty or {@code null} in * Regions or Views. */ TouchDelegateInfo(@NonNull ArrayMap<Region, Long> targetMap, boolean doCopy) { Preconditions.checkArgument(!targetMap.isEmpty() && !targetMap.containsKey(null) && !targetMap.containsValue(null)); if (doCopy) { mTargetMap = new ArrayMap<>(targetMap.size()); mTargetMap.putAll(targetMap); } else { mTargetMap = targetMap; } } /** * Set the connection ID. * * @param connectionId The connection id. */ private void setConnectionId(int connectionId) { mConnectionId = connectionId; } /** * Set the window ID. * * @param windowId The window id. */ private void setWindowId(int windowId) { mWindowId = windowId; } /** * Returns the number of touch delegate target region. * * @return Number of touch delegate target region. */ public int getRegionCount() { return mTargetMap.size(); } /** * Return the {@link Region} at the given index in the {@link TouchDelegateInfo}. * * @param index The desired index, must be between 0 and {@link #getRegionCount()}-1. * @return Returns the {@link Region} stored at the given index. */ @NonNull public Region getRegionAt(int index) { return mTargetMap.keyAt(index); } /** * Return the target {@link AccessibilityNodeInfo} for the given {@link Region}. * <p> * <strong>Note:</strong> This api can only be called from {@link AccessibilityService}. * </p> * <p> * <strong>Note:</strong> It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. * </p> * * @param region The region retrieved from {@link #getRegionAt(int)}. * @return The target node associates with the given region. */ @Nullable public AccessibilityNodeInfo getTargetForRegion(@NonNull Region region) { return getNodeForAccessibilityId(mConnectionId, mWindowId, mTargetMap.get(region)); } /** * Return the accessibility id of target node. * * @param region The region retrieved from {@link #getRegionAt(int)}. * @return The accessibility id of target node. * * @hide */ @TestApi public long getAccessibilityIdForRegion(@NonNull Region region) { return mTargetMap.get(region); } /** * {@inheritDoc} */ @Override public int describeContents() { return 0; } /** * {@inheritDoc} */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTargetMap.size()); for (int i = 0; i < mTargetMap.size(); i++) { final Region region = mTargetMap.keyAt(i); final Long accessibilityId = mTargetMap.valueAt(i); region.writeToParcel(dest, flags); dest.writeLong(accessibilityId); } } /** * @see android.os.Parcelable.Creator */ public static final Parcelable.Creator<TouchDelegateInfo> CREATOR = new Parcelable.Creator<TouchDelegateInfo>() { @Override public TouchDelegateInfo createFromParcel(Parcel parcel) { final int size = parcel.readInt(); if (size == 0) { return null; } final ArrayMap<Region, Long> targetMap = new ArrayMap<>(size); for (int i = 0; i < size; i++) { final Region region = Region.CREATOR.createFromParcel(parcel); final long accessibilityId = parcel.readLong(); targetMap.put(region, accessibilityId); } final TouchDelegateInfo touchDelegateInfo = new TouchDelegateInfo( targetMap, false); return touchDelegateInfo; } @Override public TouchDelegateInfo[] newArray(int size) { return new TouchDelegateInfo[size]; } }; } /** * @see android.os.Parcelable.Creator */ Loading Loading
api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -48025,6 +48025,7 @@ package android.view { public class TouchDelegate { ctor public TouchDelegate(android.graphics.Rect, android.view.View); method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo(); method public boolean onTouchEvent(android.view.MotionEvent); field public static final int ABOVE = 1; // 0x1 field public static final int BELOW = 2; // 0x2 Loading Loading @@ -50007,6 +50008,7 @@ package android.view.accessibility { method public int getTextSelectionEnd(); method public int getTextSelectionStart(); method public java.lang.CharSequence getTooltipText(); method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo(); method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter(); method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore(); method public java.lang.String getViewIdResourceName(); Loading Loading @@ -50097,6 +50099,7 @@ package android.view.accessibility { method public void setTextEntryKey(boolean); method public void setTextSelection(int, int); method public void setTooltipText(java.lang.CharSequence); method public void setTouchDelegateInfo(android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo); method public void setTraversalAfter(android.view.View); method public void setTraversalAfter(android.view.View, int); method public void setTraversalBefore(android.view.View); Loading Loading @@ -50223,6 +50226,16 @@ package android.view.accessibility { field public static final int RANGE_TYPE_PERCENT = 2; // 0x2 } public static final class AccessibilityNodeInfo.TouchDelegateInfo implements android.os.Parcelable { ctor public AccessibilityNodeInfo.TouchDelegateInfo(java.util.Map<android.graphics.Region, android.view.View>); method public int describeContents(); method public android.graphics.Region getRegionAt(int); method public int getRegionCount(); method public android.view.accessibility.AccessibilityNodeInfo getTargetForRegion(android.graphics.Region); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo> CREATOR; } public abstract class AccessibilityNodeProvider { ctor public AccessibilityNodeProvider(); method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
api/test-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -1609,6 +1609,10 @@ package android.view.accessibility { method public void writeToParcelNoRecycle(android.os.Parcel, int); } public static final class AccessibilityNodeInfo.TouchDelegateInfo implements android.os.Parcelable { method public long getAccessibilityIdForRegion(android.graphics.Region); } public final class AccessibilityWindowInfo implements android.os.Parcelable { method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger); } Loading
core/java/android/view/TouchDelegate.java +25 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,12 @@ package android.view; import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.graphics.Region; import android.util.ArrayMap; import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo; /** * Helper class to handle situations where you want a view to have a larger touch area than its Loading Loading @@ -77,6 +81,11 @@ public class TouchDelegate { private int mSlop; /** * Touch delegate information for accessibility */ private TouchDelegateInfo mTouchDelegateInfo; /** * Constructor * Loading Loading @@ -145,4 +154,20 @@ public class TouchDelegate { } return handled; } /** * Return a {@link TouchDelegateInfo} mapping from regions (in view coordinates) to * delegated views for accessibility usage. * * @return A TouchDelegateInfo. */ @NonNull public TouchDelegateInfo getTouchDelegateInfo() { if (mTouchDelegateInfo == null) { final ArrayMap<Region, View> targetMap = new ArrayMap<>(1); targetMap.put(new Region(mBounds), mDelegateView); mTouchDelegateInfo = new TouchDelegateInfo(targetMap); } return mTouchDelegateInfo; } }
core/java/android/view/View.java +4 −0 Original line number Diff line number Diff line Loading @@ -8881,6 +8881,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, populateAccessibilityNodeInfoDrawingOrderInParent(info); info.setPaneTitle(mAccessibilityPaneTitle); info.setHeading(isAccessibilityHeading()); if (mTouchDelegate != null) { info.setTouchDelegateInfo(mTouchDelegate.getTouchDelegateInfo()); } } /**
core/java/android/view/accessibility/AccessibilityNodeInfo.java +254 −21 Original line number Diff line number Diff line Loading @@ -23,10 +23,12 @@ import static java.util.Collections.EMPTY_LIST; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; Loading @@ -39,17 +41,21 @@ import android.text.style.AccessibilityClickableSpan; import android.text.style.AccessibilityURLSpan; import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongArray; import android.util.Pools.SynchronizedPool; import android.view.TouchDelegate; import android.view.View; import com.android.internal.R; import com.android.internal.util.CollectionUtils; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; Loading Loading @@ -748,6 +754,8 @@ public class AccessibilityNodeInfo implements Parcelable { private CollectionInfo mCollectionInfo; private CollectionItemInfo mCollectionItemInfo; private TouchDelegateInfo mTouchDelegateInfo; /** * Hide constructor from clients. */ Loading Loading @@ -810,7 +818,7 @@ public class AccessibilityNodeInfo implements Parcelable { public AccessibilityNodeInfo findFocus(int focus) { enforceSealed(); enforceValidFocusType(focus); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId, Loading @@ -834,7 +842,7 @@ public class AccessibilityNodeInfo implements Parcelable { public AccessibilityNodeInfo focusSearch(int direction) { enforceSealed(); enforceValidFocusDirection(direction); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, Loading Loading @@ -866,7 +874,7 @@ public class AccessibilityNodeInfo implements Parcelable { @UnsupportedAppUsage public boolean refresh(Bundle arguments, boolean bypassCache) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading Loading @@ -967,7 +975,7 @@ public class AccessibilityNodeInfo implements Parcelable { if (mChildNodeIds == null) { return null; } if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } final long childId = mChildNodeIds.get(index); Loading Loading @@ -1271,7 +1279,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getTraversalBefore() { enforceSealed(); return getNodeForAccessibilityId(mTraversalBefore); return getNodeForAccessibilityId(mConnectionId, mWindowId, mTraversalBefore); } /** Loading Loading @@ -1332,7 +1340,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getTraversalAfter() { enforceSealed(); return getNodeForAccessibilityId(mTraversalAfter); return getNodeForAccessibilityId(mConnectionId, mWindowId, mTraversalAfter); } /** Loading Loading @@ -1489,7 +1497,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public boolean performAction(int action) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1512,7 +1520,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public boolean performAction(int action, Bundle arguments) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1536,7 +1544,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading Loading @@ -1567,7 +1575,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1584,7 +1592,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityWindowInfo getWindow() { enforceSealed(); if (!canPerformRequestOverConnection(mSourceNodeId)) { if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); Loading @@ -1603,7 +1611,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getParent() { enforceSealed(); return getNodeForAccessibilityId(mParentNodeId); return getNodeForAccessibilityId(mConnectionId, mWindowId, mParentNodeId); } /** Loading Loading @@ -2783,7 +2791,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabelFor() { enforceSealed(); return getNodeForAccessibilityId(mLabelForId); return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabelForId); } /** Loading Loading @@ -2835,7 +2843,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabeledBy() { enforceSealed(); return getNodeForAccessibilityId(mLabeledById); return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabeledById); } /** Loading Loading @@ -2974,6 +2982,43 @@ public class AccessibilityNodeInfo implements Parcelable { return mExtras != null; } /** * Get the {@link TouchDelegateInfo} for touch delegate behavior with the represented view. * It is possible for the same node to be pointed to by several regions. Use * {@link TouchDelegateInfo#getRegionAt(int)} to get touch delegate target {@link Region}, and * {@link TouchDelegateInfo#getTargetForRegion(Region)} for {@link AccessibilityNodeInfo} from * the given region. * * @return {@link TouchDelegateInfo} or {@code null} if there are no touch delegates. */ @Nullable public TouchDelegateInfo getTouchDelegateInfo() { if (mTouchDelegateInfo != null) { mTouchDelegateInfo.setConnectionId(mConnectionId); mTouchDelegateInfo.setWindowId(mWindowId); } return mTouchDelegateInfo; } /** * Set touch delegate info if the represented view has a {@link TouchDelegate}. * <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 delegatedInfo {@link TouchDelegateInfo} returned from * {@link TouchDelegate#getTouchDelegateInfo()}. * * @throws IllegalStateException If called from an AccessibilityService. */ public void setTouchDelegateInfo(@NonNull TouchDelegateInfo delegatedInfo) { enforceNotSealed(); mTouchDelegateInfo = delegatedInfo; } /** * Gets the value of a boolean property. * Loading Loading @@ -3340,6 +3385,10 @@ public class AccessibilityNodeInfo implements Parcelable { if (!Objects.equals(mCollectionItemInfo, DEFAULT.mCollectionItemInfo)) { nonDefaultFields |= bitAt(fieldIndex); } fieldIndex++; if (!Objects.equals(mTouchDelegateInfo, DEFAULT.mTouchDelegateInfo)) { nonDefaultFields |= bitAt(fieldIndex); } int totalFields = fieldIndex; parcel.writeLong(nonDefaultFields); Loading Loading @@ -3462,6 +3511,10 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0); } if (isBitSet(nonDefaultFields, fieldIndex++)) { mTouchDelegateInfo.writeToParcel(parcel, flags); } if (DEBUG) { fieldIndex--; if (totalFields != fieldIndex) { Loading Loading @@ -3543,6 +3596,10 @@ public class AccessibilityNodeInfo implements Parcelable { if (mCollectionItemInfo != null) mCollectionItemInfo.recycle(); mCollectionItemInfo = (other.mCollectionItemInfo != null) ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null; final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo; mTouchDelegateInfo = (otherInfo != null) ? new TouchDelegateInfo(otherInfo.mTargetMap, true) : null; } /** Loading Loading @@ -3665,6 +3722,10 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.readInt() == 1) : null; if (isBitSet(nonDefaultFields, fieldIndex++)) { mTouchDelegateInfo = TouchDelegateInfo.CREATOR.createFromParcel(parcel); } mSealed = sealed; } Loading Loading @@ -3813,10 +3874,11 @@ public class AccessibilityNodeInfo implements Parcelable { } } private boolean canPerformRequestOverConnection(long accessibilityNodeId) { return ((mWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) private static boolean canPerformRequestOverConnection(int connectionId, int windowId, long accessibilityNodeId) { return ((windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) && (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID) && (mConnectionId != UNDEFINED_CONNECTION_ID)); && (connectionId != UNDEFINED_CONNECTION_ID)); } @Override Loading Loading @@ -3919,13 +3981,14 @@ public class AccessibilityNodeInfo implements Parcelable { return builder.toString(); } private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) { if (!canPerformRequestOverConnection(accessibilityId)) { private static AccessibilityNodeInfo getNodeForAccessibilityId(int connectionId, int windowId, long accessibilityId) { if (!canPerformRequestOverConnection(connectionId, windowId, accessibilityId)) { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS return client.findAccessibilityNodeInfoByAccessibilityId(connectionId, windowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); } Loading Loading @@ -4895,6 +4958,176 @@ public class AccessibilityNodeInfo implements Parcelable { } } /** * Class with information of touch delegated views and regions from {@link TouchDelegate} for * the {@link AccessibilityNodeInfo}. * * @see AccessibilityNodeInfo#setTouchDelegateInfo(TouchDelegateInfo) */ public static final class TouchDelegateInfo implements Parcelable { private ArrayMap<Region, Long> mTargetMap; // Two ids are initialized lazily in AccessibilityNodeInfo#getTouchDelegateInfo private int mConnectionId; private int mWindowId; /** * Create a new instance of {@link TouchDelegateInfo}. * * @param targetMap A map from regions (in view coordinates) to delegated views. * @throws IllegalArgumentException if targetMap is empty or {@code null} in * Regions or Views. */ public TouchDelegateInfo(@NonNull Map<Region, View> targetMap) { Preconditions.checkArgument(!targetMap.isEmpty() && !targetMap.containsKey(null) && !targetMap.containsValue(null)); mTargetMap = new ArrayMap<>(targetMap.size()); for (final Region region : targetMap.keySet()) { final View view = targetMap.get(region); mTargetMap.put(region, (long) view.getAccessibilityViewId()); } } /** * Create a new instance from target map. * * @param targetMap A map from regions (in view coordinates) to delegated views' * accessibility id. * @param doCopy True if shallow copy targetMap. * @throws IllegalArgumentException if targetMap is empty or {@code null} in * Regions or Views. */ TouchDelegateInfo(@NonNull ArrayMap<Region, Long> targetMap, boolean doCopy) { Preconditions.checkArgument(!targetMap.isEmpty() && !targetMap.containsKey(null) && !targetMap.containsValue(null)); if (doCopy) { mTargetMap = new ArrayMap<>(targetMap.size()); mTargetMap.putAll(targetMap); } else { mTargetMap = targetMap; } } /** * Set the connection ID. * * @param connectionId The connection id. */ private void setConnectionId(int connectionId) { mConnectionId = connectionId; } /** * Set the window ID. * * @param windowId The window id. */ private void setWindowId(int windowId) { mWindowId = windowId; } /** * Returns the number of touch delegate target region. * * @return Number of touch delegate target region. */ public int getRegionCount() { return mTargetMap.size(); } /** * Return the {@link Region} at the given index in the {@link TouchDelegateInfo}. * * @param index The desired index, must be between 0 and {@link #getRegionCount()}-1. * @return Returns the {@link Region} stored at the given index. */ @NonNull public Region getRegionAt(int index) { return mTargetMap.keyAt(index); } /** * Return the target {@link AccessibilityNodeInfo} for the given {@link Region}. * <p> * <strong>Note:</strong> This api can only be called from {@link AccessibilityService}. * </p> * <p> * <strong>Note:</strong> It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} * to avoid creating of multiple instances. * </p> * * @param region The region retrieved from {@link #getRegionAt(int)}. * @return The target node associates with the given region. */ @Nullable public AccessibilityNodeInfo getTargetForRegion(@NonNull Region region) { return getNodeForAccessibilityId(mConnectionId, mWindowId, mTargetMap.get(region)); } /** * Return the accessibility id of target node. * * @param region The region retrieved from {@link #getRegionAt(int)}. * @return The accessibility id of target node. * * @hide */ @TestApi public long getAccessibilityIdForRegion(@NonNull Region region) { return mTargetMap.get(region); } /** * {@inheritDoc} */ @Override public int describeContents() { return 0; } /** * {@inheritDoc} */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTargetMap.size()); for (int i = 0; i < mTargetMap.size(); i++) { final Region region = mTargetMap.keyAt(i); final Long accessibilityId = mTargetMap.valueAt(i); region.writeToParcel(dest, flags); dest.writeLong(accessibilityId); } } /** * @see android.os.Parcelable.Creator */ public static final Parcelable.Creator<TouchDelegateInfo> CREATOR = new Parcelable.Creator<TouchDelegateInfo>() { @Override public TouchDelegateInfo createFromParcel(Parcel parcel) { final int size = parcel.readInt(); if (size == 0) { return null; } final ArrayMap<Region, Long> targetMap = new ArrayMap<>(size); for (int i = 0; i < size; i++) { final Region region = Region.CREATOR.createFromParcel(parcel); final long accessibilityId = parcel.readLong(); targetMap.put(region, accessibilityId); } final TouchDelegateInfo touchDelegateInfo = new TouchDelegateInfo( targetMap, false); return touchDelegateInfo; } @Override public TouchDelegateInfo[] newArray(int size) { return new TouchDelegateInfo[size]; } }; } /** * @see android.os.Parcelable.Creator */ Loading