app: persist device mute across app restart#8504
Conversation
Mute was in-memory only, so an OS kill/force-quit while muted silently resumed device recording on the next reconnect (Featurebase: 'If I turn off recording why doesnt it stay off?', 'Cv1 unmutes on disconnect/ reconnect'). Restore _isPaused from the deviceMuted pref on construction and persist it in pause/resumeDeviceRecording; the existing reconnect path re-applies wasPaused.
… refs Adds device-mute persistence tests. Also removes 3 DeviceType.frame references left behind by the Frame removal (#8500) that broke compilation of this test file on main.
There was a problem hiding this comment.
1 issue found across 3 files
Confidence score: 4/5
- In
app/lib/providers/capture_provider.dart, theSharedPreferences.setBoolcall is asynchronous and currently not awaited, so an app kill/right-after-exit path could drop the persisted flag and lead to inconsistent state on next launch — await the write (or otherwise guarantee completion) before merging to fully de-risk it.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/lib/providers/capture_provider.dart">
<violation number="1" location="app/lib/providers/capture_provider.dart:2286">
P3: SharedPreferences setBool writes are async; assign without `await` risks losing the persisted flag on kill.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| await SharedPreferencesUtil().saveBool('nativeBleStreamingEnabled', false); | ||
| _isPaused = true; | ||
| // Persist so the mute survives an app kill/restart, not just a reconnect. | ||
| SharedPreferencesUtil().deviceMuted = true; |
There was a problem hiding this comment.
P3: SharedPreferences setBool writes are async; assign without await risks losing the persisted flag on kill.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/lib/providers/capture_provider.dart, line 2286:
<comment>SharedPreferences setBool writes are async; assign without `await` risks losing the persisted flag on kill.</comment>
<file context>
@@ -2278,13 +2282,17 @@ class CaptureProvider extends ChangeNotifier
await SharedPreferencesUtil().saveBool('nativeBleStreamingEnabled', false);
_isPaused = true;
+ // Persist so the mute survives an app kill/restart, not just a reconnect.
+ SharedPreferencesUtil().deviceMuted = true;
updateRecordingState(RecordingState.pause);
notifyListeners();
</file context>
|
Solid implementation — persisting the mute flag across restart closes a real privacy gap (the Featurebase complaints are exactly right: a muted device silently resuming recording on reconnect after an OS kill is a trust problem). Verified the core logic: The drive-by One minor suggestion: it'd round out the test coverage to add a case asserting Leaving formal approval to a human maintainer since this changes recording-state persistence semantics — worth a quick confirm that indefinite persistence until explicit resume is the intended UX, especially given the broader mute investigation this is part of. |
Problem
Device mute (double-tap pause) was stored in-memory only (
_isPaused), so it did not survive an app kill / force-quit. After the OS reclaimed the app (routine on Android background) or the user quit, the next device reconnect silently resumed recording even though the user had muted.This is a long-standing, recurring complaint on Featurebase:
The simple in-session reconnect case was already fixed (mute restored via
wasPausedinstreamDeviceRecording), but that state was lost on restart — so the complaints persisted.Fix
Persist the mute and restore it on startup so the existing reconnect path re-applies it:
preferences.dart— newdeviceMutedbool pref (mirrorsbatchMuted).capture_provider.dart— restore_isPausedfromdeviceMutedin the constructor; write the preftrue/falseinpauseDeviceRecording/resumeDeviceRecording. On the next reconnect,streamDeviceRecordingreads_isPausedaswasPausedand re-applies the mute instead of resuming. All mute entry points go through pause/resume, so they're covered.Tests
Added 3 tests in
capture_provider_test.dart:deviceMutedis setpauseDeviceRecordingpersists the flagAll 3 pass. (Other failures in this file are a pre-existing
Env._instance not initializedsandbox issue in the background-mode tests — unrelated.)Drive-by fix
maindid not compile this test file: the Frame removal (#8500) droppedDeviceType.framefrom the enum but left 3 references incapture_provider_test.dart. Removed the two Frame-only tests and switched the third toDeviceType.bee.Scope
This is gap #3 of the mute investigation. Still open:
_checkAndStartAutoSync) ignores mute, so captures made during a mute window still transcribe on reconnect.