-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathFocusOrderRule.kt
More file actions
46 lines (39 loc) · 1.95 KB
/
Copy pathFocusOrderRule.kt
File metadata and controls
46 lines (39 loc) · 1.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.composea11yscanner.rules
import com.composea11yscanner.core.model.A11yIssue
import com.composea11yscanner.core.model.A11yNode
import com.composea11yscanner.core.model.A11ySeverity
import com.composea11yscanner.core.rule.BaseScanRule
import kotlin.math.roundToInt
/**
* @param screenDensity Display density from DisplayMetrics.density.
* Used to convert [jumpThresholdDp] to pixels for comparison against [A11yNode.bounds].
* @param jumpThresholdDp Upward movement that triggers a violation (default 8dp).
*/
class FocusOrderRule(
private val screenDensity: Float,
private val jumpThresholdDp: Float = 8f,
) : BaseScanRule() {
override val ruleId = "focus-order"
override val ruleName = "Focus Order"
override val severity = A11ySeverity.Error
override val wcagReference = "WCAG 2.4.3 Focus Order (Level A)"
private val jumpThresholdPx: Int = (jumpThresholdDp * screenDensity).roundToInt()
override fun evaluateAll(nodes: List<A11yNode>): List<A11yIssue> =
nodes
.filter { it.isFocusable }
.zipWithNext { prev, curr ->
val jumpedUpward = curr.bounds.top < prev.bounds.top - jumpThresholdPx
if (!jumpedUpward) return@zipWithNext null
val prevTopDp = (prev.bounds.top / screenDensity).roundToInt()
val currTopDp = (curr.bounds.top / screenDensity).roundToInt()
issue(
node = curr,
message = "Focus jumps upward from ${prevTopDp}dp to ${currTopDp}dp. " +
"Screen readers will announce this element out of visual reading order.",
howToFix = "Reorder composables in the source so focus flows top-to-bottom, " +
"left-to-right, or apply Modifier.semantics { traversalIndex = n } " +
"to explicitly control the focus traversal sequence.",
)
}
.filterNotNull()
}