Add the two thermal controls the shipping UI was missing#658
Conversation
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 Report❌ Patch coverage is
Additional details and impacted files@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
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 keydeepMode). - Move
FLAG_KEEP_SCREEN_ONhandling intoScreenWakeand apply it from the activity lifecycle and settings toggle. - Add/extend unit tests for
ScreenWakebehavior 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.
…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>
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:
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_ONwas added unconditionally inonCreate, so a foreground session held the panel awake at outdoor brightness for its whole duration.ModeProfile.deepDecodeBudgetMillis()ismax(2500, slot × 0.75), i.e. 11.25 s of every 15 s FT8 slot, 301 deep passes that session.GeneralVariables.deepDecodeModehas always existed and the decoder honours it, but the fast/deep toggle only ever lived in the retired legacyassets/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 newkeepScreenOnconfig key, defaulting to true so an install that predates the setting behaves exactly as before. Flag application moves intoScreenWake.apply(window, keepOn):onCreatewith the default, since config has not hydrated that early;onResumeonce the stored value is loaded;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
deepModekey 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,keepScreenOnabsent 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