Matthias Nott
2026-03-07 c23dfe16e95713e7058137308bdbc28419609a39
services/audio.ts
....@@ -4,10 +4,32 @@
44 setAudioModeAsync,
55 } from "expo-audio";
66 import * as LegacyFileSystem from "expo-file-system/legacy";
7
+import { AppState } from "react-native";
78
89 export interface RecordingResult {
910 uri: string;
1011 durationMs: number;
12
+}
13
+
14
+// --- Autoplay suppression ---
15
+// Don't autoplay voice messages when the app is in the background
16
+// or when the user is on a phone call (detected via audio interruption).
17
+let _autoplayEnabled = true;
18
+let _audioInterrupted = false;
19
+
20
+// Track app state — suppress autoplay when backgrounded
21
+AppState.addEventListener("change", (state) => {
22
+ _autoplayEnabled = state === "active";
23
+});
24
+
25
+/** Check if autoplay is safe right now (app in foreground, no interruption). */
26
+export function canAutoplay(): boolean {
27
+ return _autoplayEnabled && !_audioInterrupted;
28
+}
29
+
30
+/** Called externally to signal audio interruption (e.g., phone call started/ended). */
31
+export function setAudioInterrupted(interrupted: boolean): void {
32
+ _audioInterrupted = interrupted;
1133 }
1234
1335 // --- Singleton audio player ---
....@@ -94,13 +116,13 @@
94116
95117 while (audioQueue.length > 0) {
96118 const item = audioQueue.shift()!;
97
- await playOneAudio(item.uri, item.onFinish);
119
+ await playOneAudio(item.uri, item.onFinish, false);
98120 }
99121
100122 processingQueue = false;
101123 }
102124
103
-function playOneAudio(uri: string, onFinish?: () => void): Promise<void> {
125
+function playOneAudio(uri: string, onFinish?: () => void, cancelPrevious = true): Promise<void> {
104126 return new Promise<void>(async (resolve) => {
105127 let settled = false;
106128 const finish = () => {
....@@ -118,8 +140,8 @@
118140 resolve();
119141 };
120142
121
- // Stop any currently playing audio first
122
- if (cancelCurrent) {
143
+ // Stop any currently playing audio first (only for non-queued calls)
144
+ if (cancelPrevious && cancelCurrent) {
123145 cancelCurrent();
124146 }
125147
....@@ -138,9 +160,17 @@
138160 notifyListeners(uri);
139161
140162 player.addListener("playbackStatusUpdate", (status) => {
141
- if (!status.playing && status.currentTime > 0 &&
142
- (status.duration <= 0 || status.currentTime >= status.duration)) {
143
- finish();
163
+ if (!status.playing && status.currentTime > 0) {
164
+ if (status.duration <= 0 || status.currentTime >= status.duration) {
165
+ // Playback finished naturally
166
+ finish();
167
+ } else {
168
+ // Paused mid-playback — likely audio interruption (phone call)
169
+ setAudioInterrupted(true);
170
+ }
171
+ } else if (status.playing && _audioInterrupted) {
172
+ // Resumed after interruption
173
+ setAudioInterrupted(false);
144174 }
145175 });
146176