Add new APIs for content change rate limiting.
Relnote: Add new APIs for content change rate limiting
Bug: 260253197
Test: ./gradlew core:core:connectedAndroidTest --info --daemon \
-Pandroid.testInstrumentationRunnerArguments.class=androidx.core.view.accessibility.AccessibilityNodeInfoCompatTest
Change-Id: If4ea0b5d445b1dff1cbc0d228668b63b615838ae
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index cce42f2..c0aec1a 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -3206,6 +3206,7 @@
method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
method public int getLiveRegion();
method public int getMaxTextLength();
+ method public int getMinMillisBetweenContentChanges();
method public int getMovementGranularities();
method public CharSequence! getPackageName();
method public CharSequence? getPaneTitle();
@@ -3290,6 +3291,7 @@
method public void setLiveRegion(int);
method public void setLongClickable(boolean);
method public void setMaxTextLength(int);
+ method public void setMinMillisBetweenContentChanges(int);
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
method public void setPackageName(CharSequence!);
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 381de1d..d01a55e 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -3212,6 +3212,7 @@
method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
method public int getLiveRegion();
method public int getMaxTextLength();
+ method public int getMinMillisBetweenContentChanges();
method public int getMovementGranularities();
method public CharSequence! getPackageName();
method public CharSequence? getPaneTitle();
@@ -3296,6 +3297,7 @@
method public void setLiveRegion(int);
method public void setLongClickable(boolean);
method public void setMaxTextLength(int);
+ method public void setMinMillisBetweenContentChanges(int);
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
method public void setPackageName(CharSequence!);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index ba0259a..88c229d 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -3668,6 +3668,7 @@
method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
method public int getLiveRegion();
method public int getMaxTextLength();
+ method public int getMinMillisBetweenContentChanges();
method public int getMovementGranularities();
method public CharSequence! getPackageName();
method public CharSequence? getPaneTitle();
@@ -3752,6 +3753,7 @@
method public void setLiveRegion(int);
method public void setLongClickable(boolean);
method public void setMaxTextLength(int);
+ method public void setMinMillisBetweenContentChanges(int);
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
method public void setPackageName(CharSequence!);
diff --git a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
index 7a8da9b..ec7de1a 100644
--- a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
@@ -103,6 +103,14 @@
@SdkSuppress(minSdkVersion = 19)
@Test
+ public void testGetSetMinMillisBetweenContentChanges() {
+ AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
+ nodeCompat.setMinMillisBetweenContentChanges(200);
+ assertThat(nodeCompat.getMinMillisBetweenContentChanges(), equalTo(200));
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test
public void testGetSetHeading() {
AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
nodeCompat.setHeading(true);
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index bd43883..6d63efe 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -1314,6 +1314,10 @@
private static final String UNIQUE_ID_KEY =
"androidx.view.accessibility.AccessibilityNodeInfoCompat.UNIQUE_ID_KEY";
+ private static final String MIN_MILLIS_BETWEEN_CONTENT_CHANGES_KEY =
+ "androidx.view.accessibility.AccessibilityNodeInfoCompat."
+ + "MIN_MILLIS_BETWEEN_CONTENT_CHANGES_KEY";
+
// These don't line up with the internal framework constants, since they are independent
// and we might as well get all 32 bits of utility here.
private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x00000001;
@@ -2124,11 +2128,11 @@
if (Build.VERSION.SDK_INT < 19) {
return new ArrayList<Integer>();
}
- ArrayList<Integer> list = mInfo.getExtras()
+ ArrayList<Integer> list = Api19Impl.getExtras(mInfo)
.getIntegerArrayList(key);
if (list == null) {
list = new ArrayList<Integer>();
- mInfo.getExtras().putIntegerArrayList(key, list);
+ Api19Impl.getExtras(mInfo).putIntegerArrayList(key, list);
}
return list;
}
@@ -2736,6 +2740,37 @@
}
/**
+ * Gets the minimum time duration between two content change events.
+ */
+ public int getMinMillisBetweenContentChanges() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ return Api19Impl.getExtras(mInfo).getInt(MIN_MILLIS_BETWEEN_CONTENT_CHANGES_KEY);
+ }
+ return 0;
+ }
+
+ /**
+ * Sets the minimum time duration between two content change events, which is used in throttling
+ * content change events in accessibility services.
+ *
+ * <p>
+ * Example: An app can set MinMillisBetweenContentChanges as 1 min for a view which sends
+ * content change events to accessibility services one event per second.
+ * Accessibility service will throttle those content change events and only handle one event
+ * per minute for that view.
+ * </p>
+ *
+ * @see AccessibilityEventCompat#getContentChangeTypes for all content change types.
+ * @param minMillisBetweenContentChanges the minimum duration between content change events.
+ */
+ public void setMinMillisBetweenContentChanges(int minMillisBetweenContentChanges) {
+ if (Build.VERSION.SDK_INT >= 19) {
+ Api19Impl.getExtras(mInfo).putInt(MIN_MILLIS_BETWEEN_CONTENT_CHANGES_KEY,
+ minMillisBetweenContentChanges);
+ }
+ }
+
+ /**
* Returns whether the node originates from a view considered important for accessibility.
*
* @return {@code true} if the node originates from a view considered important for
@@ -2923,10 +2958,10 @@
private void clearExtrasSpans() {
if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().remove(SPANS_START_KEY);
- mInfo.getExtras().remove(SPANS_END_KEY);
- mInfo.getExtras().remove(SPANS_FLAGS_KEY);
- mInfo.getExtras().remove(SPANS_ID_KEY);
+ Api19Impl.getExtras(mInfo).remove(SPANS_START_KEY);
+ Api19Impl.getExtras(mInfo).remove(SPANS_END_KEY);
+ Api19Impl.getExtras(mInfo).remove(SPANS_FLAGS_KEY);
+ Api19Impl.getExtras(mInfo).remove(SPANS_ID_KEY);
}
}
@@ -2971,7 +3006,7 @@
if (BuildCompat.isAtLeastR()) {
return mInfo.getStateDescription();
} else if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras().getCharSequence(STATE_DESCRIPTION_KEY);
+ return Api19Impl.getExtras(mInfo).getCharSequence(STATE_DESCRIPTION_KEY);
}
return null;
}
@@ -3006,7 +3041,7 @@
if (BuildCompat.isAtLeastR()) {
mInfo.setStateDescription(stateDescription);
} else if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().putCharSequence(STATE_DESCRIPTION_KEY, stateDescription);
+ Api19Impl.getExtras(mInfo).putCharSequence(STATE_DESCRIPTION_KEY, stateDescription);
}
}
@@ -3021,7 +3056,7 @@
if (BuildCompat.isAtLeastT()) {
return mInfo.getUniqueId();
} else if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras().getString(UNIQUE_ID_KEY);
+ return Api19Impl.getExtras(mInfo).getString(UNIQUE_ID_KEY);
}
return null;
}
@@ -3042,7 +3077,7 @@
if (BuildCompat.isAtLeastT()) {
mInfo.setUniqueId(uniqueId);
} else if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().putString(UNIQUE_ID_KEY, uniqueId);
+ Api19Impl.getExtras(mInfo).putString(UNIQUE_ID_KEY, uniqueId);
}
}
@@ -3370,7 +3405,7 @@
if (Build.VERSION.SDK_INT >= 26) {
return mInfo.getHintText();
} else if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras().getCharSequence(HINT_TEXT_KEY);
+ return Api19Impl.getExtras(mInfo).getCharSequence(HINT_TEXT_KEY);
}
return null;
}
@@ -3392,7 +3427,7 @@
if (Build.VERSION.SDK_INT >= 26) {
mInfo.setHintText(hintText);
} else if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().putCharSequence(HINT_TEXT_KEY, hintText);
+ Api19Impl.getExtras(mInfo).putCharSequence(HINT_TEXT_KEY, hintText);
}
}
@@ -3600,7 +3635,7 @@
*/
public Bundle getExtras() {
if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras();
+ return Api19Impl.getExtras(mInfo);
} else {
return new Bundle();
}
@@ -4002,7 +4037,7 @@
if (Build.VERSION.SDK_INT >= 28) {
return mInfo.getTooltipText();
} else if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras().getCharSequence(TOOLTIP_TEXT_KEY);
+ return Api19Impl.getExtras(mInfo).getCharSequence(TOOLTIP_TEXT_KEY);
}
return null;
}
@@ -4024,7 +4059,7 @@
if (Build.VERSION.SDK_INT >= 28) {
mInfo.setTooltipText(tooltipText);
} else if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().putCharSequence(TOOLTIP_TEXT_KEY, tooltipText);
+ Api19Impl.getExtras(mInfo).putCharSequence(TOOLTIP_TEXT_KEY, tooltipText);
}
}
@@ -4044,7 +4079,7 @@
if (Build.VERSION.SDK_INT >= 28) {
mInfo.setPaneTitle(paneTitle);
} else if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().putCharSequence(PANE_TITLE_KEY, paneTitle);
+ Api19Impl.getExtras(mInfo).putCharSequence(PANE_TITLE_KEY, paneTitle);
}
}
@@ -4058,7 +4093,7 @@
if (Build.VERSION.SDK_INT >= 28) {
return mInfo.getPaneTitle();
} else if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras().getCharSequence(PANE_TITLE_KEY);
+ return Api19Impl.getExtras(mInfo).getCharSequence(PANE_TITLE_KEY);
}
return null;
}
@@ -4225,7 +4260,7 @@
*/
public @Nullable CharSequence getRoleDescription() {
if (Build.VERSION.SDK_INT >= 19) {
- return mInfo.getExtras().getCharSequence(ROLE_DESCRIPTION_KEY);
+ return Api19Impl.getExtras(mInfo).getCharSequence(ROLE_DESCRIPTION_KEY);
} else {
return null;
}
@@ -4257,7 +4292,7 @@
*/
public void setRoleDescription(@Nullable CharSequence roleDescription) {
if (Build.VERSION.SDK_INT >= 19) {
- mInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
+ Api19Impl.getExtras(mInfo).putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
}
}
@@ -4532,4 +4567,16 @@
info.setTextSelectable(selectable);
}
}
+
+ @RequiresApi(19)
+ private static class Api19Impl {
+ private Api19Impl() {
+ // This class is non instantiable.
+ }
+
+ @DoNotInline
+ public static Bundle getExtras(AccessibilityNodeInfo info) {
+ return info.getExtras();
+ }
+ }
}