$ErrorActionPreference = "Stop" $repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") $layoutRoot = Join-Path $repoRoot "app/src/main/res/layout" $valuesRoot = Join-Path $repoRoot "app/src/main/res/values" $themesPath = Join-Path $valuesRoot "themes.xml" $failures = New-Object System.Collections.Generic.List[string] function Add-Failure([string] $message) { $failures.Add($message) | Out-Null } function Get-XmlLine([string] $content, [int] $index) { if ($index -lt 0) { return 1 } return (($content.Substring(0, $index) -split "`n").Count) } function Get-AttributeValue([string] $block, [string] $attributeName) { $escapedName = [regex]::Escape($attributeName) $match = [regex]::Match($block, "$escapedName=""([^""]+)""") if ($match.Success) { return $match.Groups[1].Value } return $null } function Assert-MinDp([string] $value, [int] $minimum, [string] $where, [string] $attributeName) { if ([string]::IsNullOrWhiteSpace($value)) { Add-Failure "$where is missing $attributeName" return } if ($value -notmatch "^([0-9]+)dp$") { Add-Failure "$where has non-fixed $attributeName='$value'" return } $actual = [int] $Matches[1] if ($actual -lt $minimum) { Add-Failure "$where has $attributeName=${actual}dp, expected at least ${minimum}dp" } } $xmlFiles = Get-ChildItem -Path @($layoutRoot, $valuesRoot) -Filter "*.xml" -Recurse foreach ($file in $xmlFiles) { $content = Get-Content $file.FullName -Raw -Encoding UTF8 $relativePath = Resolve-Path -Relative $file.FullName $textSizeDpMatches = [regex]::Matches($content, 'android:textSize="[0-9.]+dp"') foreach ($match in $textSizeDpMatches) { $line = Get-XmlLine $content $match.Index Add-Failure "${relativePath}:$line uses dp for textSize; use sp" } $materialButtons = [regex]::Matches($content, '(?s)') foreach ($match in $materialButtons) { $line = Get-XmlLine $content $match.Index $where = "${relativePath}:$line MaterialButton" $block = $match.Value if ($block -match 'android:minWidth="0dp"') { Add-Failure "$where resets minWidth to 0dp" } $minHeight = Get-AttributeValue $block "android:minHeight" if (-not [string]::IsNullOrWhiteSpace($minHeight)) { Assert-MinDp $minHeight 48 $where "android:minHeight" } } $imageButtons = [regex]::Matches($content, '(?s)') foreach ($match in $imageButtons) { $line = Get-XmlLine $content $match.Index $where = "${relativePath}:$line ImageButton" $block = $match.Value Assert-MinDp (Get-AttributeValue $block "android:layout_width") 48 $where "android:layout_width" Assert-MinDp (Get-AttributeValue $block "android:layout_height") 48 $where "android:layout_height" } } $themesContent = Get-Content $themesPath -Raw -Encoding UTF8 foreach ($styleName in @("Primary", "Secondary", "Tonal")) { $stylePattern = "(?s)" $styleMatch = [regex]::Match($themesContent, $stylePattern) if (-not $styleMatch.Success) { Add-Failure "themes.xml is missing Widget.Aletheia.Button.$styleName" continue } $heightMatch = [regex]::Match($styleMatch.Value, '([0-9]+)dp') if (-not $heightMatch.Success) { Add-Failure "Widget.Aletheia.Button.$styleName is missing android:minHeight" continue } $height = [int] $heightMatch.Groups[1].Value if ($height -lt 48) { Add-Failure "Widget.Aletheia.Button.$styleName has android:minHeight=${height}dp, expected at least 48dp" } } if ($failures.Count -gt 0) { Write-Error ("Accessibility layout smoke test failed:`n" + ($failures -join "`n")) } Write-Host "Accessibility layout smoke test passed."