Skip to content

feat(mobile): a meeting recording survives calls, silenced capture, low space and a deleted note - #1743

Merged
github-actions[bot] merged 1 commit into
plan/meeting-recordingfrom
meeting-recording/4e8927db-2e77-451f-8ba7-81fe87edca40
Nov 10, 2026
Merged

github-actions[bot] merged 1 commit into
plan/meeting-recordingfrom
meeting-recording/4e8927db-2e77-451f-8ba7-81fe87edca40

Conversation

@JSdotNet

Copy link
Copy Markdown
Owner

What changed

A meeting recording on a phone note now survives what Android does to it (plan +meeting-recording, entry 5 — 4e8927db-2e77-451f-8ba7-81fe87edca40).

Event Now
Phone call AudioManager mode (MODE_IN_CALL, call screening/redirect; listener on API 31+, polled on the level tick below; no new permission) pauses the recording once and shows "Paused: a phone call has the microphone." The bar and the notification offer Resume; the call ending never resumes it.
Another app silences the capture MediaRecorder.RegisterAudioRecordingCallback / isClientSilenced (API 29+) shows "Another app has the microphone. Nothing is being recorded." Below 29, ten seconds at level 0 shows "No sound is reaching the recording…" (FlatLevelDetector, skipped when the recorder cannot measure a level).
Low space RecordingSpace + IFreeSpace (StatFs on Android, DriveInfo elsewhere): a start needs 248 MiB (200 + 2 × one 24 MiB part); a running recording is stopped at ≤ 200 MiB (checked every 10 s), attached, and the bar says why.
Note deleted elsewhere The recording runs on; Stop creates "Recording · d MMM yyyy" with every part (RecordingAttachments → NoteViewProjection.CreateAsync), and the old note's page says where it went.
Microphone refused One sentence per host, shared by dictation and the meeting recorder (MicrophoneAccess.DeniedInBrowser, AndroidMicrophoneAccess.Denied).

Shared UI: RecordingBar gains a Notice drawn as an Alert; storybook stories "Paused by a phone call" and "Silenced by another app". RecordingService redraws its notification when the notice changes.

Acceptance criteria and how each was verified

  1. Call pauses, resumes only on Resume — NoteRecordingSessionTests (pause once, off the raising thread; stays paused after the call; Resume during a call; stale queued pause skipped in both orders), NoteRecordingScreenTests; storybook story in the browser (pass). Android detection built, not run on a device.
  2. Silenced capture / flat hint — session and FlatLevelDetectorTests; storybook story (pass). The API 29 callback is built, not run on a device.
  3. Space check and clean stop — RecordingSpaceTests, session tests (refusal, stop, stop with a part kept, stop after the note was deleted).
  4. Deleted note → new note "Recording · " — session tests, RecordingAttachmentsTests (cap counted against the new note), screen test for the moved notice. Browser scenario not run (see below).
  5. Microphone refused = dictation's sentence — WebMeetingRecorderTests; browser: Record and Dictate showed the identical sentence (pass).

Build & Test

  • dotnet build Backlog.WithoutAppHeads.slnf -c Release -p:TreatWarningsAsErrors=true: 0 warnings, 0 errors.
  • Android head, Release, -warnaserror with CI's packaging overrides: 0 warnings, 0 errors.
  • dotnet test --solution Backlog.WithoutAppHeads.slnf -c Release: 23245 total, 23226 passed, 0 failed, 19 skipped.
  • Browser QA (Aspire, Playwright): 3/3 runnable scenarios pass — storybook notices, Record→Stop regression, microphone refused. No errors from the recording code in the Aspire logs.

Review findings left open

None. Two review rounds: 1 bug fixed (a space-stop sentence hid attach failures), 6 advisories fixed (cap counted against the deleted note, false flat hint without AudioContext, stale queued call pause, missing pause/thread tests, storybook wording pointer, an exception dropped during a space stop), plus dictation's web sentence moved onto the shared constant.

What could not be proved

  • Everything on a real Android device: call detection by audio mode, Resume from the notification, the silencing callback on API 29+, the flat hint below 29, StatFs, the 200 MB stop. All are unit-tested at their seams only.
  • Deleted note end to end in the browser: a deletion arrives only through a sync pull, and this machine's Cosmos emulator could not start (Docker: "all predefined address pools have been fully subnetted", stale cosmos/storage containers from other worktrees).
  • Below Android 12, a call that raises a MediaRecorder error before the mode poll sees it stops and attaches instead of pausing.
  • Devbook not updated (the entry's "Update Backlog's own devbook" sub-item is still open): spec check ran check-only and found the requirements intro, capture/features.md#meeting-recording, 06-runtime-view.md#meeting-recording, ADR 0048's "No code is built yet" and tech/mobile.md code-ahead, plus eight behaviours no requirement states yet. They need a flow-spec change.
  • Out of scope, unchanged: a killed-app recording whose note was deleted meanwhile stays in notes/recordings/; the sync service still refuses audio types (entry 4).

Assumptions

  • "MB" is MiB; start threshold 248 MiB (200 + 2 × 24 MiB part), refusal rounds free space down.
  • Notice priority: call > silenced > flat hint, only while the recording is live; after the call ends the bar reads plain "Paused"; Resume during a call is allowed and then shows the silenced notice.
  • A VoIP call (MODE_IN_COMMUNICATION) is not "a phone call"; it is caught as silenced capture.
  • A space stop reuses the fault-stop path, so its reason shows as the bar's failure line and LastFailure.
  • The "saved to a new note" notice lives in NoteRecordingBar (on the deleted note's page); no auto-navigation; the new note carries the title only.
  • A host that cannot tell free space refuses and stops nothing.

Opened by an unattended execute-plan run; Personal Validation is pending — validate on this draft.

🤖 Generated with Claude Code

…ow space and a deleted note

- A phone call (AudioManager mode, no new permission) pauses the recording with
  "Paused: a phone call has the microphone." and only Resume starts it again.
- A capture another app silences shows a notice (AudioRecordingCallback, API 29+);
  below that, ten seconds at a flat level shows a hint.
- A start needs about twice a part free; the recording stops cleanly at 200 MB
  free and says why.
- A note deleted elsewhere while recording keeps recording; Stop saves to a new
  note "Recording · <date>".
- A refused microphone reads the same as dictation on each host.

Plan +meeting-recording, entry 5 (4e8927db-2e77-451f-8ba7-81fe87edca40).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JSdotNet
JSdotNet marked this pull request as ready for review November 10, 2026 23:51
@github-actions
github-actions Bot merged commit 89f0b22 into plan/meeting-recording Nov 10, 2026
8 checks passed
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.

1 participant