Skip to content

Instantly share code, notes, and snippets.

@yschimke
Created August 24, 2022 16:44
Show Gist options
  • Save yschimke/b848e35c05f371680d38b7c1949257d6 to your computer and use it in GitHub Desktop.
Save yschimke/b848e35c05f371680d38b7c1949257d6 to your computer and use it in GitHub Desktop.
ComposeA11yExtension
package com.google.android.horologist.audio.ui
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.node.RootForTest
import androidx.compose.ui.platform.ViewRootForTest
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.semantics.SemanticsNode
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import app.cash.paparazzi.RenderExtension
class ComposeA11yExtension : RenderExtension {
private var rootForTest: RootForTest? = null
init {
ViewRootForTest.onViewCreatedCallback = { viewRoot ->
viewRoot.view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(p0: View?) {
Exception("Attach").printStackTrace()
if (p0 is RootForTest) {
rootForTest = p0
}
}
override fun onViewDetachedFromWindow(p0: View?) {
if (p0 is RootForTest) {
printChildren(p0.semanticsOwner.rootSemanticsNode)
}
}
})
}
}
private fun printChildren(p0: SemanticsNode) {
val contentDescription = p0.config.getOrNull(SemanticsProperties.ContentDescription)
val stateDescription = p0.config.getOrNull(SemanticsProperties.StateDescription)
val onClickLabel = p0.config.getOrNull(SemanticsActions.OnClick)?.label
if (contentDescription != null || stateDescription != null || onClickLabel != null) {
println("Position: ${p0.layoutInfo.coordinates.positionInRoot()} ${p0.layoutInfo.coordinates.size}")
if (contentDescription != null) {
println("Content Description $contentDescription")
}
if (stateDescription != null) {
println("State Description $stateDescription")
}
if (onClickLabel != null) {
println("On Click $onClickLabel")
}
}
p0.children.forEach {
printChildren(it)
}
}
private fun buildAccessibilityView(contentView: View): View {
val linearLayout = LinearLayout(contentView.context).apply {
orientation = LinearLayout.VERTICAL
}
return linearLayout
}
override fun renderView(contentView: View): View {
Exception("renderView").printStackTrace()
rootForTest?.let {
printChildren(it.semanticsOwner.rootSemanticsNode)
}
return LinearLayout(contentView.context).apply {
orientation = LinearLayout.HORIZONTAL
weightSum = 2f
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
val contentLayoutParams = contentView.layoutParams ?: generateLayoutParams(null)
addView(
contentView,
LinearLayout.LayoutParams(
contentLayoutParams.width,
contentLayoutParams.height,
1f
)
)
addView(
buildAccessibilityView(contentView),
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
1f
)
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment