https://github.com/google/talkback/blob/master/utils/src/main/java/Role.java + *
+ * https://github.com/google/talkback/blob/master/utils/src/main/java/Role.java
*/
public enum AccessibilityRole {
- NONE,
- BUTTON,
- LINK,
- SEARCH,
- IMAGE,
- IMAGEBUTTON,
- KEYBOARDKEY,
- TEXT,
- ADJUSTABLE,
- SUMMARY,
- HEADER;
+ NONE, BUTTON, LINK, SEARCH, IMAGE, IMAGEBUTTON, KEYBOARDKEY, TEXT, ADJUSTABLE, SUMMARY, HEADER, ALERT, CHECKBOX,
+ COMBOBOX, MENU, MENUBAR, MENUITEM, PROGRESSBAR, RADIO, RADIOGROUP, SCROLLBAR, SPINBUTTON,
+ SWITCH, TAB, TABLIST, TIMER, TOOLBAR;
public static String getValue(AccessibilityRole role) {
switch (role) {
- case NONE:
- return null;
- case BUTTON:
- return "android.widget.Button";
- case LINK:
- return "android.widget.ViewGroup";
- case SEARCH:
- return "android.widget.EditText";
- case IMAGE:
- return "android.widget.ImageView";
- case IMAGEBUTTON:
- return "android.widget.ImageView";
- case KEYBOARDKEY:
- return "android.inputmethodservice.Keyboard$Key";
- case TEXT:
- return "android.widget.ViewGroup";
- case ADJUSTABLE:
- return "android.widget.SeekBar";
- case SUMMARY:
- return "android.widget.ViewGroup";
- case HEADER:
- return "android.widget.ViewGroup";
- default:
- throw new IllegalArgumentException("Invalid accessibility role value: " + role);
+ case BUTTON:
+ return "android.widget.Button";
+ case SEARCH:
+ return "android.widget.EditText";
+ case IMAGE:
+ return "android.widget.ImageView";
+ case IMAGEBUTTON:
+ return "android.widget.ImageButon";
+ case KEYBOARDKEY:
+ return "android.inputmethodservice.Keyboard$Key";
+ case TEXT:
+ return "android.widget.TextView";
+ case ADJUSTABLE:
+ return "android.widget.SeekBar";
+ case CHECKBOX:
+ return "android.widget.CheckBox";
+ case RADIO:
+ return "android.widget.RadioButton";
+ case SPINBUTTON:
+ return "android.widget.SpinButton";
+ case SWITCH:
+ return "android.widget.Switch";
+ case NONE:
+ case LINK:
+ case SUMMARY:
+ case HEADER:
+ case ALERT:
+ case COMBOBOX:
+ case MENU:
+ case MENUBAR:
+ case MENUITEM:
+ case PROGRESSBAR:
+ case RADIOGROUP:
+ case SCROLLBAR:
+ case TAB:
+ case TABLIST:
+ case TIMER:
+ case TOOLBAR:
+ return "android.view.View";
+ default:
+ throw new IllegalArgumentException("Invalid accessibility role value: " + role);
}
}
@@ -92,32 +104,55 @@ private AccessibilityDelegateUtil() {
}
public static void setDelegate(final View view) {
- final String accessibilityHint = (String) view.getTag(R.id.accessibility_hint);
final AccessibilityRole accessibilityRole = (AccessibilityRole) view.getTag(R.id.accessibility_role);
- // if a view already has an accessibility delegate, replacing it could cause problems,
+ // if a view already has an accessibility delegate, replacing it could cause
+ // problems,
// so leave it alone.
- if (!ViewCompat.hasAccessibilityDelegate(view) &&
- (accessibilityHint != null || accessibilityRole != null)) {
- ViewCompat.setAccessibilityDelegate(
- view,
- new AccessibilityDelegateCompat() {
- @Override
- public void onInitializeAccessibilityNodeInfo(
- View host, AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- if (!(accessibilityHint == null)) {
- String contentDescription=(String)info.getContentDescription();
- if (contentDescription != null) {
- contentDescription = contentDescription + ", " + accessibilityHint;
- info.setContentDescription(contentDescription);
- } else {
- info.setContentDescription(accessibilityHint);
- }
- }
-
- setRole(info, accessibilityRole, view.getContext());
+ if (!ViewCompat.hasAccessibilityDelegate(view)
+ && (accessibilityRole != null || view.getTag(R.id.accessibility_states) != null)) {
+ ViewCompat.setAccessibilityDelegate(view, new AccessibilityDelegateCompat() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ setRole(info, accessibilityRole, view.getContext());
+ // states are changable.
+ ReadableArray accessibilityStates = (ReadableArray) view.getTag(R.id.accessibility_states);
+ if (accessibilityStates != null) {
+ setState(info, accessibilityStates, view.getContext());
}
- });
+ }
+ });
+ }
+ }
+
+ public static void setState(AccessibilityNodeInfoCompat info, ReadableArray accessibilityStates, Context context) {
+ for (int i = 0; i < accessibilityStates.size(); i++) {
+ String state = accessibilityStates.getString(i);
+ switch (state) {
+ case "selected":
+ info.setSelected(true);
+ break;
+ case "disabled":
+ info.setEnabled(false);
+ break;
+ case "checked":
+ info.setCheckable(true);
+ info.setChecked(true);
+ if (info.getClassName().equals("android.widget.Switch")) {
+ info.setText(context.getString(R.string.state_on_description));
+ }
+ break;
+ case "unchecked":
+ info.setCheckable(true);
+ info.setChecked(false);
+ if (info.getClassName().equals("android.widget.Switch")) {
+ info.setText(context.getString(R.string.state_off_description));
+ }
+ break;
+ case "hasPopup":
+ info.setCanOpenPopup(true);
+ break;
+ }
}
}
@@ -125,50 +160,85 @@ public void onInitializeAccessibilityNodeInfo(
* Strings for setting the Role Description in english
*/
- //TODO: Eventually support for other languages on talkback
+ // TODO: Eventually support for other languages on talkback
public static void setRole(AccessibilityNodeInfoCompat nodeInfo, AccessibilityRole role, final Context context) {
if (role == null) {
role = AccessibilityRole.NONE;
}
nodeInfo.setClassName(AccessibilityRole.getValue(role));
- if (Locale.getDefault().getLanguage().equals(new Locale("en").getLanguage())) {
- if (role.equals(AccessibilityRole.LINK)) {
- nodeInfo.setRoleDescription(context.getString(R.string.link_description));
-
- if (nodeInfo.getContentDescription() != null) {
- SpannableString spannable = new SpannableString(nodeInfo.getContentDescription());
- spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0);
- nodeInfo.setContentDescription(spannable);
- }
+ if (role.equals(AccessibilityRole.LINK)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.link_description));
- if (nodeInfo.getText() != null) {
- SpannableString spannable = new SpannableString(nodeInfo.getText());
- spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0);
- nodeInfo.setText(spannable);
- }
- }
- if (role.equals(AccessibilityRole.SEARCH)) {
- nodeInfo.setRoleDescription(context.getString(R.string.search_description));
- }
- if (role.equals(AccessibilityRole.IMAGE)) {
- nodeInfo.setRoleDescription(context.getString(R.string.image_description));
- }
- if (role.equals(AccessibilityRole.IMAGEBUTTON)) {
- nodeInfo.setRoleDescription(context.getString(R.string.image_button_description));
+ if (nodeInfo.getContentDescription() != null) {
+ SpannableString spannable = new SpannableString(nodeInfo.getContentDescription());
+ spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0);
+ nodeInfo.setContentDescription(spannable);
}
- if (role.equals(AccessibilityRole.ADJUSTABLE)) {
- nodeInfo.setRoleDescription(context.getString(R.string.adjustable_description));
- }
- if (role.equals(AccessibilityRole.HEADER)) {
- nodeInfo.setRoleDescription(context.getString(R.string.header_description));
- final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
- AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, true);
- nodeInfo.setCollectionItemInfo(itemInfo);
+
+ if (nodeInfo.getText() != null) {
+ SpannableString spannable = new SpannableString(nodeInfo.getText());
+ spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0);
+ nodeInfo.setText(spannable);
}
}
+ if (role.equals(AccessibilityRole.SEARCH)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.search_description));
+ }
+ if (role.equals(AccessibilityRole.IMAGE)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.image_description));
+ }
if (role.equals(AccessibilityRole.IMAGEBUTTON)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.imagebutton_description));
nodeInfo.setClickable(true);
}
+ if (role.equals(AccessibilityRole.SUMMARY)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.summary_description));
+ }
+ if (role.equals(AccessibilityRole.HEADER)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.header_description));
+ final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
+ AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, true);
+ nodeInfo.setCollectionItemInfo(itemInfo);
+ }
+ if (role.equals(AccessibilityRole.ALERT)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.alert_description));
+ }
+ if (role.equals(AccessibilityRole.COMBOBOX)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.combobox_description));
+ }
+ if (role.equals(AccessibilityRole.MENU)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.menu_description));
+ }
+ if (role.equals(AccessibilityRole.MENUBAR)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.menubar_description));
+ }
+ if (role.equals(AccessibilityRole.MENUITEM)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.menuitem_description));
+ }
+ if (role.equals(AccessibilityRole.PROGRESSBAR)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.progressbar_description));
+ }
+ if (role.equals(AccessibilityRole.RADIOGROUP)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.radiogroup_description));
+ }
+ if (role.equals(AccessibilityRole.SCROLLBAR)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.scrollbar_description));
+ }
+ if (role.equals(AccessibilityRole.SPINBUTTON)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.spinbutton_description));
+ }
+ if (role.equals(AccessibilityRole.TAB)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.rn_tab_description));
+ }
+ if (role.equals(AccessibilityRole.TABLIST)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.tablist_description));
+ }
+ if (role.equals(AccessibilityRole.TIMER)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.timer_description));
+ }
+ if (role.equals(AccessibilityRole.TOOLBAR)) {
+ nodeInfo.setRoleDescription(context.getString(R.string.toolbar_description));
+ }
}
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
index d309317a3d671a..3135afa71120f5 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
@@ -9,6 +9,9 @@
import android.view.View;
import android.view.ViewParent;
import androidx.core.view.ViewCompat;
+
+import java.util.HashMap;
+
import com.facebook.react.R;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.AccessibilityDelegateUtil.AccessibilityRole;
@@ -57,6 +60,13 @@ public abstract class BaseViewManager