Skip to content

Add the two thermal controls the shipping UI was missing#658

Merged
patrickrb merged 2 commits into
devfrom
feat/thermal-controls
Jul 23, 2026
Merged

Add the two thermal controls the shipping UI was missing#658
patrickrb merged 2 commits into
devfrom
feat/thermal-controls

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Why

A long portable session heats the phone until it browns out its own OTG accessory rail and drops the CAT link. From the 2026-07-23 field log:

time battery temp voltage USB drops
07:25 (start) 35.1 °C 4147 mV
08:09 48.6 °C 3991 mV
09:18 44.4 °C 3725 mV 12 in the preceding 30 min

Zero drops in the first 90 minutes while the phone was cool and full; twelve bus re-enumerations once it was hot, two of which left the rig keyed (fixed separately in #652).

The phone's two largest sustained loads during that session were both unadjustable from the shipping UI:

  • FLAG_KEEP_SCREEN_ON was added unconditionally in onCreate, so a foreground session held the panel awake at outdoor brightness for its whole duration.
  • Deep decode runs the subtract-and-redecode loop for up to 75% of every slot at full CPUModeProfile.deepDecodeBudgetMillis() is max(2500, slot × 0.75), i.e. 11.25 s of every 15 s FT8 slot, 301 deep passes that session. GeneralVariables.deepDecodeMode has always existed and the decoder honours it, but the fast/deep toggle only ever lived in the retired legacy assets/ConfigFragment.java. It was never ported to Compose, so whatever sits in the config DB is what you get, permanently.

What

Both are now toggles in a new POWER & HEAT section of Advanced settings.

Keep screen on → new GeneralVariables.keepScreenOn, persisted under a new keepScreenOn config key, defaulting to true so an install that predates the setting behaves exactly as before. Flag application moves into ScreenWake.apply(window, keepOn):

  • called from onCreate with the default, since config has not hydrated that early;
  • re-applied in a new onResume once the stored value is loaded;
  • applied live from the toggle, so the effect is visible from the settings screen.

RX keeps running with the screen off via RxForegroundService, so turning it off costs nothing but having to wake the phone to see the waterfall.

Deep decode → writes the same deepMode key the legacy fragment used, so an existing preference carries over untouched. The description states the trade-off plainly: in that same session deep passes found the partner's reply in 31 cycles the fast pass missed, so this is a lever for hot days, not a default to change.

What this does not do

Neither toggle stops the phone getting hot on its own — the hardware fix is powering the interface externally rather than off the phone's OTG rail. This makes the two biggest software contributors adjustable, which they weren't.

Tests

  • ScreenWakeTest — apply true/false, no-op clear on a window that never held the flag, idempotent repeats, re-enable after disable, and flag-mask reading that isn't confused by other window flags.
  • DatabaseOprConfigHydrationTest — both keys hydrate on and off, keepScreenOn absent from config keeps the previous hard-coded behaviour, and a non-boolean value reads as off rather than crashing hydration (the failure mode that test class exists for).

Full suite: 2481 tests, 0 failures. Installed on a Pixel 8 and smoke-launched — process comes up clean with no crash records. The settings section itself was not visually verified; the device was locked.

🤖 Generated with Claude Code

A long portable session heats the phone until it browns out its own OTG
accessory rail and drops the CAT link. Field log 2026-07-23: battery climbed
35.1C -> 48.6C over two hours with the pack sagging 4147mV -> 3725mV, zero
USB drops in the first 90 minutes while it was cool, then twelve bus
re-enumerations once it was hot - two of which left the rig keyed.

The phone's two largest sustained loads during that session were both
unadjustable:

  - FLAG_KEEP_SCREEN_ON was added unconditionally in onCreate, so a
    foreground session held the panel awake at outdoor brightness for its
    whole duration, with no setting.

  - Deep decode runs the subtract-and-redecode loop for up to 75% of every
    slot at full CPU (ModeProfile#deepDecodeBudgetMillis = max(2500,
    slot*0.75), i.e. 11.25s of every 15s FT8 slot; 301 deep passes that
    session). GeneralVariables.deepDecodeMode has always existed and the
    decoder honours it, but the fast/deep toggle only ever lived in the
    retired legacy assets/ConfigFragment.java - it was never ported to the
    Compose UI, so whatever sits in the config DB is what you get.

Both are now toggles in a new POWER & HEAT section of Advanced settings.

Screen-awake becomes GeneralVariables.keepScreenOn, persisted under a new
"keepScreenOn" config key and defaulting to true, so an install that predates
the setting behaves exactly as before. The flag application moves into
ScreenWake.apply(window, keepOn): called from onCreate with the default
(config has not hydrated that early), re-applied in a new onResume once the
stored value is loaded, and applied live from the toggle so the effect is
visible from the settings screen. RX keeps running with the screen off via
RxForegroundService, so turning it off costs nothing but having to wake the
phone to see the waterfall.

