Skip to content

Add a "New Prefix" (WPX) decode highlight + filter for prefix chasers - #695

Merged
patrickrb merged 2 commits into
devfrom
optio/task-a093727a-ceed-4489-b1b8-8b304f30615d
Aug 2, 2026
Merged

Add a "New Prefix" (WPX) decode highlight + filter for prefix chasers#695
patrickrb merged 2 commits into
devfrom
optio/task-a093727a-ceed-4489-b1b8-8b304f30615d

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

What & why

Worked All Prefixes (CQ WPX) is one of the most popular amateur-radio award programs — each distinct callsign prefix (W1, VE3, 9A1, DL0, …) counts once, so operators actively hunt "new prefixes" the same way they chase new DXCC entities, zones, states or grids.

The decode list already flags New DXCC / New Zone / New State / New Grid / New Band, but not new prefixes. This PR closes that gap so a WPX chaser can:

  • see a NEW PFX pill on any decoded CQ station whose prefix they haven't logged, and
  • one-tap the "New Prefix" filter to reduce the decode list to only unworked prefixes.

"Wow, I wish every FT8 app did this." — WPX is a top-three award chase and no Android FT8 app surfaces it live in the decode list.

How it works

  • WpxPrefix.of() — a pure, dependency-free CQ WPX prefix extractor:
    • simple calls (W1AWW1, 9A1AA9A1, 3DA0RS3DA0),
    • no-numeral historic calls (RAEMRA0),
    • portable numbers (W1AW/4W4),
    • portable prefixes (DL/W1AWDL0, W1AW/KH6KH6),
    • ignored operational suffixes (/P /M /MM /AM /QRP),
    • anything that isn't a plausible callsign (grids, reports, hashed calls) returns null so it's never flagged.
      It's shared by the worked-prefix loader and the live decode predicate so the two can never disagree.
  • GetAllQSLCallsign builds a distinct worked-prefix set (any band), mirroring the existing worked-grid set; refreshed on the same cadence.
  • New NEW_PREFIX status pill (rose, distinct from the other badges), isNewPrefixStation predicate wired into resolveQsoStatus just below New Grid, a "New Prefix" filter chip + empty state, and a Settings → Decode Highlights toggle.

Defaults

The highlight is off by default (like New Grid) — early in an operator's WPX chase almost every prefix is "new", so opt-in keeps the default decode list quiet. The filter chip is always available regardless of the toggle.

Tests

  • WpxPrefixTest (pure JUnit) — the extractor across simple, leading-digit, no-numeral, portable-number, portable-prefix, suffix and non-callsign cases.
  • NewPrefixTest (Robolectric) — the shared predicate, worked-vs-new behaviour, the pill priority in resolveQsoStatus, and the filterMessages "New Prefix" filter (CQ-only, unworked-only).

All decode + callsign unit tests pass locally (testDebugUnitTest).

Scope / compatibility

