Skip to content
Merged
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
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
appStartup = "1.2.0"
kotlin = "2.3.21"
composeBom = "2024.12.01"
hilt = "2.54"
Expand All @@ -23,6 +24,7 @@ androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "j
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-startup-runtime = { group = "androidx.startup", name = "startup-runtime", version.ref = "appStartup" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
Expand Down
10 changes: 10 additions & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ComposeA11yScanner">
<meta-data
android:name="a11y_scanner_min_touch_target"
android:value="48" />
<meta-data
android:name="a11y_scanner_min_contrast"
android:value="4.5" />
<meta-data
android:name="a11y_scanner_auto_scan"
android:value="false" />

<activity
android:name=".SampleActivity"
android:exported="true"
Expand Down
217 changes: 212 additions & 5 deletions sample/src/main/java/com/composea11yscanner/sample/SampleActivity.kt
Original file line number Diff line number Diff line change
@@ -1,36 +1,243 @@
package com.composea11yscanner.sample

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.composea11yscanner.ComposeA11yScanner
import com.composea11yscanner.triggers.scanOnLongPress
import com.composea11yscanner.triggers.scanOnShake
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class SampleActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent { SampleApp() }
setContent {
MaterialTheme {
ScanTriggerDemo()
}
}
}
}

@SuppressLint("UnusedBoxWithConstraintsScope")
@Composable
fun SampleApp(modifier: Modifier = Modifier) {
fun ScanTriggerDemo(modifier: Modifier = Modifier) {
var shakeEnabled by remember { mutableStateOf(true) }
scanOnShake(enabled = shakeEnabled)

Scaffold(modifier = modifier.fillMaxSize()) { innerPadding ->
Text(text = "ComposeA11yScanner Sample", modifier = Modifier.padding(innerPadding))
Column(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text(
text = "Scan Trigger Demo",
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.SemiBold,
)

BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
val useWideLayout = maxWidth >= 720.dp
if (useWideLayout) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
TriggerOptionCard(
title = "Manual",
body = "Button tap",
modifier = Modifier.weight(1f),
) {
Button(
onClick = { ComposeA11yScanner.triggerScan() },
contentPadding = PaddingValues(horizontal = 14.dp, vertical = 10.dp),
) {
Text("Run Scan")
}
}
TriggerOptionCard(
title = "Long Press",
body = "Hold this panel",
modifier = Modifier
.weight(1f)
.scanOnLongPress(),
) {
TriggerTarget("Hold")
}
ShakeTriggerCard(
enabled = shakeEnabled,
onEnabledChange = { shakeEnabled = it },
modifier = Modifier.weight(1f),
)
}
} else {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
TriggerOptionCard(title = "Manual", body = "Button tap") {
Button(onClick = { ComposeA11yScanner.triggerScan() }) {
Text("Run Scan")
}
}
TriggerOptionCard(
title = "Long Press",
body = "Hold this panel",
modifier = Modifier.scanOnLongPress(),
) {
TriggerTarget("Hold")
}
ShakeTriggerCard(
enabled = shakeEnabled,
onEnabledChange = { shakeEnabled = it },
)
}
}
}

DemoScanContent()
}
}
}

@Composable
private fun TriggerOptionCard(
title: String,
body: String,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
Card(
modifier = modifier.height(164.dp),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.SpaceBetween,
) {
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
Text(body, style = MaterialTheme.typography.bodyMedium)
}
content()
}
}
}

@Composable
private fun ShakeTriggerCard(
enabled: Boolean,
onEnabledChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
TriggerOptionCard(
title = "Shake",
body = if (enabled) "Sensor armed" else "Sensor off",
modifier = modifier,
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text("Enabled", style = MaterialTheme.typography.labelLarge)
Switch(checked = enabled, onCheckedChange = onEnabledChange)
}
}
}

@Composable
private fun TriggerTarget(label: String) {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(8.dp))
.padding(horizontal = 16.dp, vertical = 10.dp),
contentAlignment = Alignment.Center,
) {
Text(label, color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.SemiBold)
}
}

@Composable
private fun DemoScanContent() {
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text("Inspectable Content", style = MaterialTheme.typography.titleMedium)
Text("These controls intentionally include a few accessibility issues for the scanner.")
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.size(28.dp)
.background(Color(0xFF1976D2), RoundedCornerShape(4.dp))
.clickable { },
)
Spacer(modifier = Modifier.width(12.dp))
Text("Small clickable swatch")
}
Text(
text = "Low contrast text sample",
color = Color(0xFFBDBDBD),
modifier = Modifier.background(Color.White),
)
}
}
}

@Preview(showBackground = true)
@Composable
fun SampleAppPreview() {
SampleApp()
fun ScanTriggerDemoPreview() {
MaterialTheme {
ScanTriggerDemo()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ data class ScannerConfig(
val minTouchTargetDp: Int = 48,
val minContrastRatio: Float = 4.5f,
val debugOverlay: Boolean = true,
val autoScan: Boolean = true,
)
2 changes: 2 additions & 0 deletions scanner-ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ dependencies {
implementation(project(":scanner-rules"))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.startup.runtime)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
Expand Down
16 changes: 15 additions & 1 deletion scanner-ui/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.composea11yscanner.A11yScannerInitializer"
android:value="androidx.startup" />
</provider>
</application>
</manifest>
Loading
Loading