<# .SYNOPSIS Robuster Screenshot vom Android-Emulator/Gerät. .DESCRIPTION Umgeht PowerShells UTF-16-Encoding-Problem bei Binärausgaben. Nutzt adb pull statt exec-out-Pipe, validiert den PNG-Header, und liefert als Fallback eine Text-Beschreibung der UI via uiautomator. .PARAMETER OutputPath Zielpfad für den Screenshot. Standard: tmp/screenshot-.png .PARAMETER Target 'emulator' (Standard) oder 'device'. .PARAMETER UiDump Zusätzlich UI-Hierarchie als Text ausgeben (nützlich für automatische Verifikation). .EXAMPLE & ".github/skills/android-build/screenshot.ps1" & ".github/skills/android-build/screenshot.ps1" -UiDump & ".github/skills/android-build/screenshot.ps1" -OutputPath "my-screenshot.png" #> param( [string]$OutputPath, [ValidateSet('emulator', 'device')] [string]$Target = 'emulator', [switch]$UiDump ) $ErrorActionPreference = 'Stop' # --- Konfiguration --- $SDK_ROOT = "C:\Users\JensR\AppData\Local\Android\Sdk" $ADB = "$SDK_ROOT\platform-tools\adb.exe" $PROJECT_DIR = $PSScriptRoot | Split-Path | Split-Path | Split-Path $ADB_TIMEOUT = 15 # Sekunden pro ADB-Kommando $DEVICE_TMP = "/sdcard/screenshot-tmp.png" # --- Hilfsfunktionen --- function Write-Step { param([string]$msg) Write-Host ">> $msg" -ForegroundColor Cyan } function Write-Ok { param([string]$msg) Write-Host "OK $msg" -ForegroundColor Green } function Write-Err { param([string]$msg) Write-Host "ERR $msg" -ForegroundColor Red } function Get-AdbFlag { if ($Target -eq 'device') { return '-d' } return '-e' } function Invoke-AdbWithTimeout { param( [string[]]$Arguments, [int]$TimeoutSec = $ADB_TIMEOUT ) $psi = [System.Diagnostics.ProcessStartInfo]::new($ADB) $psi.Arguments = $Arguments -join ' ' $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.UseShellExecute = $false $psi.CreateNoWindow = $true $proc = [System.Diagnostics.Process]::Start($psi) $stdout = $proc.StandardOutput.ReadToEnd() $stderr = $proc.StandardError.ReadToEnd() $exited = $proc.WaitForExit($TimeoutSec * 1000) if (-not $exited) { $proc.Kill() throw "ADB-Timeout nach ${TimeoutSec}s: $($Arguments -join ' ')" } return @{ ExitCode = $proc.ExitCode Stdout = $stdout Stderr = $stderr } } # --- Verbindung prüfen --- $flag = Get-AdbFlag Write-Step "Prüfe $Target-Verbindung..." try { $check = Invoke-AdbWithTimeout @($flag, 'get-state') -TimeoutSec 5 if ($check.Stdout.Trim() -ne 'device') { Write-Err "$Target nicht verbunden (Status: $($check.Stdout.Trim()))" exit 1 } } catch { Write-Err "$Target nicht erreichbar: $_" exit 1 } Write-Ok "$Target verbunden" # --- Zielpfad bestimmen --- $tmpDir = Join-Path $PROJECT_DIR "tmp" if (-not (Test-Path $tmpDir)) { New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null } if (-not $OutputPath) { $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $OutputPath = Join-Path $tmpDir "screenshot-$timestamp.png" } elseif (-not [System.IO.Path]::IsPathRooted($OutputPath)) { $OutputPath = Join-Path $PROJECT_DIR $OutputPath } # --- Screenshot aufnehmen --- Write-Step "Screenshot aufnehmen..." # Schritt 1: Screenshot auf dem Gerät erstellen try { $cap = Invoke-AdbWithTimeout @($flag, 'shell', 'screencap', '-p', $DEVICE_TMP) -TimeoutSec $ADB_TIMEOUT if ($cap.ExitCode -ne 0) { Write-Err "screencap fehlgeschlagen: $($cap.Stderr)" exit 1 } } catch { Write-Err "screencap abgebrochen: $_" exit 1 } # Schritt 2: Datei vom Gerät holen (adb pull = binärsicher) try { $pull = Invoke-AdbWithTimeout @($flag, 'pull', $DEVICE_TMP, $OutputPath) -TimeoutSec $ADB_TIMEOUT if ($pull.ExitCode -ne 0) { Write-Err "adb pull fehlgeschlagen: $($pull.Stderr)" exit 1 } } catch { Write-Err "adb pull abgebrochen: $_" exit 1 } # Schritt 3: Temp-Datei auf dem Gerät löschen try { Invoke-AdbWithTimeout @($flag, 'shell', 'rm', '-f', $DEVICE_TMP) -TimeoutSec 5 | Out-Null } catch { # Nicht kritisch } # --- PNG validieren --- if (-not (Test-Path $OutputPath)) { Write-Err "Screenshot-Datei nicht erstellt: $OutputPath" exit 1 } $fileInfo = Get-Item $OutputPath if ($fileInfo.Length -eq 0) { Remove-Item $OutputPath -Force Write-Err "Screenshot-Datei ist leer (0 Bytes)" exit 1 } $bytes = [System.IO.File]::ReadAllBytes($OutputPath) $pngMagic = @(0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A) $headerValid = $true for ($i = 0; $i -lt 8; $i++) { if ($bytes[$i] -ne $pngMagic[$i]) { $headerValid = $false; break } } if (-not $headerValid) { $hexHeader = ($bytes[0..7] | ForEach-Object { "{0:X2}" -f $_ }) -join " " Write-Err "Ungültiger PNG-Header: $hexHeader (erwartet: 89 50 4E 47 0D 0A 1A 0A)" Write-Err "Mögliche Ursache: PowerShell UTF-16-Encoding oder Geräte-Fehler" Remove-Item $OutputPath -Force exit 1 } $sizeKB = [math]::Round($fileInfo.Length / 1KB) $relativePath = $OutputPath.Replace("$PROJECT_DIR\", "") Write-Ok "Screenshot gespeichert: $relativePath (${sizeKB} KB, PNG validiert)" # --- UI-Dump (optional) --- if ($UiDump) { Write-Step "UI-Hierarchie auslesen..." try { Invoke-AdbWithTimeout @($flag, 'shell', 'uiautomator', 'dump', '/sdcard/ui-dump.xml') -TimeoutSec 10 | Out-Null $xmlResult = Invoke-AdbWithTimeout @($flag, 'shell', 'cat', '/sdcard/ui-dump.xml') -TimeoutSec 5 Invoke-AdbWithTimeout @($flag, 'shell', 'rm', '-f', '/sdcard/ui-dump.xml') -TimeoutSec 5 | Out-Null # Sichtbare Texte extrahieren $texts = [regex]::Matches($xmlResult.Stdout, 'text="([^"]+)"') | ForEach-Object { $_.Groups[1].Value } | Where-Object { $_ -ne "" } if ($texts.Count -gt 0) { Write-Step "Sichtbare Texte auf dem Bildschirm:" $texts | ForEach-Object { Write-Host " · $_" -ForegroundColor White } } else { Write-Host " (keine Texte gefunden)" -ForegroundColor DarkGray } } catch { Write-Err "UI-Dump fehlgeschlagen: $_" } } exit 0