Addresses three review findings:
1. Coordinates now come straight from settingsRepo.stationPosition in the screen (collectAsStateWithLifecycle) — the exact same StateFlow the Settings page shows. Previously a separate ViewModel re-derived them, and it could lag behind the Settings page (user: '设置页更新了站位但漫游页死活不更新'). With the shared source the two pages can never disagree. RoamingViewModel removed; state derivation moved to RoamingState.fromPosition().
2. Red marker placement ported faithfully from the QTH定位器 app: it is driven by the 3rd character pair of the 8-char locator (the 'ih' in OL42ih45), mapped to a 0..1 fraction (lon a=west..x=east, lat inverted a=south..x=north), then scaled to the actual center-cell size. The grid now uses the reference proportions (columns 21.4/56.2/21.4, rows 31.5/35.9/31.7) and fills the screen, so the marker lands accurately on any device.
3. Workflow now uploads a versioned APK (look4sat-<version>.apk instead of look4sat.apk).
Also: Settings 'Other' card rows got vertical spacing (Arrangement.spacedBy) so the new roaming toggle is not glued to the night-mode row.
Ports the QTH定位器 (com.us1pm.gridsquarelocator) location panel into Look4Sat as a new 'Roaming' page, restyled with the app's own look.
UI (top to bottom):
- Info header: GPS status dot, big 8-char locator, Lat/Lon rows with DMS + 5-decimal display, and a GPS 定位 button
- 3x3 grid panel: the current 4-char Maidenhead square (e.g. OL42) centered, surrounded by its 8 neighbors (OL33..OL51), with a red position marker in the center cell
Logic:
- QthConverter gains qthNeighbors(square) building the 3x3 grid with field/square carry at boundaries (AA00 wraps to RR99, IO91 crosses into J field), and qthToSquare(locator) extracting the 4-char square
- Verified against the decompiled app algorithm and the reference screenshot (OL42 grid matches exactly); 9 unit tests cover normal, boundary and field-wrap cases
Navigation:
- New bottom-nav item 'Roaming' between Match and Settings, with a crosshair icon
- New feature:roaming module (ViewModel + Compose screen) registered in the app
Build config:
- Lowered Gradle JVM heap from -Xmx6g to 768m: the 2GB build server froze on the old value; heavy release builds stay on GitHub Actions
Conditionally applies the BG7NTA signingConfig when keystore.properties exists (gitignored). CI signs via apksigner with GitHub Secrets, so this only affects local builds.
The official app signs com.rtbishop.look4sat with its own certificate. A fork sharing that applicationId cannot be installed over the official build (signature mismatch) and users saw overwrite/install failures.
- Add applicationId version catalog entry; namespace stays com.rtbishop.look4sat so source imports are untouched, applicationId becomes com.rtbishop.look4sat.bg7nta.
- Update PROPERTY_SATELLITE_DATA_OPTIMIZED meta-data to the fork id.
- Document the fork-applicationId requirement in the version catalog.
- MutualViewModel is Activity-scoped, so returning from Radar keeps query results
- TrackSampleData gains time field for live cut-off
- Removed standalone mutual card from radar page
- RadarViewCompose draws optional dashed station-B track + current dot
- RadarScreen builds trackB from mutual data up to current time
- Fix cubic Bezier control points (proper Catmull-Rom to Bezier conversion)
- Refine AOS/LOS more robustly by walking from edge into the pass window
- Still 5s sampling for smooth curves
- Pre-fill station A grid from settingsRepo.stationPosition
- Find ALL mutual passes per satellite (not just first)
- Fix Color.hashCode() -> Color.toArgb() for native canvas paint
- Use 2min gap between pass searches to avoid duplicates
Port satlover.de dual-station pass matching feature:
- New feature/mutual module with mutual pass data model
- MutualViewModel: compute overlapping passes for two stations
- ElevationCurveChart: Canvas-based dual elevation curve with drag
- MutualScreen: input form + results with expandable cards
- Navigation: add Mutual tab to bottom navigation bar
- i18n: add Chinese/English strings for new feature