Commit Graph
18 Commits
Author SHA1 Message Date
mckero 018a3afd2b fix(amsat): mark satellites whose reports were crowded out of the global pull
The API caps at 500 records regardless of the hours requested. With 88 catalog
satellites, eight of them more active than 50 reports per 72 hours, quieter
satellites get crowded out. Measured live: the global pull returned 500 reports
covering 36 satellites, while the summary endpoint reported 743 reports across 38
satellites. 26 of 38 satellites had incomplete data, and two (PO-101_[FM] and
TEVEL2-6_[FM]) had zero reports in the global pull despite having reports in the
summary.

The summary endpoint (api/v1/summary.php) returns per-satellite report counts in
one request, so the fix adds one extra call rather than the 88-request
alternative of per-satellite pulls. A satellite whose global pull is incomplete
gets a subdued "68 / 116" marker next to its name, telling the operator the page
knows there is more data it could not fetch. The marker is silent when the
summary is unavailable or the counts match, so the feature degrades gracefully.

The earlier no-data grey (0xFFE8E8E8) already prevented the worst case: slots
crowded out of the global pull were marked as "we never looked" rather than
claiming "nobody reported". The marker now closes the remaining gap: the page
can honestly say "we know there are 116 reports for this satellite but we could
only show you 68 of them".

