Files
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/AccessibilityDelegateUtil.java
T
David VaccaandFacebook Github Bot 8d5ac8de76 Migration of RN-Android OSS tests to Android X
Summary:
This diff migrates RN to AndroidX.
As part of this diff I disabled few tests in RNAndroid OSS that will be re-enabled this week. As part of the refactor of BUCK files in OSS

Reviewed By: shergin

Differential Revision: D14200097

fbshipit-source-id: 932fcae251d1553e672acd67ecd0e703dcb364aa
2019-03-17 08:13:30 -07:00

175 lines
6.3 KiB
Java

// Copyright (c) Facebook, Inc. and its affiliates.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
package com.facebook.react.uimanager;
import android.content.Context;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat;
import android.text.SpannableString;
import android.text.style.URLSpan;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat;
import android.view.View;
import com.facebook.react.R;
import java.util.Locale;
import javax.annotation.Nullable;
/**
* Utility class that handles the addition of a "role" for accessibility to either a View or
* AccessibilityNodeInfo.
*/
public class AccessibilityDelegateUtil {
/**
* These roles are defined by Google's TalkBack screen reader, and this list should be kept up to
* date with their implementation. Details can be seen in their source code here:
*
* <p>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;
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);
}
}
public static AccessibilityRole fromValue(@Nullable String value) {
for (AccessibilityRole role : AccessibilityRole.values()) {
if (role.name().equalsIgnoreCase(value)) {
return role;
}
}
throw new IllegalArgumentException("Invalid accessibility role value: " + value);
}
}
private AccessibilityDelegateUtil() {
// No instances
}
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,
// 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());
}
});
}
}
/**
* Strings for setting the Role Description in english
*/
//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 (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 (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 (role.equals(AccessibilityRole.IMAGEBUTTON)) {
nodeInfo.setClickable(true);
}
}
}