Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -473,11 +473,7 @@ private void updateViewContentDescription(@NonNull T view) {
}

@ReactProp(name = ViewProps.ACCESSIBILITY_ACTIONS)
public void setAccessibilityActions(T view, ReadableArray accessibilityActions) {
if (accessibilityActions == null) {
return;
}

public void setAccessibilityActions(T view, @Nullable ReadableArray accessibilityActions) {
view.setTag(R.id.accessibility_actions, accessibilityActions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ public open class ReactAccessibilityDelegate( // The View this delegate is attac
host?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED)
}
}
private val accessibilityActionsMap = HashMap<Int, String?>()
private var accessibilityLabelledBy: View? = null

init {
Expand Down Expand Up @@ -130,7 +129,6 @@ public open class ReactAccessibilityDelegate( // The View this delegate is attac
val actionId: Int =
actionIdMap.get(actionName)
?: customActionIdMap.getOrPut(actionName) { customActionCounter++ }
accessibilityActionsMap[actionId] = actionName
val accessibilityAction = AccessibilityActionCompat(actionId, actionLabel)
info.addAction(accessibilityAction)
}
Expand Down Expand Up @@ -225,9 +223,10 @@ public open class ReactAccessibilityDelegate( // The View this delegate is attac
if (action == AccessibilityNodeInfoCompat.ACTION_EXPAND) {
host.setTag(R.id.accessibility_state_expanded, true)
}
if (accessibilityActionsMap.containsKey(action)) {
val actionName = findDeclaredActionName(host, action)
if (actionName != null) {
val eventData = createMap()
eventData.putString("actionName", accessibilityActionsMap[action])
eventData.putString("actionName", actionName)
if (
action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS.id &&
args?.containsKey(AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE) == true
Expand Down Expand Up @@ -273,6 +272,22 @@ public open class ReactAccessibilityDelegate( // The View this delegate is attac
return super.performAccessibilityAction(host, action, args)
}

private fun findDeclaredActionName(host: View, actionId: Int): String? {
val accessibilityActions =
host.getTag(R.id.accessibility_actions) as ReadableArray? ?: return null
for (i in 0..<accessibilityActions.size()) {
val action = accessibilityActions.getMap(i)
if (action == null || !action.hasKey("name")) {
continue
}
val actionName = action.getString("name")
if ((actionIdMap[actionName] ?: customActionIdMap[actionName]) == actionId) {
return actionName
}
}
return null
}

/**
* Schedule a command for sending an accessibility event. Note: A command is used to ensure that
* accessibility events are sent at most one in a given time frame to save system resources while
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package com.facebook.react.uimanager

import android.view.View.OnFocusChangeListener
import android.view.accessibility.AccessibilityNodeInfo
import com.facebook.react.R
import com.facebook.react.bridge.BridgeReactContext
import com.facebook.react.bridge.DynamicFromObject
Expand Down Expand Up @@ -162,4 +163,57 @@ class BaseViewManagerTest {
viewManager.setAccessibilityLabelledBy(view, DynamicFromObject(JavaOnlyArray()))
Assertions.assertThat(view.getTag(R.id.labelled_by)).isNull()
}

@Test
fun testDeclaredActivateActionTakesTheAccessibilityClick() {
applyAccessibilityActions(JavaOnlyArray.of(JavaOnlyMap.of("name", "activate")))
view.createAccessibilityNodeInfo()

Assertions.assertThat(performAccessibilityClick()).isEqualTo(0)
}

@Test
fun testRemovedAccessibilityActionsNoLongerTakeTheAccessibilityClick() {
applyAccessibilityActions(JavaOnlyArray.of(JavaOnlyMap.of("name", "activate")))
view.createAccessibilityNodeInfo()
applyAccessibilityActions(null)

Assertions.assertThat(performAccessibilityClick()).isEqualTo(1)
}

@Test
fun testEmptiedAccessibilityActionsNoLongerTakeTheAccessibilityClick() {
applyAccessibilityActions(JavaOnlyArray.of(JavaOnlyMap.of("name", "activate")))
view.createAccessibilityNodeInfo()
applyAccessibilityActions(JavaOnlyArray())

Assertions.assertThat(performAccessibilityClick()).isEqualTo(1)
}

@Test
fun testRemovedAccessibilityActionsLeaveTheNodeInfo() {
applyAccessibilityActions(
JavaOnlyArray.of(JavaOnlyMap.of("name", "openMenu", "label", "Open menu"))
)
applyAccessibilityActions(null)

Assertions.assertThat(
view.createAccessibilityNodeInfo().actionList.map { it.label?.toString() }
)
.doesNotContain("Open menu")
}

private fun applyAccessibilityActions(accessibilityActions: JavaOnlyArray?) {
viewManager.updateProperties(
view,
ReactStylesDiffMap(JavaOnlyMap.of(ViewProps.ACCESSIBILITY_ACTIONS, accessibilityActions)),
)
}

private fun performAccessibilityClick(): Int {
var clicks = 0
view.setOnClickListener { clicks++ }
view.performAccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, null)
return clicks
}
}
Loading