summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yinglei Wang <yingleiw@google.com> 2024-08-24 03:13:40 +0000
committer Yinglei Wang <yingleiw@google.com> 2024-10-09 17:51:39 +0000
commit212800a6ff56a216e92ee430d87480f5aec50e4c (patch)
tree348e8f72e7629177ac8f0548fa34cdece4196463
parent79d663532e23deb4798782c3ee16a7b835ed9862 (diff)
Add tri-state checked api
Design doc: https://docs.google.com/document/d/1vUzX7eSbIw_0N0kD5cYO09almUzYB7YhHNRhZcfKfXo/edit?usp=sharing Test: CTS test added Flag: android.view.accessibility.tri_state_checked Bug: 333784774 Change-Id: Ifc8eee777740397d089ad242daa711bfc224722d
-rw-r--r--core/api/current.txt10
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java14
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java122
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig9
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
5 files changed, 151 insertions, 6 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 00541afeea99..98e2dba84634 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -54919,6 +54919,7 @@ package android.view.accessibility {
method public void setPackageName(CharSequence);
method public void setSpeechStateChangeTypes(int);
method public void writeToParcel(android.os.Parcel, int);
+ field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CONTENT_CHANGE_TYPE_CHECKED = 8192; // 0x2000
field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
@@ -55064,6 +55065,7 @@ package android.view.accessibility {
method @Deprecated public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
method public void getBoundsInWindow(@NonNull android.graphics.Rect);
+ method @FlaggedApi("android.view.accessibility.tri_state_checked") public int getChecked();
method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
method @Nullable public android.view.accessibility.AccessibilityNodeInfo getChild(int, int);
method public int getChildCount();
@@ -55106,7 +55108,7 @@ package android.view.accessibility {
method public boolean isAccessibilityDataSensitive();
method public boolean isAccessibilityFocused();
method public boolean isCheckable();
- method public boolean isChecked();
+ method @Deprecated @FlaggedApi("android.view.accessibility.tri_state_checked") public boolean isChecked();
method public boolean isClickable();
method public boolean isContentInvalid();
method public boolean isContextClickable();
@@ -55151,7 +55153,8 @@ package android.view.accessibility {
method public void setBoundsInWindow(@NonNull android.graphics.Rect);
method public void setCanOpenPopup(boolean);
method public void setCheckable(boolean);
- method public void setChecked(boolean);
+ method @Deprecated @FlaggedApi("android.view.accessibility.tri_state_checked") public void setChecked(boolean);
+ method @FlaggedApi("android.view.accessibility.tri_state_checked") public void setChecked(int);
method public void setClassName(CharSequence);
method public void setClickable(boolean);
method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
@@ -55247,6 +55250,9 @@ package android.view.accessibility {
field public static final int ACTION_SELECT = 4; // 0x4
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+ field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_FALSE = 0; // 0x0
+ field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_PARTIAL = 2; // 0x2
+ field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_TRUE = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index a43906f30ff7..dfac244fcc0d 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
@@ -784,6 +785,19 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*/
public static final int CONTENT_CHANGE_TYPE_ENABLED = 1 << 12;
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The source node changed its checked state, which is returned by
+ * {@link AccessibilityNodeInfo#getChecked()}.
+ * The view changing its checked state should call
+ * {@link AccessibilityNodeInfo#setChecked(int)} and then send this event.
+ *
+ * @see AccessibilityNodeInfo#getChecked()
+ * @see AccessibilityNodeInfo#setChecked(int)
+ */
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ public static final int CONTENT_CHANGE_TYPE_CHECKED = 1 << 13;
+
// Speech state change types.
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index fe6aafbd7e16..c5ca059d1cea 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -860,6 +860,37 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final String EXTRA_DATA_REQUESTED_KEY =
"android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
+ // Tri-state checked states.
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CHECKED_STATE" }, value = {
+ CHECKED_STATE_FALSE,
+ CHECKED_STATE_TRUE,
+ CHECKED_STATE_PARTIAL
+ })
+ public @interface CheckedState {}
+
+ /**
+ * This node is not checked.
+ */
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ public static final int CHECKED_STATE_FALSE = 0;
+
+ /**
+ * This node is checked.
+ */
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ public static final int CHECKED_STATE_TRUE = 1;
+
+ /**
+ * This node is partially checked. For example,
+ * when a checkbox owns a number of sub-options and they have
+ * different states, then the main checkbox is in a partially-checked state.
+ */
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ public static final int CHECKED_STATE_PARTIAL = 2;
+
// Boolean attributes.
private static final int BOOLEAN_PROPERTY_CHECKABLE = 1 /* << 0 */;
@@ -1038,6 +1069,10 @@ public class AccessibilityNodeInfo implements Parcelable {
private IBinder mLeashedParent;
private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
+ // TODO(b/369951517) Initialize mChecked explicitly with
+ // the CHECKED_FALSE state when flagging is removed.
+ private int mChecked;
+
/**
* Creates a new {@link AccessibilityNodeInfo}.
*/
@@ -2319,28 +2354,100 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Gets whether this node is checked.
+ * Gets whether this node is checked. This is only meaningful
+ * when {@link #isCheckable()} returns {@code true}.
+ *
+ * @deprecated Use {@link #getChecked()} instead.
*
* @return True if the node is checked.
*/
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ @Deprecated
public boolean isChecked() {
return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
}
/**
- * Sets whether this node is checked.
+ * Sets whether this node is checked. This is only meaningful
+ * when {@link #isCheckable()} returns {@code true}.
* <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>
*
+ * @deprecated Use {@link #setChecked(int)} instead.
+ *
* @param checked True if the node is checked.
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ @Deprecated
public void setChecked(boolean checked) {
setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
+ if (Flags.triStateChecked()) {
+ mChecked = checked ? CHECKED_STATE_TRUE : CHECKED_STATE_FALSE;
+ }
+ }
+
+ /**
+ * Gets the checked state of this node. This is only meaningful
+ * when {@link #isCheckable()} returns {@code true}.
+ *
+ * @see #setCheckable(boolean)
+ * @see #isCheckable()
+ * @see #setChecked(int)
+ *
+ * @return The checked state, one of:
+ * <ul>
+ * <li>{@link #CHECKED_STATE_FALSE}
+ * <li>{@link #CHECKED_STATE_TRUE}
+ * <li>{@link #CHECKED_STATE_PARTIAL}
+ * </ul>
+ */
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ public @CheckedState int getChecked() {
+ return mChecked;
+ }
+
+ /**
+ * Sets the checked state of this node. This is only meaningful
+ * when {@link #isCheckable()} returns {@code true}.
+ * <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>
+ *
+ * @see #setCheckable(boolean)
+ * @see #isCheckable()
+ * @see #getChecked()
+ *
+ * @param checked The checked state. One of
+ * <ul>
+ * <li>{@link #CHECKED_STATE_FALSE}
+ * <li>{@link #CHECKED_STATE_TRUE}
+ * <li>{@link #CHECKED_STATE_PARTIAL}
+ * </ul>
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ * @throws IllegalArgumentException if checked is not one of {@link #CHECKED_STATE_FALSE},
+ * {@link #CHECKED_STATE_TRUE}, or {@link #CHECKED_STATE_PARTIAL}.
+ */
+ @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+ public void setChecked(@CheckedState int checked) {
+ enforceNotSealed();
+ switch (checked) {
+ case CHECKED_STATE_FALSE:
+ case CHECKED_STATE_TRUE:
+ case CHECKED_STATE_PARTIAL:
+ mChecked = checked;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown checked argument: " + checked);
+ }
+ setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked == CHECKED_STATE_TRUE);
}
/**
@@ -4515,6 +4622,10 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mLeashedParentNodeId != DEFAULT.mLeashedParentNodeId) {
nonDefaultFields |= bitAt(fieldIndex);
}
+ fieldIndex++;
+ if (mChecked != DEFAULT.mChecked) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
int totalFields = fieldIndex;
parcel.writeLong(nonDefaultFields);
@@ -4683,6 +4794,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeLong(mLeashedParentNodeId);
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeInt(mChecked);
+ }
if (DEBUG) {
fieldIndex--;
@@ -4771,6 +4885,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mLeashedChild = other.mLeashedChild;
mLeashedParent = other.mLeashedParent;
mLeashedParentNodeId = other.mLeashedParentNodeId;
+ mChecked = other.mChecked;
}
private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -4960,6 +5075,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mLeashedParentNodeId = parcel.readLong();
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mChecked = parcel.readInt();
+ }
mSealed = sealed;
}
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 69cbb9b2bfb8..8ffae845de1f 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -219,6 +219,13 @@ flag {
}
flag {
+ name: "tri_state_checked"
+ namespace: "accessibility"
+ description: "Feature flag for adding tri-state checked api"
+ bug: "333784774"
+}
+
+flag {
name: "warning_use_default_dialog_type"
namespace: "accessibility"
description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG"
@@ -226,4 +233,4 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-}
+ }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 6e563ff44478..da202b63d0f1 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest {
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 44;
+ private static final int NUM_MARSHALLED_PROPERTIES = 45;
/**
* The number of properties that are purposely not marshalled