Pure UI/logging surface — no change to the decoder, TX pipeline, or FT8/WSJT-X protocol. Follows the exact New DXCC/Zone/State/Grid pattern; the legacy CallingListAdapter is intentionally left untouched (New Zone/State weren't wired there either — the modern Compose decode list is the live path).

🤖 Generated with Claude Code

Worked All Prefixes (CQ WPX) is one of the most-chased amateur-radio award
programs, but until now the decode list could flag new DXCC entities, zones,
states, grids and bands — not new callsign prefixes. This adds a "New Prefix"
highlight pill and decode filter that mark CQ stations whose WPX prefix (e.g.
W1, VE3, DL0) the operator hasn't logged yet, so prefix hunters can spot a new
one at a glance and one-tap-filter the list down to only new prefixes.

- WpxPrefix.of() is a pure, dependency-free CQ WPX prefix extractor (simple
  calls, no-numeral historic calls, portable numbers CALL/n, portable prefixes
  pfx/CALL, ignored /P /M /QRP suffixes; non-callsigns return null). Shared by
  the DB worked-prefix loader and the live decode predicate so they can't drift.
- GetAllQSLCallsign builds a distinct worked-prefix set (any band), mirroring
  the existing worked-grid set.
- New NEW_PREFIX status pill, isNewPrefixStation predicate, "New Prefix" filter
  chip + empty state, and a Settings → Decode Highlights toggle (off by default,
  like New Grid — early on most prefixes are "new").

Tests: WpxPrefixTest covers the extractor across simple/compound/edge cases;
NewPrefixTest covers the predicate, the pill priority, and the filter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 44.44444% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.12%. Comparing base (0360375) to head (b6097f6).
⚠️ Report is 17 commits behind head on dev.

Files with missing lines Patch % Lines
...o/ks3ckc/ft8af/ui/settings/DecodeFilterSettings.kt 0.00% 12 Missing ⚠️
...otlin/radio/ks3ckc/ft8af/ui/decode/DecodeScreen.kt 25.00% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #695      +/-   ##
============================================
- Coverage     36.70%   36.12%   -0.59%     
- Complexity      222      286      +64     
============================================
  Files           227      245      +18     
  Lines         27933    29998    +2065     
  Branches       3485     3935     +450     
============================================
+ Hits          10254    10838     +584     
- Misses        17426    18811    +1385     
- Partials        253      349      +96     
Flag Coverage Δ
android 17.68% <44.44%> (+1.32%) ⬆️
native 9.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../src/main/kotlin/radio/ks3ckc/ft8af/theme/Color.kt 96.77% <100.00%> (+0.03%) ⬆️
...lin/radio/ks3ckc/ft8af/ui/components/StatusPill.kt 75.00% <100.00%> (+1.31%) ⬆️
...n/kotlin/radio/ks3ckc/ft8af/ui/decode/DecodeRow.kt 11.36% <100.00%> (+1.45%) ⬆️
...otlin/radio/ks3ckc/ft8af/ui/decode/DecodeScreen.kt 19.11% <25.00%> (+0.14%) ⬆️
...o/ks3ckc/ft8af/ui/settings/DecodeFilterSettings.kt 0.00% <0.00%> (ø)

... and 25 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds CQ WPX “New Prefix” support to the Compose decode list, enabling both a NEW PFX status pill and a “New Prefix” decode filter backed by a shared prefix extractor and an in-memory worked-prefix set.

Changes:

  • Introduces WpxPrefix.of() to extract CQ WPX prefixes from decoded/logged callsigns.
  • Loads a distinct worked-prefix set from QSLTable and adds NEW_PREFIX pill + “New Prefix” filter + settings toggle/resources.
  • Adds unit tests for the new predicate/filter and for the prefix extractor.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
ft8af/app/src/main/java/com/k1af/ft8af/callsign/WpxPrefix.java New CQ WPX prefix extractor used by both DB loader and live decode predicate.
ft8af/app/src/test/java/com/k1af/ft8af/callsign/WpxPrefixTest.java Unit tests for the WPX prefix extractor.
ft8af/app/src/main/java/com/k1af/ft8af/database/DatabaseOpr.java Loads worked-prefix set from the log and reads highlightNewPrefix config.
ft8af/app/src/main/java/com/k1af/ft8af/GeneralVariables.java Adds worked-prefix set + highlightNewPrefix toggle + checkQSLPrefix.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/decode/DecodeRow.kt Adds isNewPrefixStation and wires NEW_PREFIX into resolveQsoStatus.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/decode/DecodeScreen.kt Adds “New Prefix” filter option, label, empty state, and filter predicate.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/settings/DecodeFilterSettings.kt Adds Settings toggle for the new-prefix highlight.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/components/StatusPill.kt Adds QsoStatus.NEW_PREFIX pill styling + label mapping.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/theme/Color.kt Adds StatusPrefix color constant for the new pill.
ft8af/app/src/main/res/values/strings_compose.xml Adds strings for the new pill/filter/empty state/settings row.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/ui/decode/NewPrefixTest.kt Robolectric tests for predicate, pill priority, and “New Prefix” filter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ft8af/app/src/test/java/com/k1af/ft8af/callsign/WpxPrefixTest.java
Comment thread ft8af/app/src/main/java/com/k1af/ft8af/callsign/WpxPrefix.java
…695)

CALL/n lifted the leading letters off the raw token and appended the new
number. That works only for calls whose prefix starts with letters and
carries a numeral, which is what the tests covered.

Two shapes it got wrong. A digit-leading call has no leading letters at
all, so 9A1AA/7 lifted "" and returned null — a prefix chaser simply never
saw it. A historic call with no numeral is all letters, so RAEM/4 lifted
the whole token and produced "RAEM4", which is not a prefix.

Both already resolve correctly as plain calls (9A1AA -> 9A1, RAEM -> RA0),
so the portable form now runs the base through that same rule and swaps
the trailing numeral: 9A1AA/7 -> 9A7, RAEM/4 -> RA4. Letter-leading calls
are unchanged.

It also inherits simple()'s conservatism, which the old path bypassed:
W1/7 and FN42/7 are a bare prefix and a grid, not callsigns, and now stay
null instead of inventing W7 and FN7.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@patrickrb
patrickrb merged commit e1ef54e into dev Aug 2, 2026
17 checks passed
@patrickrb
patrickrb deleted the optio/task-a093727a-ceed-4489-b1b8-8b304f30615d branch August 2, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants