Skip to content

Add a Light theme option in Settings (Appearance > Theme)#353

Open
patrickrb wants to merge 2 commits into
devfrom
feat/light-theme
Open

Add a Light theme option in Settings (Appearance > Theme)#353
patrickrb wants to merge 2 commits into
devfrom
feat/light-theme

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

What

Adds a selectable Light theme (dark stays the default) via a Theme list picker in a new Appearance section under Settings → Advanced. Built so more themes can be added later with a single enum entry.

Why

The app was hard-locked to dark — a single darkColorScheme plus a forced MODE_NIGHT_YES, with ~700 color references pointing directly at hardcoded dark constants. There was no light palette and no way to switch.

How

The key trick avoids rewriting ~700 call sites: back the existing color names in theme/Color.kt with a single mutableStateOf palette (activePalette). Compose tracks State reads in both the composition and the draw phase, so every existing color read — in composables and in DrawScope lambdas — becomes reactive automatically. Switching themes swaps the palette and repaints the whole UI live, in place (no activity recreate, no flicker).

Saturated semantic hues (Status*, Band*, Target*, PskSpot) stay theme-independent constants — they read fine on either background and this avoids stale static captures (e.g. the band-color map).

Changes

  • theme/Color.ktFT8AFPalette data class, DarkPalette/LightPalette, activePalette snapshot state; switchable colors are now reactive getters (names unchanged).
  • theme/FT8AFTheme.kt — build the Material ColorScheme from the active palette; drive system-bar icon appearance off palette luminance; FT8AFColors members become getters.
  • theme/ThemePreference.kt (new)ThemeOption enum, palette map, id parse/round-trip, SharedPreferences persistence (read synchronously before setContent to avoid a cold-start flash), and live applyTheme().
  • ComposeMainActivity — apply the saved theme before setContent instead of forcing MODE_NIGHT_YES.
  • AdvancedSettings — Appearance section + Theme picker (mirrors the Language picker).
  • strings_compose.xml — new appearance/theme strings (locale files fall back to default until translated).

Tests

New ThemePreferenceTest (pure logic, no Robolectric): id parse/round-trip + unknown→default fallback, paletteFor mapping, dark≠light palette spot checks, isLight flags, and currentThemeNameRes. gradlew testDebugUnitTest passes; assembleDebug packages cleanly.

Not done here

On-device visual verification: installing a local debug build over the CI-signed app requires an uninstall that wipes the QSO log/config, so I left the device check for the maintainer. Proposed Light hex values are a sensible starting point and may want on-device tuning (notably the amber accent / cyan signal contrast on white).

🤖 Generated with Claude Code

The app was hard-locked to dark. Introduce a selectable Light theme
(dark stays the default) via a "Theme" list picker in a new Appearance
section of Advanced settings, structured so more themes can be added
later.

Mechanism: back the existing color names in theme/Color.kt with a single
snapshot-state palette (activePalette). Compose tracks State reads in both
the composition and draw phases, so every existing color reference — in
composables and in DrawScope lambdas — becomes reactive with no call-site
changes. Switching themes swaps the palette and repaints the whole UI live
(no activity recreate). Saturated semantic hues (Status/Band/Target/Psk)
stay theme-independent constants.

- theme/Color.kt: FT8AFPalette + Dark/Light palettes + activePalette state;
  switchable colors become reactive getters.
- theme/FT8AFTheme.kt: build the Material ColorScheme from the active palette;
  drive system-bar icon appearance off palette luminance; FT8AFColors getters.
- theme/ThemePreference.kt (new): ThemeOption enum, palette map, id parse/
  round-trip, SharedPreferences persistence (synchronous, read before
  setContent to avoid a cold-start flash), and live applyTheme().
- ComposeMainActivity: apply the saved theme before setContent instead of
  forcing MODE_NIGHT_YES.
- AdvancedSettings: Appearance section + Theme picker (mirrors Language).
- strings_compose.xml: new appearance/theme strings.
- Unit tests for the pure ThemePreference logic.

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

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 44.50549% with 101 lines in your changes missing coverage. Please review.
✅ Project coverage is 11.87%. Comparing base (6bc083c) to head (ee78b99).
⚠️ Report is 31 commits behind head on dev.

Files with missing lines Patch % Lines
...main/kotlin/radio/ks3ckc/ft8af/theme/FT8AFTheme.kt 0.00% 60 Missing ⚠️
...radio/ks3ckc/ft8af/ui/settings/AdvancedSettings.kt 0.00% 27 Missing ⚠️
...kotlin/radio/ks3ckc/ft8af/theme/ThemePreference.kt 54.54% 10 Missing ⚠️
.../src/main/kotlin/radio/ks3ckc/ft8af/theme/Color.kt 95.83% 3 Missing ⚠️
...n/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #353      +/-   ##
============================================
+ Coverage     11.70%   11.87%   +0.16%     
- Complexity      113      120       +7     
============================================
  Files            85       90       +5     
  Lines         11691    12673     +982     
  Branches       2110     2255     +145     
============================================
+ Hits           1369     1505     +136     
- Misses        10191    11037     +846     
  Partials        131      131              
Files with missing lines Coverage Δ
...n/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt 2.50% <0.00%> (-0.08%) ⬇️
.../src/main/kotlin/radio/ks3ckc/ft8af/theme/Color.kt 96.66% <95.83%> (-3.34%) ⬇️
...kotlin/radio/ks3ckc/ft8af/theme/ThemePreference.kt 54.54% <54.54%> (ø)
...radio/ks3ckc/ft8af/ui/settings/AdvancedSettings.kt 13.12% <0.00%> (-2.67%) ⬇️
...main/kotlin/radio/ks3ckc/ft8af/theme/FT8AFTheme.kt 0.00% <0.00%> (ø)

... and 3 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 a user-selectable Light theme (Dark remains default) by introducing a theme/palette abstraction that keeps existing color call sites intact while enabling live theme switching and persistence across launches.

Changes:

  • Introduces ThemeOption + SharedPreferences persistence and live applyTheme() startup/application path.
  • Refactors theme colors to be backed by a snapshot activePalette so existing color names become reactive getters.
  • Adds an Appearance section in Advanced Settings with a Theme picker, plus associated string resources and unit tests.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/theme/Color.kt Adds FT8AFPalette, dark/light palettes, and snapshot-backed activePalette with reactive color getters to avoid rewriting call sites.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/theme/FT8AFTheme.kt Builds Material3 ColorScheme from the active palette and adjusts system bar icon appearance based on palette brightness.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/theme/ThemePreference.kt Adds theme enum + id parsing, palette mapping, SharedPreferences persistence, and applyTheme() (palette swap + night mode).
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt Applies the saved theme before setContent instead of forcing night mode.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/settings/AdvancedSettings.kt Adds Appearance section and Theme picker dialog wired to applyTheme() + persistence.
ft8af/app/src/main/res/values/strings_compose.xml Adds strings for Appearance section and theme picker labels/descriptions.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/theme/ThemePreferenceTest.kt Adds pure unit tests for theme id parsing/round-trip, palette mapping, and display-name resolver.

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

Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/theme/FT8AFTheme.kt Outdated
Replace the hand-rolled RGB-weighted average with Compose's luminance()
(WCAG relative luminance, with sRGB gamma decoding) when choosing the
Material3 base scheme and system-bar icon appearance, so near-mid
backgrounds classify correctly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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