Skip to content

Commit

Permalink
Rewrite android code from isScreenReaderFocusable to iOS isAccessibil…
Browse files Browse the repository at this point in the history
…ityElement
  • Loading branch information
ASalavei committed Dec 10, 2024
1 parent 1a8c8ce commit 72dbae9
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package androidx.compose.ui.accessibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Button
import androidx.compose.material.Checkbox
import androidx.compose.material.ExperimentalMaterialApi
Expand All @@ -32,6 +33,7 @@ import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TopAppBar
import androidx.compose.material.TriStateCheckbox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
Expand All @@ -47,6 +49,7 @@ import androidx.compose.ui.test.assertAccessibilityTree
import androidx.compose.ui.test.findNode
import androidx.compose.ui.test.firstAccessibleNode
import androidx.compose.ui.test.runUIKitInstrumentedTest
import androidx.compose.ui.text.buildAnnotatedString
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
Expand Down Expand Up @@ -75,11 +78,19 @@ class ComponentsAccessibilitySemanticTest {
Button({ tapped = true }) {
Text("Content")
}
Button({ }) {}
}

assertAccessibilityTree {
isAccessibilityElement = true
traits(UIAccessibilityTraitButton)
node {
isAccessibilityElement = true
label = "Content"
traits(UIAccessibilityTraitButton)
}
node {
isAccessibilityElement = true
traits(UIAccessibilityTraitButton)
}
}

val node = firstAccessibleNode()
Expand Down Expand Up @@ -330,19 +341,31 @@ class ComponentsAccessibilitySemanticTest {
contentDescription = null,
modifier = Modifier.testTag("Image 2").semantics { role = Role.Image }
)
Image(
ImageBitmap(10, 10),
contentDescription = "Abstract Picture",
modifier = Modifier.testTag("Image 3")
)
}
}

assertAccessibilityTree {
node {
isAccessibilityElement = false
identifier = "Image 1"
traits()
}
node {
isAccessibilityElement = true
isAccessibilityElement = false
identifier = "Image 2"
traits(UIAccessibilityTraitImage)
}
node {
isAccessibilityElement = true
identifier = "Image 3"
label = "Abstract Picture"
traits(UIAccessibilityTraitImage)
}
}
}

Expand All @@ -351,7 +374,7 @@ class ComponentsAccessibilitySemanticTest {
setContentWithAccessibilityEnabled {
Column {
Text("Static Text", modifier = Modifier.testTag("Text 1"))
Text("Custom Button", modifier = Modifier.testTag("Text 2").clickable { })
Text("Custom Button", modifier = Modifier.testTag("Text 2").clickable { })
}
}

Expand Down Expand Up @@ -465,4 +488,45 @@ class ComponentsAccessibilitySemanticTest {
}
}
}
}

@Test
fun testSelectionContainer() = runUIKitInstrumentedTest {
@Composable
fun LabeledInfo(label: String, data: String) {
Text(
buildAnnotatedString {
append("$label: ")
append(data)
}
)
}

setContentWithAccessibilityEnabled {
SelectionContainer {
Column {
Text("Title")
LabeledInfo("Subtitle", "subtitle")
LabeledInfo("Details", "details")
}
}
}

assertAccessibilityTree {
node {
label = "Title"
isAccessibilityElement = true
traits(UIAccessibilityTraitStaticText)
}
node {
label = "Subtitle: subtitle"
isAccessibilityElement = true
traits(UIAccessibilityTraitStaticText)
}
node {
label = "Details: details"
isAccessibilityElement = true
traits(UIAccessibilityTraitStaticText)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.viewinterop.InteropWrappingView
import androidx.compose.ui.viewinterop.NativeAccessibilityViewSemanticsKey
import androidx.compose.ui.window.IntermediateTextInputUIView
import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlin.time.measureTime
Expand Down Expand Up @@ -1547,26 +1546,23 @@ private val SemanticsNode.isValid: Boolean
private val SemanticsNode.isRTL: Boolean
get() = layoutInfo.layoutDirection == LayoutDirection.Rtl

private val SemanticsNode.isAccessibilityElement: Boolean get() {
val config = this.config

@Suppress("DEPRECATION")
return if (isHidden) {
false
} else {
if (config.getOrNull(SemanticsProperties.IsTraversalGroup) == true
|| config.contains(SemanticsProperties.IsPopup)
|| config.contains(SemanticsProperties.IsDialog)
) {
false
} else if (config.getOrNull(SemanticsProperties.IsContainer) == true &&
config.props.size == 1
) {
false
} else {
config.containsImportantForAccessibility()
}
}
private val SemanticsNode.isAccessibilityElement: Boolean
get() = isScreenReaderFocusable()

// Simplified version of the isScreenReaderFocusable() from the
// AndroidComposeViewAccessibilityDelegateCompat.android.kt
private fun SemanticsNode.isScreenReaderFocusable(): Boolean {
val isSpeakingNode = unmergedConfig.contains(SemanticsProperties.ContentDescription) ||
unmergedConfig.contains(SemanticsProperties.EditableText) ||
unmergedConfig.contains(SemanticsProperties.Text) ||
unmergedConfig.contains(SemanticsProperties.StateDescription) ||
unmergedConfig.contains(SemanticsProperties.ToggleableState) ||
unmergedConfig.contains(SemanticsProperties.Selected) ||
unmergedConfig.contains(SemanticsProperties.ProgressBarRangeInfo)

return !isHidden &&
(unmergedConfig.isMergingSemanticsOfDescendants ||
isUnmergedLeafNode && isSpeakingNode)
}

@OptIn(ExperimentalComposeUiApi::class)
Expand Down

0 comments on commit 72dbae9

Please sign in to comment.