Deep decode writes the same "deepMode" key the legacy fragment used, so an
existing preference carries over untouched. The description states the
trade-off plainly - it is worth real decodes (in the same session deep passes
found the partner's reply in 31 cycles the fast pass missed), so this is a
lever for hot days rather than a default to change.

Neither of these stops the phone getting hot on its own; the hardware fix is
powering the interface externally instead of off the phone's OTG rail. They
make the two biggest software contributors adjustable.

Tests: ScreenWake add/clear/idempotence/flag-mask, and config hydration for
both keys including absent (keeps previous behaviour) and non-boolean values.
Full suite 2481 pass. Installed and smoke-launched on a Pixel 8.

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

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 10.81081% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.78%. Comparing base (55de7f4) to head (6218cf4).
⚠️ Report is 13 commits behind head on dev.

Files with missing lines Patch % Lines
...radio/ks3ckc/ft8af/ui/settings/AdvancedSettings.kt 0.00% 28 Missing ⚠️
...n/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt 0.00% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #658      +/-   ##
============================================
+ Coverage     36.73%   36.78%   +0.04%     
- Complexity      197      211      +14     
============================================
  Files           216      219       +3     
  Lines         26888    27048     +160     
  Branches       3287     3320      +33     
============================================
+ Hits           9877     9949      +72     
- Misses        16785    16864      +79     
- Partials        226      235       +9     
Flag Coverage Δ
android 15.34% <10.81%> (+0.30%) ⬆️
native 9.93% <ø> (ø)

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

Files with missing lines Coverage Δ
...p/src/main/kotlin/radio/ks3ckc/ft8af/ScreenWake.kt 100.00% <100.00%> (ø)
...n/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt 2.29% <0.00%> (-0.03%) ⬇️
...radio/ks3ckc/ft8af/ui/settings/AdvancedSettings.kt 8.31% <0.00%> (-0.55%) ⬇️

... and 8 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

This PR adds two missing “thermal control” toggles to the Compose Advanced Settings UI to help reduce phone heat during long portable sessions (and thereby reduce OTG/CAT dropouts): Keep screen on and Deep decode. It introduces a new GeneralVariables.keepScreenOn preference (persisted via config DB), surfaces the existing deepMode preference in Compose, and factors screen-flag application into a testable ScreenWake helper.

Changes:

  • Add a new POWER & HEAT settings section with toggles for Keep screen on (new key keepScreenOn, default on) and Deep decode (existing key deepMode).
  • Move FLAG_KEEP_SCREEN_ON handling into ScreenWake and apply it from the activity lifecycle and settings toggle.
  • Add/extend unit tests for ScreenWake behavior and for config hydration of both keys.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/ScreenWakeTest.kt New Robolectric coverage for ScreenWake flag add/clear and bit checks.
ft8af/app/src/test/java/com/k1af/ft8af/database/DatabaseOprConfigHydrationTest.java Extends config hydration tests to cover deepMode and new keepScreenOn.
ft8af/app/src/main/res/values/strings_compose.xml Adds strings for the new POWER & HEAT section and toggle descriptions.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/settings/AdvancedSettings.kt Surfaces the two toggles in Compose and persists updates via writeConfig.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ScreenWake.kt New helper encapsulating screen-awake flag application and query.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt Applies screen-awake preference via ScreenWake and re-applies on resume.
ft8af/app/src/main/java/com/k1af/ft8af/GeneralVariables.java Adds keepScreenOn global (default true) alongside existing deepDecodeMode.
ft8af/app/src/main/java/com/k1af/ft8af/database/DatabaseOpr.java Hydrates new keepScreenOn key from config DB into GeneralVariables.

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

Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ComposeMainActivity.kt
Comment thread ft8af/app/src/main/java/com/k1af/ft8af/database/DatabaseOpr.java
Comment thread ft8af/app/src/main/res/values/strings_compose.xml Outdated
…afe hydration

- ComposeMainActivity: observe mutableConfigLoaded and re-apply ScreenWake when
  config hydration completes. onCreate/onResume both run before initData()'s
  async load finishes, so a stored keepScreenOn=false would otherwise keep the
  screen awake until the next resume or a manual toggle. Delivered on the main
  thread, lifecycle-bound.
- DatabaseOpr: hydrate deepMode/keepScreenOn with "1".equals(result) so a null
  config value (missing column from an imported backup) can't NPE.
- strings_compose: mark settings_deep_decode_desc formatted="false" and use a
  single "75%" so the literal percent isn't shown doubled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@patrickrb
patrickrb merged commit 15d9341 into dev Jul 23, 2026
17 checks passed
@patrickrb
patrickrb deleted the feat/thermal-controls branch July 23, 2026 21:17
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