From 75c4c55f8115fc9abb958e9c87b90725c350986a Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Thu, 10 Sep 2026 12:36:36 +0200 Subject: [PATCH] Add VS Code run-and-debug configurations for the Android emulator F5 now builds, installs and attaches the debugger on the emulator, with the emulator booted automatically first, so there is no manual setup step between opening the project and stepping through code. tool/start_emulator.ps1 backs the preLaunchTask and is idempotent: it returns in about three seconds when a device is already attached, so re-launching the debugger cannot stack emulator instances. It fails with a useful message when the AVD is missing rather than hanging until a timeout. Five configurations rather than one, because they answer different questions. The profile-mode entry matters most for what comes next: debug builds run the Dart VM unoptimised, so the radar animation always looks worse than it is, and milestone 3's acceptance criterion is about whether it runs smoothly. The Windows entry trades the map away for a launch that takes seconds, which is worth it for pure UI work. Verified on this machine: the script cold-boots the AVD and waits for sys.boot_completed, and a second run exits immediately. Also removes the broken pixel_7_-_api_35 AVD, which could not boot because its system image was never installed. That returned 4.8 GB, taking free disk from 3.5 GB to 10.7 GB. Co-Authored-By: Claude Opus 5 --- Nuvolari/.vscode/extensions.json | 7 +++ Nuvolari/.vscode/launch.json | 75 ++++++++++++++++++++++++++++ Nuvolari/.vscode/settings.json | 31 ++++++++++++ Nuvolari/.vscode/tasks.json | 62 +++++++++++++++++++++++ Nuvolari/tool/start_emulator.ps1 | 85 ++++++++++++++++++++++++++++++++ 5 files changed, 260 insertions(+) create mode 100644 Nuvolari/.vscode/extensions.json create mode 100644 Nuvolari/.vscode/launch.json create mode 100644 Nuvolari/.vscode/settings.json create mode 100644 Nuvolari/.vscode/tasks.json create mode 100644 Nuvolari/tool/start_emulator.ps1 diff --git a/Nuvolari/.vscode/extensions.json b/Nuvolari/.vscode/extensions.json new file mode 100644 index 0000000..0fb255c --- /dev/null +++ b/Nuvolari/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "dart-code.flutter", + "dart-code.dart-code", + "ms-vscode.powershell" + ] +} diff --git a/Nuvolari/.vscode/launch.json b/Nuvolari/.vscode/launch.json new file mode 100644 index 0000000..f222dc6 --- /dev/null +++ b/Nuvolari/.vscode/launch.json @@ -0,0 +1,75 @@ +{ + // Debug configurations for the Run and Debug panel (F5). + // + // Open the "Nuvolari" folder as the VS Code workspace root — these paths are + // relative to it, not to the repository root. + // + // Every configuration boots the Android emulator first via a preLaunchTask, + // so F5 works from a cold machine with no manual step. The task returns + // immediately if a device is already attached. + "version": "0.2.0", + "configurations": [ + { + // The everyday one: demo mode, no env.json needed, hot reload on save. + "name": "Nuvolari — debug (emulatore)", + "type": "dart", + "request": "launch", + "program": "${workspaceFolder}/app/lib/main.dart", + "cwd": "${workspaceFolder}/app", + "deviceId": "emulator-5554", + "flutterMode": "debug", + "preLaunchTask": "Avvia emulatore Android" + }, + { + // Debug builds run the Dart VM unoptimised, so animation always looks + // worse than it is. Judge the radar timeline here, never in debug. + "name": "Nuvolari — profile (misura le prestazioni)", + "type": "dart", + "request": "launch", + "program": "${workspaceFolder}/app/lib/main.dart", + "cwd": "${workspaceFolder}/app", + "deviceId": "emulator-5554", + "flutterMode": "profile", + "preLaunchTask": "Avvia emulatore Android" + }, + { + // Same as the first, but with real endpoints and keys. Requires env.json, + // which is git-ignored — copy env.example.json and fill it in. + "name": "Nuvolari — debug con env.json", + "type": "dart", + "request": "launch", + "program": "${workspaceFolder}/app/lib/main.dart", + "cwd": "${workspaceFolder}/app", + "deviceId": "emulator-5554", + "flutterMode": "debug", + "toolArgs": ["--dart-define-from-file=${workspaceFolder}/env.json"], + "preLaunchTask": "Avvia emulatore Android" + }, + { + // Runs on the workstation instead of a device: no emulator, no Android + // build, seconds to start. Good for UI work; the native map will not + // render here, so it is useless for anything map-related. + "name": "Nuvolari — debug su Windows (senza mappa)", + "type": "dart", + "request": "launch", + "program": "${workspaceFolder}/app/lib/main.dart", + "cwd": "${workspaceFolder}/app", + "deviceId": "windows", + "flutterMode": "debug" + }, + { + "name": "Test — tutti", + "type": "dart", + "request": "launch", + "program": "${workspaceFolder}/app/test", + "cwd": "${workspaceFolder}/app" + }, + { + "name": "Test — file corrente", + "type": "dart", + "request": "launch", + "program": "${file}", + "cwd": "${workspaceFolder}/app" + } + ] +} diff --git a/Nuvolari/.vscode/settings.json b/Nuvolari/.vscode/settings.json new file mode 100644 index 0000000..3b17c93 --- /dev/null +++ b/Nuvolari/.vscode/settings.json @@ -0,0 +1,31 @@ +{ + // Forward slashes on purpose: VS Code accepts them on Windows and they avoid + // the JSON backslash-escaping trap. + "dart.flutterSdkPath": "C:/src/flutter", + + // Format and fix imports on save; `dart format` is enforced by tool/verify.ps1 + // anyway, so catching it here avoids a failed verification later. + "[dart]": { + "editor.formatOnSave": true, + "editor.rulers": [80], + "editor.selectionHighlight": false, + "editor.tabCompletion": "onlySnippets", + "editor.wordBasedSuggestions": "off" + }, + + // Generated from lib/l10n/*.arb during pub get, and git-ignored. + "dart.analysisExcludedFolders": ["app/build", "app/.dart_tool"], + + "files.exclude": { + "**/.dart_tool": true, + "**/build": true, + "**/.gradle": true + }, + + // Radar frames are binary and numerous; keeping them out of search results + // makes finding code faster. + "search.exclude": { + "**/app/assets/mock/frames": true, + "**/app/lib/l10n/app_localizations*.dart": true + } +} diff --git a/Nuvolari/.vscode/tasks.json b/Nuvolari/.vscode/tasks.json new file mode 100644 index 0000000..d5ae234 --- /dev/null +++ b/Nuvolari/.vscode/tasks.json @@ -0,0 +1,62 @@ +{ + "version": "2.0.0", + "tasks": [ + { + // preLaunchTask for the emulator debug configurations. Idempotent: it + // exits at once when a device is already attached, so pressing F5 twice + // does not start two emulators. + "label": "Avvia emulatore Android", + "type": "shell", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", "Bypass", + "-File", "${workspaceFolder}/tool/start_emulator.ps1" + ], + "problemMatcher": [], + "presentation": { + "reveal": "silent", + "panel": "shared", + "clear": true + } + }, + { + "label": "Verifica (format, analyze, test, build)", + "type": "shell", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", "Bypass", + "-File", "${workspaceFolder}/tool/verify.ps1" + ], + "group": { "kind": "build", "isDefault": true }, + "problemMatcher": ["$dart-build_runner"] + }, + { + "label": "Verifica rapida (senza build)", + "type": "shell", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", "Bypass", + "-File", "${workspaceFolder}/tool/verify.ps1", + "-SkipBuild" + ], + "problemMatcher": [] + }, + { + "label": "Rigenera i frame radar dimostrativi", + "type": "shell", + "command": "python", + "args": ["${workspaceFolder}/tool/generate_mock_frames.py"], + "problemMatcher": [] + }, + { + "label": "Spegni emulatore", + "type": "shell", + "command": "adb", + "args": ["emu", "kill"], + "problemMatcher": [] + } + ] +} diff --git a/Nuvolari/tool/start_emulator.ps1 b/Nuvolari/tool/start_emulator.ps1 new file mode 100644 index 0000000..6883fa5 --- /dev/null +++ b/Nuvolari/tool/start_emulator.ps1 @@ -0,0 +1,85 @@ +<# +.SYNOPSIS + Boots the Nuvolari Android emulator, unless one is already running. + +.DESCRIPTION + Used as the preLaunchTask for the VS Code debug configurations, so pressing + F5 works from a cold machine without a separate manual step. + + Idempotent on purpose: if a device is already attached the script returns + immediately, so re-launching the debugger does not stack emulator instances. + +.PARAMETER Avd + Name of the AVD to boot. Defaults to the one this project sets up. + +.PARAMETER TimeoutSeconds + How long to wait for the boot to complete. Quickboot snapshots are disabled + on this AVD to save disk, so a cold boot takes a minute or two. + +.EXAMPLE + .\tool\start_emulator.ps1 +#> +[CmdletBinding()] +param( + [string]$Avd = 'nuvolari', + [int]$TimeoutSeconds = 300 +) + +$ErrorActionPreference = 'Stop' + +$sdk = if ($env:ANDROID_HOME) { $env:ANDROID_HOME } + elseif ($env:ANDROID_SDK_ROOT) { $env:ANDROID_SDK_ROOT } + else { 'C:\Android\Sdk' } + +$adb = Join-Path $sdk 'platform-tools\adb.exe' +$emulator = Join-Path $sdk 'emulator\emulator.exe' + +foreach ($tool in @($adb, $emulator)) { + if (-not (Test-Path $tool)) { + throw "Not found: $tool. Set ANDROID_HOME or install the Android SDK." + } +} + +function Get-AttachedDevice { + # `adb devices` prints a header line, then "\tdevice" for each ready + # device. Anything in another state (offline, unauthorized) does not count. + $lines = & $adb devices 2>$null | Select-Object -Skip 1 + foreach ($line in $lines) { + if ($line -match '^(\S+)\s+device$') { return $Matches[1] } + } + return $null +} + +$existing = Get-AttachedDevice +if ($existing) { + Write-Host "Device already attached: $existing" -ForegroundColor Green + exit 0 +} + +$available = & $emulator -list-avds 2>$null +if ($available -notcontains $Avd) { + throw "AVD '$Avd' does not exist. Available: $($available -join ', '). See docs/roadmap.md for how it was created." +} + +Write-Host "Booting emulator '$Avd' (cold boot, snapshots are disabled)..." -ForegroundColor Cyan + +# Detached, with its own window, so the debug session does not own its lifetime +# and stopping the debugger leaves the emulator up for the next run. +Start-Process -FilePath $emulator ` + -ArgumentList @('-avd', $Avd, '-no-audio', '-no-boot-anim', '-gpu', 'host') ` + -WindowStyle Minimized | Out-Null + +$deadline = (Get-Date).AddSeconds($TimeoutSeconds) +while ((Get-Date) -lt $deadline) { + $device = Get-AttachedDevice + if ($device) { + $booted = (& $adb -s $device shell getprop sys.boot_completed 2>$null) -replace '\s', '' + if ($booted -eq '1') { + Write-Host "Emulator ready: $device" -ForegroundColor Green + exit 0 + } + } + Start-Sleep -Seconds 3 +} + +throw "Emulator '$Avd' did not finish booting within $TimeoutSeconds seconds."