summaryrefslogtreecommitdiff
path: root/android/view/accessibility
diff options
context:
space:
mode:
Diffstat (limited to 'android/view/accessibility')
-rw-r--r--android/view/accessibility/AccessibilityCache.java7
-rw-r--r--android/view/accessibility/AccessibilityEvent.java41
-rw-r--r--android/view/accessibility/AccessibilityInteractionClient.java117
-rw-r--r--android/view/accessibility/AccessibilityManager.java16
-rw-r--r--android/view/accessibility/AccessibilityNodeInfo.java33
5 files changed, 157 insertions, 57 deletions
diff --git a/android/view/accessibility/AccessibilityCache.java b/android/view/accessibility/AccessibilityCache.java
index d7851171..da5a1cd6 100644
--- a/android/view/accessibility/AccessibilityCache.java
+++ b/android/view/accessibility/AccessibilityCache.java
@@ -23,8 +23,6 @@ import android.util.LongArray;
import android.util.LongSparseArray;
import android.util.SparseArray;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.ArrayList;
import java.util.List;
@@ -33,8 +31,7 @@ import java.util.List;
* It is updated when windows change or nodes change.
* @hide
*/
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class AccessibilityCache {
+public class AccessibilityCache {
private static final String LOG_TAG = "AccessibilityCache";
@@ -329,6 +326,8 @@ public final class AccessibilityCache {
final long oldParentId = oldInfo.getParentNodeId();
if (info.getParentNodeId() != oldParentId) {
clearSubTreeLocked(windowId, oldParentId);
+ } else {
+ oldInfo.recycle();
}
}
diff --git a/android/view/accessibility/AccessibilityEvent.java b/android/view/accessibility/AccessibilityEvent.java
index 5adea669..1d19a9f5 100644
--- a/android/view/accessibility/AccessibilityEvent.java
+++ b/android/view/accessibility/AccessibilityEvent.java
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -23,6 +24,8 @@ import android.util.Pools.SynchronizedPool;
import com.android.internal.util.BitUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -709,6 +712,38 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*/
public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_VIEW_CLICKED,
+ TYPE_VIEW_LONG_CLICKED,
+ TYPE_VIEW_SELECTED,
+ TYPE_VIEW_FOCUSED,
+ TYPE_VIEW_TEXT_CHANGED,
+ TYPE_WINDOW_STATE_CHANGED,
+ TYPE_NOTIFICATION_STATE_CHANGED,
+ TYPE_VIEW_HOVER_ENTER,
+ TYPE_VIEW_HOVER_EXIT,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_WINDOW_CONTENT_CHANGED,
+ TYPE_VIEW_SCROLLED,
+ TYPE_VIEW_TEXT_SELECTION_CHANGED,
+ TYPE_ANNOUNCEMENT,
+ TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
+ TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_INTERACTION_END,
+ TYPE_WINDOWS_CHANGED,
+ TYPE_VIEW_CONTEXT_CLICKED,
+ TYPE_ASSIST_READING_CONTEXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
/**
* Mask for {@link AccessibilityEvent} all types.
*
@@ -741,7 +776,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
private static final SynchronizedPool<AccessibilityEvent> sPool =
new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
- private int mEventType;
+ private @EventType int mEventType;
private CharSequence mPackageName;
private long mEventTime;
int mMovementGranularity;
@@ -833,7 +868,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*
* @return The event type.
*/
- public int getEventType() {
+ public @EventType int getEventType() {
return mEventType;
}
@@ -890,7 +925,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setEventType(int eventType) {
+ public void setEventType(@EventType int eventType) {
enforceNotSealed();
mEventType = eventType;
}
diff --git a/android/view/accessibility/AccessibilityInteractionClient.java b/android/view/accessibility/AccessibilityInteractionClient.java
index c3d6c695..d890f329 100644
--- a/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/android/view/accessibility/AccessibilityInteractionClient.java
@@ -28,6 +28,8 @@ import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -86,6 +88,12 @@ public final class AccessibilityInteractionClient
private static final LongSparseArray<AccessibilityInteractionClient> sClients =
new LongSparseArray<>();
+ private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
+ new SparseArray<>();
+
+ private static AccessibilityCache sAccessibilityCache =
+ new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
+
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
private final Object mInstanceLock = new Object();
@@ -100,12 +108,6 @@ public final class AccessibilityInteractionClient
private Message mSameThreadMessage;
- private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
- new SparseArray<>();
-
- private static final AccessibilityCache sAccessibilityCache =
- new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
-
/**
* @return The client for the current thread.
*/
@@ -133,6 +135,50 @@ public final class AccessibilityInteractionClient
}
}
+ /**
+ * Gets a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ * @return The cached connection if such.
+ */
+ public static IAccessibilityServiceConnection getConnection(int connectionId) {
+ synchronized (sConnectionCache) {
+ return sConnectionCache.get(connectionId);
+ }
+ }
+
+ /**
+ * Adds a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ * @param connection The connection.
+ */
+ public static void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
+ synchronized (sConnectionCache) {
+ sConnectionCache.put(connectionId, connection);
+ }
+ }
+
+ /**
+ * Removes a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ */
+ public static void removeConnection(int connectionId) {
+ synchronized (sConnectionCache) {
+ sConnectionCache.remove(connectionId);
+ }
+ }
+
+ /**
+ * This method is only for testing. Replacing the cache is a generally terrible idea, but
+ * tests need to be able to verify this class's interactions with the cache
+ */
+ @VisibleForTesting
+ public static void setCache(AccessibilityCache cache) {
+ sAccessibilityCache = cache;
+ }
+
private AccessibilityInteractionClient() {
/* reducing constructor visibility */
}
@@ -300,7 +346,7 @@ public final class AccessibilityInteractionClient
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, bypassCache);
if (infos != null && !infos.isEmpty()) {
for (int i = 1; i < infos.size(); i++) {
infos.get(i).recycle();
@@ -356,7 +402,7 @@ public final class AccessibilityInteractionClient
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false);
return infos;
}
}
@@ -409,7 +455,7 @@ public final class AccessibilityInteractionClient
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false);
return infos;
}
}
@@ -460,7 +506,7 @@ public final class AccessibilityInteractionClient
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false);
return info;
}
} else {
@@ -509,7 +555,7 @@ public final class AccessibilityInteractionClient
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false);
return info;
}
} else {
@@ -731,13 +777,17 @@ public final class AccessibilityInteractionClient
*
* @param info The info.
* @param connectionId The id of the connection to the system.
+ * @param bypassCache Whether or not to bypass the cache. The node is added to the cache if
+ * this value is {@code false}
*/
private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info,
- int connectionId) {
+ int connectionId, boolean bypassCache) {
if (info != null) {
info.setConnectionId(connectionId);
info.setSealed(true);
- sAccessibilityCache.add(info);
+ if (!bypassCache) {
+ sAccessibilityCache.add(info);
+ }
}
}
@@ -746,14 +796,16 @@ public final class AccessibilityInteractionClient
*
* @param infos The {@link AccessibilityNodeInfo}s.
* @param connectionId The id of the connection to the system.
+ * @param bypassCache Whether or not to bypass the cache. The nodes are added to the cache if
+ * this value is {@code false}
*/
private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
- int connectionId) {
+ int connectionId, boolean bypassCache) {
if (infos != null) {
final int infosCount = infos.size();
for (int i = 0; i < infosCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, bypassCache);
}
}
}
@@ -773,41 +825,6 @@ public final class AccessibilityInteractionClient
}
/**
- * Gets a cached accessibility service connection.
- *
- * @param connectionId The connection id.
- * @return The cached connection if such.
- */
- public IAccessibilityServiceConnection getConnection(int connectionId) {
- synchronized (sConnectionCache) {
- return sConnectionCache.get(connectionId);
- }
- }
-
- /**
- * Adds a cached accessibility service connection.
- *
- * @param connectionId The connection id.
- * @param connection The connection.
- */
- public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
- synchronized (sConnectionCache) {
- sConnectionCache.put(connectionId, connection);
- }
- }
-
- /**
- * Removes a cached accessibility service connection.
- *
- * @param connectionId The connection id.
- */
- public void removeConnection(int connectionId) {
- synchronized (sConnectionCache) {
- sConnectionCache.remove(connectionId);
- }
- }
-
- /**
* Checks whether the infos are a fully connected tree with no duplicates.
*
* @param infos The result list to check.
diff --git a/android/view/accessibility/AccessibilityManager.java b/android/view/accessibility/AccessibilityManager.java
index 35f6acba..0375635f 100644
--- a/android/view/accessibility/AccessibilityManager.java
+++ b/android/view/accessibility/AccessibilityManager.java
@@ -24,6 +24,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -187,6 +188,7 @@ public final class AccessibilityManager {
*
* @hide
*/
+ @TestApi
public interface AccessibilityServicesStateChangeListener {
/**
@@ -452,6 +454,18 @@ public final class AccessibilityManager {
}
/**
+ * Returns whether there are observers registered for this event type. If
+ * this method returns false you shuold not generate events of this type
+ * to conserve resources.
+ *
+ * @param type The event type.
+ * @return Whether the event is being observed.
+ */
+ public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+ return mIsEnabled && (mRelevantEventTypes & type) != 0;
+ }
+
+ /**
* Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
@@ -683,6 +697,7 @@ public final class AccessibilityManager {
* for a callback on the process's main handler.
* @hide
*/
+ @TestApi
public void addAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
synchronized (mLock) {
@@ -698,6 +713,7 @@ public final class AccessibilityManager {
*
* @hide
*/
+ @TestApi
public void removeAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener) {
// Final CopyOnWriteArrayList - no lock needed.
diff --git a/android/view/accessibility/AccessibilityNodeInfo.java b/android/view/accessibility/AccessibilityNodeInfo.java
index 9bdd3ffc..faea9200 100644
--- a/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/android/view/accessibility/AccessibilityNodeInfo.java
@@ -635,6 +635,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000;
+ private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000;
+
private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x0100000;
/**
@@ -2321,6 +2323,37 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
+ * that {@code false} indicates that it is not explicitly marked, not that the node is not
+ * a focusable unit. Screen readers should generally used other signals, such as
+ * {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
+ * focus.
+ *
+ * @return {@code true} if the node is specifically marked as a focusable unit for screen
+ * readers, {@code false} otherwise.
+ *
+ * @see View#isScreenReaderFocusable()
+ */
+ public boolean isScreenReaderFocusable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE);
+ }
+
+ /**
+ * Sets whether the node should be considered a focusable unit by a screen reader.
+ * <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 screenReaderFocusable {@code true} if the node is a focusable unit for screen readers,
+ * {@code false} otherwise.
+ */
+ public void setScreenReaderFocusable(boolean screenReaderFocusable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
+ }
+
+ /**
* Returns whether the node's text represents a hint for the user to enter text. It should only
* be {@code true} if the node has editable text.
*