- Grid-QSO dialog gains a Match action (radio-tower icon + label) in its
header; tapping it navigates to the match page pre-filled with the tapped
grid as the opposite station and a 24h time range, auto-starts the query,
and scrolls the page straight to the time-range card.
- Every grid is now tappable (previously only worked grids opened the
dialog); unworked grids show an empty QSO list instead of a hint.
The award chip (VUCC/DXCC/WAJA/...) now lives in an Activity-scoped
MapFilterViewModel instead of a per-composition remember, so switching
to another page and back keeps the last selection; it resets to VUCC
only once per process (cold start). Removes the effect that forced
VUCC on every grid-mode entry.
The stored sync cursor was 'yyyyMMdd', which LoTW's qso_qslsince silently
ignores (measured live: it falls back to the system-supplied default).
Write a full UTC 'yyyy-MM-dd HH:mm:ss' cursor instead — the documented
format LoTW honors — so incremental pulls are exact (second precision)
and the settings page can show the last successful sync time on the LoTW
card, formatted like the ephemeris update time (prefs_updated_title).
Legacy yyyyMMdd cursors are normalized for the API, the gate and the
display (lotwCursorApi/lotwCursorDate/lotwCursorEpochMs).
Mirrors the ephemeris auto-update check: on every app start, if LoTW
credentials are configured, the new 'auto-sync LoTW grids' toggle is on,
at least one manual sync was done before, and today has not synced yet
(ARRL rate limit, ~once a day), pull confirmations since the last sync
and merge them. Same callsign -> incremental merge; callsign change ->
full replace. Manual dialog and automatic path share the orchestration
(mode resolution, gate, merge/persist) moved to core:domain
LoTWGridSync.kt; LoTWSyncMode moved to core:domain. 8 unit tests.
Root cause: LocalElevationThresholds was provided inside MainScreen, but
RadarDestination is a root-level sibling entry in NavRoot's NavDisplay, so
it fell outside the CompositionLocal scope and always used the default
15/45 thresholds.
Fix: hoist the CompositionLocalProvider to NavRoot so both the tab content
and the root-level RadarDestination read the user's low/high elevation
thresholds. Also affects upstream (same navigation structure).
Restores the original repo's calendar function (ic_calendar → AddToCalendar via
CalendarContract ACTION_INSERT) to the top-right of both pages, replacing the
map buttons:
- Radar page: back to upstream's addToCalendar (current pass name/aos/los).
- Passes page: new PassesAction.AddToCalendar wired through PassesViewModel +
container.provideAddToCalendar() (next pass name/aos/los); left button back
to the pass filter (reverts the earlier radios-button swap).
The map button is gone from both top bars — the map is now a bottom-nav tab.
Also drops the now-dead openMapRequest/navigateToMap navigation chain in
NavRoot/MainScreen that only the radar map button used.
Brings in libnativedecoderjni.so for arm64-v8a (from Iu-yang1/Look4Sat-FT4,
the Morse Expert 1.15 official Play arm64 split) and lifts the ABI pin from
armeabi-v7a-only to armeabi-v7a + arm64-v8a in both feature/cw and app. 64-bit
devices now load the native decoder natively instead of falling back to ARM32
(or crashing when the arm64 lib was missing).
- Map moves from the passes-page top-right button to the bottom navigation
bar at position 4 from the left (Satellites, Passes, AMSAT, Map, Mutual,
Settings), matching the upstream nav order.
- Passes top bar restored to the original repo layout: left = radios dialog
(ic_radios), right = passes filter/schedule dialog (ic_filter) — the map
button is gone from the top bar.
- Worked-grid QSO dialog expand arrow enlarged from bodySmall to 18sp.
Passwords for the release keystore were committed in plaintext to a
public repo. Move them to gitignored local.properties with an
environment-variable fallback. Keystore file itself stays in $HOME.
Add a full-screen update checker accessible from a button at the
bottom of the settings page. It queries the fork's GitHub latest
release and can download + install the APK in place.
- UpdateRepository (core/data): fetch releases/latest via GitHub API,
parse tag/description/APK asset, stream APK download to cacheDir
- VersionComparator (core/domain): compare 4.4.6-ba7opf.N style tags
(base version first, then build number; v-prefix tolerant) + tests
- IMainContainer/MainContainer: inject updateRepo
- UpdateCheckerScreen (feature/settings): shows current version,
checks for updates, displays latest version + release notes, and a
Download & install button; FileProvider hands the APK to the
system installer
- SettingsScreen: bottom entry button, full-screen page toggle
- AndroidManifest: REQUEST_INSTALL_PACKAGES permission + FileProvider
- i18n strings (en/zh)
Replace the radar page calendar action with the map icon and route the action through the Passes map flow so the Passes navigation item remains selected.
Start AMSAT status prefetch when the app enters foreground and share the in-flight request with the status page fetch path to avoid duplicate network requests.
When adjusting RX in passband mode (via +/- buttons or slider), the old
code used TransponderMapper.mapDownlinkToUplink (pure passband mapping
without Doppler compensation), producing ~10kHz error at LEO velocities.
Fix: use DopplerFrequencyCalculator.computeUplinkFromDownlinkWithOffset
for the full TX-from-RX round-trip, matching the RX→PASSBAND button
onClick logic. Also fixed frame mismatch: position now uses txLow/txRange
(Doppler-shifted frame) consistently.
- 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