Also fixed a subagent mutation-testing residue: the coverage floor had been
moved from global (reports.minOfOrNull) to per-satellite (satReports.minOfOrNull)
and left in the tree. One test caught it (coverage is judged from all reports,
not one satellite's), proving the test has teeth.

Adds getAmSatSummary to IRemoteSource and RemoteSource, parseSummary to
AmSatRepository, and summaryCount to SatStatus. All eight test-file
implementations of IRemoteSource were updated for the new method.
2026-08-22 11:47:42 +00:00
mckero 3612e662e7 fix(amsat): distinguish slots we have no data for from slots nobody reported
Grey meant two different things. The API caps at 500 records however many hours
are requested: measured against the live endpoint, a 72-hour request returned 500
reports spanning only 49 hours, so the oldest 9.5 hours of the third day had no
data at all. Those cells were painted the same grey as "nobody reported", which
claimed knowledge we did not have - 352 of 3168 cells on a real page, a third of
the third day's column.

Slots entirely older than the earliest report in the response now use a lighter
grey. Coverage is judged from all reports rather than per satellite: a quiet
satellite has no reports of its own, but the slots it shares with the rest of the
response were still covered, so it must read as "not heard" rather than "unknown".

The two greys are now in the legend, which previously listed only the four active
states. That matters more than it sounds: on the live page 81% of cells are
"nobody reported" and 11% are outside our data, so a user looking at a mostly-grey
row had no way to tell a dead satellite from a gap in what we fetched. The legend
chips use a solid dot, so the two greys stay distinguishable despite the 25%
alpha background. Strings added to all nine locales.

Three tests cover it: a day entirely before the data starts, a day straddling the
boundary, and an empty response marking nothing as covered.
2026-08-22 10:27:44 +00:00
mckero 8f646d76f9 fix(amsat): align the status grid to UTC calendar days, one stripe per slot
Two defects in our own AMSAT page, both found by auditing the change that exposed
them.

The day columns claimed to be dates but were a rolling window anchored on the
fetch time. Fetching at 06:07 UTC put 17.9 hours of yesterday into the cell
labelled today; measured against a live amsat.org page of 1021 reports, 73% of
them landed in the wrong day column and none matched the official cell. Days are
now UTC calendar days and slots are fixed UTC bands - slot 0 is 22:00-24:00, slot
11 is 00:00-02:00 - so a cell's contents match its label whenever it is fetched.

The day cell painted one colour for the whole day, taken from the first slot that
had a report, so a satellite that worked all morning and failed all afternoon
looked identical to one that worked once - the reported symptom. It now draws one
stripe per two-hour slot in the same 64x28 dp footprint. Twelve stripes are about
5 dp each, roughly 15 px at 440 dpi, and runs of the same status merge visually,
so a day reads as a few blocks rather than twelve lines. Every density from ldpi
up allocates all twelve without dropping one, and the 4 dp corner radius leaves
95% of the end stripes visible. The report count text is gone; tapping a day
still lists every report from it, which was already the richer view.

buildStatuses and ApiReport are internal rather than private so the grid contract
can be tested. AmSatSlotBuildTest drives it directly: fetchStatus cannot be
tested here because the parsing around it uses Android's JSONObject, a JVM stub
that makes every call return null - eight of nine tests written against it failed
for that reason before being rewritten.

Also corrects three KDoc comments claiming 5 days when the code builds 3, and
records in AGENTS.md that the status colours are ARGB literals in core:data,
duplicated in MainTheme, which anything needing themeable or colour-blind-safe
colours has to fix first.
2026-08-22 06:59:39 +00:00
mckero 57d6f9d7ed chore(merge): drop dead leftovers from the upstream merge
Post-merge audit found code the merge left unreferenced:

- SettingsRepo: keySatelliteUrls / keyTransceiversUrls / separatorUrl were
  upstream's list-shaped data-source keys. The merge kept the fork's map-shaped
  DataSourcesSettings, so these three had a definition and zero uses.
- feature/status/res/drawable/ic_refresh.xml: SatStatusScreen imports
  core.presentation.R only, so its R.drawable.ic_refresh resolves to the
  core copy; the feature-local copy was never addressable. It was the only
  file under feature/status/src/main/res, so the directory goes with it.

Verified zero references with a repo-wide grep before removing each symbol.
compileReleaseKotlin plus core:domain / core:data / feature:map /
feature:roaming unit tests stay green.
2026-08-20 13:24:45 +00:00
mckero a654735337 merge: upstream rt-bishop main (18 commits) with conflict resolution
Merges rt-bishop/Look4Sat main (a42a5f1f, 18 commits: AMSAT status page,
customizable data sources, Doppler calculator, radar compass offset, per-sat
offset memory, localized date formats) into the fork's 30-commit audit
baseline.

Conflict resolution policy (user-directed):
- AMSAT feature (AmSatRepository, SatStatusScreen/ViewModel, SatStatus model):
  upstream version, which the user judged better built. MainScreen routes
  Screen.AmSat through SatStatusDestination().
- Localization: our values-zh/values-tr restored (upstream's merge dropped the
  fork-only strings); new upstream strings (sat_group counts, compass offset,
  frequency offset help) added in EN + ZH.
- fork-only features kept (CW decoder, Mutual/Roaming, WaveLog, APRS, Log tab,
  custom TLE/transceiver source switches): ours.
- Both sides' additions merged where independent: radar compass offset fields
  (Settings/SettingsRepo/RadarState), calculatorOffsetKHz action, wider linear
  transponder detection, deduplicateTransponders + its tests, sunrise/sunset
  tests merged with the moon hour-angle test.
- Sources kept as the fork's map structure (DatabaseRepo depends on it);
  satelliteModes list re-added for SelectionRepo. getSatelliteTypesIds /
  setSatelliteTypeIds re-added to ISettingsRepo+SettingsRepo; SharedDialog
  re-added to Components; providePairedBluetoothDevices added to
  IMainContainer/MainContainer.
- Icons renamed upstream (ic_satellites->ic_sputnik, ic_radar->ic_satellite)
  applied to Navigation/MutualScreen.

Build verified: all modules compileReleaseKotlin + unit tests
(core:domain, core:data, feature:map, feature:roaming) green.
2026-08-20 12:35:01 +00:00
mckero 7319cf8f5b fix(coroutines): propagate cancellation in remote source and status view model
RemoteSource's five suspend functions and SatStatusViewModel's two fetch paths
caught bare Exception, which also swallows CancellationException. When the
owning scope is cancelled (screen leaves, app closes) a cancelled network call
was reported as a null/error result instead of stopping: the caller kept
running until the next suspension point, and SatStatusViewModel wrote state
updates into an already-cancelled scope. Correct coroutine hygiene is to let
cancellation propagate - rethrow CancellationException before the generic
catch. Verified semantically with an asyncio probe: a swallowed cancel returns
a normal-looking null and the caller continues; a propagated cancel stops the
coroutine immediately.

No behaviour change for real errors; :core:data and :feature:status compile.
2026-08-17 16:46:09 +00:00
mckero 828f720955 fix(status): show gray cell instead of crashing on an empty day
AmSatParser deliberately uses getOrNull + mapNotNull while reading each day's
12 slots, so a shortened HTML row can legitimately produce SatDay(slots=[]).
StatusRow then selected the first non-gray slot and fell back to slots.first(),
which throws NoSuchElementException and crashes the entire AMSAT status screen.

Use firstOrNull for both lookups and render a zero-count gray placeholder when
no slot exists. Real amsat.org HTML currently has all 41 satellite rows at the
full 73 cells, but the parser's own tolerance contract means the UI must handle
what it can emit.

Verified against the live page: parser matches 41/41 rows and 477/477 reports;
:feature:status:compileReleaseKotlin BUILD SUCCESSFUL.
2026-08-14 15:27:43 +00:00
Arty Bishop 7cdc2952dc v4.4.6 - AMSAT status page, fully customizable data sources 2026-08-13 09:47:26 +02:00
Arty Bishop 24eebdef74 Consolidated app dialogs and tweaked bottom sheets 2026-08-11 14:14:14 +02:00
Arty Bishop 3f5b48f270 Integrated the AMSAT status page created by MCKero6423 2026-08-11 14:05:55 +02:00
mckero 10eb84690e Added AMSAT satellite status tracking page (#234) 2026-08-08 14:26:36 +02:00
mckero de85aa1e8b refactor(amsat): align with Material3 conventions per PR #233 review
Addresses feedback from rt-bishop/Look4Sat#233:

1. Migrate hardcoded colors to MainTheme colorScheme
   - Extend darkScheme: tertiary (Active 0xFF648FFF), tertiaryContainer (Telemetry 0xFFFFB000)
   - Extend lightScheme: tertiary (0xFF3C6FE0), tertiaryContainer (0xFFE09800) for contrast
   - Remove top-level Color() constants from SatStatusScreen.kt
   - Add statusColorOf() mapper using MaterialTheme.colorScheme

2. Move HTTP implementation from domain to data layer (Clean Architecture)
   - Delete AmSatApiClient.kt from core:domain (violates AGENTS.md: "Pure Kotlin, NO Android deps")
   - Migrate to IRemoteSource/RemoteSource in core:data (uses existing OkHttp3)
   - AmSatRepository now depends on IRemoteSource instead of AmSatApiClient

3. Inline JSON parsing (prepare for java.time migration)
   - Parse AMSAT API responses (names, reports) in AmSatRepository
   - Time parsing still uses manual logic (java.time desugaring in follow-up)

Before:
  - Hardcoded Color(0xFFXXXXXX) in UI + Repository (no theme support)
  - HttpURLConnection in domain layer (architecture violation)
  - AMSAT colors duplicated across modules

After:
  - MaterialTheme.colorScheme.tertiary/tertiaryContainer (light/dark adaptive)
  - HTTP via data layer RemoteSource (follows AGENTS.md architecture)
  - Single source of truth for AMSAT colors

Ref: https://github.com/rt-bishop/Look4Sat/pull/233#discussion_r1868599947
Ref: AGENTS.md "core:domain - Pure Kotlin (JVM). NO Android dependencies."
2026-08-05 15:26:42 +00:00
mckero 90bb584813 fix(status): drop appcompat tint attr from ic_refresh (library module has no such attr) 2026-08-05 08:53:05 +00:00
mckero f789a15338 fix(status): AMSAT page load failure, refresh spinner, retry button
- P0: fetchStatus() now runs on Dispatchers.IO - the previous
  synchronous URLConnection on the main thread threw
  NetworkOnMainThreadException and showed "load failed" on every open
- P1: refresh button rotates a vector icon (ic_refresh) instead of the
  "↻" text glyph, whose off-center font metrics made the spinner
  orbit around a shifted pivot
- P2: error state gains a Retry button (4 locales); amsat_refresh
  string added (5 locales)
- versionCode 456 (bump for reinstalling over 455), versionName stays 4.5.5
2026-08-05 08:50:01 +00:00
mckero 6221516893 feat(amsat): 4.5.5 official AMSAT API + edge-to-edge status page
- Replace HTML parsing with the official AMSAT Satellite Status API v1
  (catalog.php + reports.php, JSON): AmSatApiClient (pure JVM, hand
  rolled ISO-8601/epoch parsing for minSdk 24) + rewritten
  AmSatRepository (satellite list from catalog, reports slotted into
  6 days x 12 two-hour slots, status colors per report value)
- Fix edge-to-edge: status bar / navigation bar insets on the status
  page (refresh button and update time were unreachable)
- Version 4.5.5 (455), What's new in all 5 locales
2026-08-05 08:12:17 +00:00
mckero c9cab8457d chore(i18n): translate all code comments to English
All Chinese comments (//, /* */, KDoc) across core/app/feature/build-
logic translated to English (550 lines, 73 files after FT8 rollback).
Code logic untouched - comment text only. Verified: all modules
compileDebugKotlin BUILD SUCCESSFUL.
2026-08-05 07:59:35 +00:00
mckero 72f507163a fix(status): shrink day cells so long satellite names stay visible
User feedback: on narrow screens long names (e.g. AO-123_[FM]) were
truncated before the mode tag. Scaled the 6 day cells down
proportionally (weight 1f -> 0.8f, height 26dp -> 24dp) and widened
the name column (weight 1.4f -> 2f) in both header and rows.
Verified: :feature:status:compileDebugKotlin BUILD SUCCESSFUL.
2026-08-04 16:41:41 +00:00
mckero f7402a3531 feat(status): 4.5.3 AMSAT satellite status page + radar log pass divider
New feature/status module: fetches https://amsat.org/status/ and
renders a live status grid in the official site colors:
- Parser (AmSatParser): 47 satellites x 6 days x 12 two-hour slots,
  official colors (blue=Active, orange=TLM/Beacon, pink=Not Heard,
  deep-orange=Conflicting, gray=none); 598+ report details extracted
  from inline JS tooltips (callsign/date/time/grid)
- Three-level viz: color grid -> report count -> tap day cell opens
  report list dialog
- Manual refresh with spin animation + last-updated timestamp +
  legend row; loading/error states
- New "AMSAT" entry in the More menu (Screen.AmSat), integrated with
  page-order / hide-page settings (SettingsScreen screens list,
  defaultSubMenuOrder, allNavItems, migration for existing users)
- AmSatRepository via IRemoteSource.getStatusHtml() (UA header);
  shared remoteSource promoted to a lazy class property in MainContainer

Radar page Log tab: local entries now grouped by pass session with a
thick divider + satellite label between groups (matches the log page).

What's new updated in EN/ZH/TR/IN/ID. Version bumped to 4.5.3/453.
Not released (user gates all releases).
2026-08-04 15:57:41 +00:00