bollwerk/.github/skills/android-build/android-dev.ps1
Jens Reinemann 6116a6c6ef docs: Android-Skills und Dev-Skript hinzufügen
Drei Copilot-Skills für den Android-Workflow:
- android-build: Gradle-Build, bekannte Issues (OneDrive, stderr)
- android-emulator: AVD-Verwaltung, Boot-Handling, S24Ultra_API35
- android-device: Physisches Gerät (Samsung S24 Ultra), USB/Wireless ADB

Zentrales PowerShell-Skript android-dev.ps1 mit 13 Aktionen:
build, clean, clean-build, emulator-start/stop, install-emulator/device,
launch, deploy-emulator/device, logcat, devices, screenshot.
Getestet: build, deploy-emulator, screenshot, emulator-stop.
2026-05-13 16:57:24 +02:00

346 lines
10 KiB
PowerShell

<#
.SYNOPSIS
Zentrales Entwicklungsskript für die Krisenvorrat Android-App.
.DESCRIPTION
Handhabt Build, Deploy und Emulator-Operationen.
Behandelt bekannte Komplikationen (OneDrive-Locks, Boot-Delays, stderr-Warnungen).
.PARAMETER Action
Die auszuführende Aktion:
build - Debug-APK bauen
clean - Build-Verzeichnisse löschen
clean-build - Clean + Build in einem Schritt
emulator-start - Emulator starten und auf Boot warten
emulator-stop - Emulator beenden
install-emulator - APK auf Emulator installieren
install-device - APK auf physisches Gerät installieren
launch - App starten (auf dem aktiven Ziel)
deploy-emulator - Build + Install + Launch auf Emulator
deploy-device - Build + Install + Launch auf physisches Gerät
logcat - App-Logcat anzeigen (Ctrl+C zum Beenden)
devices - Verbundene Geräte auflisten
screenshot - Screenshot vom aktiven Gerät speichern
.PARAMETER Target
Zielgerät: 'emulator' (Standard) oder 'device'.
.EXAMPLE
& ".github/skills/android-build/android-dev.ps1" -Action build
& ".github/skills/android-build/android-dev.ps1" -Action deploy-emulator
& ".github/skills/android-build/android-dev.ps1" -Action logcat -Target device
#>
param(
[Parameter(Mandatory)]
[ValidateSet(
'build', 'clean', 'clean-build',
'emulator-start', 'emulator-stop',
'install-emulator', 'install-device',
'launch',
'deploy-emulator', 'deploy-device',
'logcat', 'devices', 'screenshot'
)]
[string]$Action,
[ValidateSet('emulator', 'device')]
[string]$Target = 'emulator'
)
$ErrorActionPreference = 'Stop'
# --- Konfiguration ---
$SDK_ROOT = "C:\Users\JensR\AppData\Local\Android\Sdk"
$ADB = "$SDK_ROOT\platform-tools\adb.exe"
$EMULATOR = "$SDK_ROOT\emulator\emulator.exe"
$PROJECT_DIR = $PSScriptRoot | Split-Path | Split-Path | Split-Path # .github/skills/android-build → repo root
$APK_PATH = "$PROJECT_DIR\app\build\outputs\apk\debug\app-debug.apk"
$AVD_NAME = "S24Ultra_API35"
$PACKAGE = "de.krisenvorrat.app"
$ACTIVITY = "$PACKAGE/.MainActivity"
$BOOT_TIMEOUT = 300 # Sekunden (erster Boot eines neuen AVD kann >2min dauern)
$INSTALL_RETRY = 3
$env:ANDROID_HOME = $SDK_ROOT
# --- 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 Write-Warn { param([string]$msg) Write-Host "WARN $msg" -ForegroundColor Yellow }
function Get-AdbTarget {
if ($Target -eq 'device') { return '-d' }
return '-e'
}
function Test-DeviceConnected {
param([string]$type)
$flag = if ($type -eq 'device') { '-d' } else { '-e' }
try {
$result = & $ADB $flag get-state 2>&1 | Out-String
return ($result.Trim() -eq 'device')
}
catch {
return $false
}
}
function Invoke-Gradle {
param([string[]]$Tasks)
Write-Step "Gradle: $($Tasks -join ' ')"
Push-Location $PROJECT_DIR
try {
$output = & .\gradlew.bat @Tasks 2>&1 | Out-String
$success = $output -match 'BUILD SUCCESSFUL'
$failed = $output -match 'BUILD FAILED'
if ($success) {
$duration = if ($output -match 'in (\d+[ms]\s?\d*\w*)') { $Matches[0] } else { '' }
Write-Ok "BUILD SUCCESSFUL $duration"
return $true
}
elseif ($failed) {
# Fehlerdetails extrahieren
$errorBlock = ($output -split "`n" | Select-String -Pattern "What went wrong|ERROR:|FAILURE:" -Context 0, 5) -join "`n"
Write-Err "BUILD FAILED"
Write-Host $errorBlock -ForegroundColor Red
return $false
}
else {
Write-Warn "Build-Status unklar. Output prüfen:"
Write-Host ($output | Select-Object -Last 20)
return $false
}
}
finally {
Pop-Location
}
}
function Remove-BuildDirs {
Write-Step "Build-Verzeichnisse löschen"
$dirs = @("$PROJECT_DIR\app\build", "$PROJECT_DIR\build")
foreach ($d in $dirs) {
if (Test-Path $d) {
Remove-Item $d -Recurse -Force -ErrorAction SilentlyContinue
if (Test-Path $d) {
Write-Warn "Konnte $d nicht vollständig löschen (OneDrive-Lock?). Versuche erneut..."
Start-Sleep -Seconds 2
Remove-Item $d -Recurse -Force -ErrorAction SilentlyContinue
}
}
}
Write-Ok "Build-Verzeichnisse bereinigt"
}
function Start-Emulator {
# Prüfe ob Emulator bereits läuft
if (Test-DeviceConnected 'emulator') {
Write-Ok "Emulator läuft bereits"
return $true
}
Write-Step "Emulator starten: $AVD_NAME"
Start-Process -FilePath $EMULATOR -ArgumentList "-avd $AVD_NAME -gpu auto" -WindowStyle Normal
Write-Step "Warte auf ADB-Verbindung..."
& $ADB wait-for-device
Write-Step "Warte auf Boot (max ${BOOT_TIMEOUT}s)..."
$elapsed = 0
do {
Start-Sleep -Seconds 5
$elapsed += 5
$boot = (& $ADB -e shell getprop sys.boot_completed 2>$null | Out-String).Trim()
if ($elapsed % 15 -eq 0) { Write-Host " ... ${elapsed}s" -ForegroundColor DarkGray }
} while ($boot -ne "1" -and $elapsed -lt $BOOT_TIMEOUT)
if ($boot -ne "1") {
Write-Err "Emulator-Boot Timeout nach ${BOOT_TIMEOUT}s"
return $false
}
# Extra-Pause für PackageManager-Initialisierung
Write-Step "Warte 5s auf PackageManager..."
Start-Sleep -Seconds 5
Write-Ok "Emulator gebootet nach ${elapsed} Sekunden"
return $true
}
function Install-Apk {
param([string]$targetType)
if (-not (Test-Path $APK_PATH)) {
Write-Err "APK nicht gefunden: $APK_PATH"
Write-Err "Bitte zuerst bauen: -Action build"
return $false
}
$flags = Get-AdbTarget
$apkSize = [math]::Round((Get-Item $APK_PATH).Length / 1MB, 1)
Write-Step "APK installieren ${apkSize} MB auf $targetType"
for ($attempt = 1; $attempt -le $INSTALL_RETRY; $attempt++) {
$result = & $ADB $flags install -r $APK_PATH 2>&1 | Out-String
if ($result -match 'Success') {
Write-Ok "APK installiert"
return $true
}
if ($attempt -lt $INSTALL_RETRY) {
Write-Warn "Install fehlgeschlagen (Versuch $attempt/$INSTALL_RETRY). Warte 5s..."
Start-Sleep -Seconds 5
}
}
Write-Err "APK-Installation fehlgeschlagen nach $INSTALL_RETRY Versuchen:"
Write-Host $result -ForegroundColor Red
return $false
}
function Start-App {
$flags = Get-AdbTarget
Write-Step "App starten: $ACTIVITY"
$result = & $ADB $flags shell am start -n $ACTIVITY 2>&1 | Out-String
if ($result -match 'Error|Exception') {
Write-Err "App-Start fehlgeschlagen:"
Write-Host $result -ForegroundColor Red
return $false
}
Write-Ok "App gestartet"
return $true
}
# --- Aktionen ---
switch ($Action) {
'build' {
$ok = Invoke-Gradle @('assembleDebug')
if ($ok -and (Test-Path $APK_PATH)) {
$size = [math]::Round((Get-Item $APK_PATH).Length / 1MB, 2)
Write-Ok "APK: $APK_PATH - ${size} MB"
}
exit ([int](-not $ok))
}
'clean' {
Remove-BuildDirs
exit 0
}
'clean-build' {
Remove-BuildDirs
$ok = Invoke-Gradle @('assembleDebug')
if ($ok -and (Test-Path $APK_PATH)) {
$size = [math]::Round((Get-Item $APK_PATH).Length / 1MB, 2)
Write-Ok "APK: $APK_PATH - ${size} MB"
}
exit ([int](-not $ok))
}
'emulator-start' {
$ok = Start-Emulator
exit ([int](-not $ok))
}
'emulator-stop' {
Write-Step "Emulator beenden"
& $ADB emu kill 2>$null
Write-Ok "Emulator gestoppt"
exit 0
}
'install-emulator' {
$Target = 'emulator'
if (-not (Test-DeviceConnected 'emulator')) {
Write-Err "Kein Emulator verbunden. Starte mit: -Action emulator-start"
exit 1
}
$ok = Install-Apk 'emulator'
exit ([int](-not $ok))
}
'install-device' {
$Target = 'device'
if (-not (Test-DeviceConnected 'device')) {
Write-Err "Kein physisches Gerät verbunden. USB-Debugging prüfen."
exit 1
}
$ok = Install-Apk 'device'
exit ([int](-not $ok))
}
'launch' {
$ok = Start-App
exit ([int](-not $ok))
}
'deploy-emulator' {
$Target = 'emulator'
$ok = Start-Emulator
if (-not $ok) { exit 1 }
$ok = Invoke-Gradle @('assembleDebug')
if (-not $ok) { exit 1 }
$ok = Install-Apk 'emulator'
if (-not $ok) { exit 1 }
$ok = Start-App
exit ([int](-not $ok))
}
'deploy-device' {
$Target = 'device'
if (-not (Test-DeviceConnected 'device')) {
Write-Err "Kein physisches Gerät verbunden."
exit 1
}
$ok = Invoke-Gradle @('assembleDebug')
if (-not $ok) { exit 1 }
$ok = Install-Apk 'device'
if (-not $ok) { exit 1 }
$ok = Start-App
exit ([int](-not $ok))
}
'logcat' {
$flags = Get-AdbTarget
Write-Step "Logcat für $PACKAGE (Ctrl+C zum Beenden)"
$pid = & $ADB $flags shell pidof $PACKAGE 2>$null
if ($pid) {
& $ADB $flags logcat --pid=$pid -v time
}
else {
Write-Warn "App läuft nicht. Zeige alle Logs mit Tag-Filter..."
& $ADB $flags logcat -v time *:W
}
}
'devices' {
Write-Step "Verbundene Geräte"
& $ADB devices -l
}
'screenshot' {
$flags = Get-AdbTarget
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$filename = "screenshot-$timestamp.png"
Write-Step "Screenshot: $filename"
$screenshotPath = "$PROJECT_DIR\$filename"
& $ADB $flags shell screencap -p "/sdcard/$filename"
$ErrorActionPreference = 'Continue'
$pullResult = & $ADB $flags pull "/sdcard/$filename" $screenshotPath 2>&1 | Out-String
& $ADB $flags shell rm "/sdcard/$filename" 2>&1 | Out-Null
$ErrorActionPreference = 'Stop'
if (Test-Path $screenshotPath) {
Write-Ok "Gespeichert: $filename"
}
else {
Write-Err "Screenshot fehlgeschlagen"
}
}
}