diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 5671322d17..9f757671e3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -414,7 +414,7 @@ object ChatModel { // We delete taking into account meta.createdAt to make sure we will not be in situation when two items with the same id will be deleted // (it can happen if already deleted chat item in backend still in the list and new one came with the same (re-used) chat item id) val remove = it.id == cItem.id && it.meta.createdAt == cItem.meta.createdAt - if (remove) { AudioPlayer.stop(it) } + if (remove) { withBGApi { AudioPlayer.stop(it) } } remove } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/RecAndPlay.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/RecAndPlay.kt index fd1824d5b6..c1526e0014 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/RecAndPlay.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/RecAndPlay.kt @@ -28,7 +28,7 @@ data class CurrentlyPlayingState( interface AudioPlayerInterface { val currentlyPlaying: MutableState - fun play( + suspend fun play( fileSource: CryptoFile, audioPlaying: MutableState, progress: MutableState, @@ -36,12 +36,12 @@ interface AudioPlayerInterface { resetOnEnd: Boolean, smallView: Boolean, ) - fun stop() - fun stop(item: ChatItem) - fun stop(fileName: String?) - fun pause(audioPlaying: MutableState, pro: MutableState) - fun seekTo(ms: Int, pro: MutableState, filePath: String?) - fun duration(unencryptedFilePath: String): Int? + suspend fun stop() + suspend fun stop(item: ChatItem) + suspend fun stop(fileName: String?) + suspend fun pause(audioPlaying: MutableState, pro: MutableState) + suspend fun seekTo(ms: Int, pro: MutableState, filePath: String?) + suspend fun duration(unencryptedFilePath: String): Int? } expect object AudioPlayer: AudioPlayerInterface diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index e3bd936067..c51c429bb8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -178,7 +178,9 @@ fun ChatView(staleChatId: State, onComposed: suspend (chatId: String) - selectedChatItems = selectedChatItems, back = { hideKeyboard(view) - AudioPlayer.stop() + withBGApi { + AudioPlayer.stop() + } chatModel.chatId.value = null chatModel.groupMembers.clear() chatModel.groupMembersIndexes.clear() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeVoiceView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeVoiceView.kt index b070dce1d1..139cfd5856 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeVoiceView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeVoiceView.kt @@ -52,10 +52,12 @@ fun ComposeVoiceView( ) { IconButton( onClick = { - if (!audioPlaying.value) { - AudioPlayer.play(CryptoFile.plain(filePath), audioPlaying, progress, duration, resetOnEnd = false, smallView = false) - } else { - AudioPlayer.pause(audioPlaying, progress) + withBGApi { + if (!audioPlaying.value) { + AudioPlayer.play(CryptoFile.plain(filePath), audioPlaying, progress, duration, resetOnEnd = false, smallView = false) + } else { + AudioPlayer.pause(audioPlaying, progress) + } } }, enabled = finishedRecording @@ -87,7 +89,9 @@ fun ComposeVoiceView( if (cancelEnabled) { IconButton( onClick = { - AudioPlayer.stop(filePath) + withBGApi { + AudioPlayer.stop(filePath) + } cancelVoice() }, modifier = Modifier.padding(0.dp) @@ -121,7 +125,7 @@ fun FinishedRecordingSlider(backgroundColor: Color, progress: MutableState, 0.24f) Slider( progress.value.toFloat(), - onValueChange = { AudioPlayer.seekTo(it.toInt(), progress, filePath) }, + onValueChange = { withBGApi { AudioPlayer.seekTo(it.toInt(), progress, filePath) } }, Modifier .fillMaxWidth() .drawBehind { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt index 5ae46ef4e7..f0583dfccd 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt @@ -62,8 +62,10 @@ fun CIVoiceView( val play: () -> Unit = { val playIfExists = { if (fileSource.value != null) { - AudioPlayer.play(fileSource.value!!, audioPlaying, progress, duration, resetOnEnd = true, smallView = smallView) - brokenAudio = !audioPlaying.value + withBGApi { + AudioPlayer.play(fileSource.value!!, audioPlaying, progress, duration, resetOnEnd = true, smallView = smallView) + brokenAudio = !audioPlaying.value + } } } if (chatModel.connectedToRemote() && fileSource.value == null) { @@ -75,7 +77,10 @@ fun CIVoiceView( } else playIfExists() } val pause = { - AudioPlayer.pause(audioPlaying, progress) + withBGApi { + AudioPlayer.pause(audioPlaying, progress) + } + Unit } val text = remember(ci.file?.fileId, ci.file?.fileStatus) { derivedStateOf { @@ -87,7 +92,9 @@ fun CIVoiceView( } } VoiceLayout(file, ci, text, audioPlaying, progress, duration, brokenAudio, sent, hasText, timedMessagesTTL, showViaProxy, sizeMultiplier, play, pause, longClick, receiveFile) { - AudioPlayer.seekTo(it, progress, fileSource.value?.filePath) + withBGApi { + AudioPlayer.seekTo(it, progress, fileSource.value?.filePath) + } } if (smallView) { KeyChangeEffect(chatModel.chatId.value, chatModel.currentUser.value?.userId, chatModel.currentRemoteHost.value) { diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/RecAndPlay.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/RecAndPlay.desktop.kt index c2ae402160..aae0b77133 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/RecAndPlay.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/RecAndPlay.desktop.kt @@ -92,16 +92,16 @@ actual object AudioPlayer: AudioPlayerInterface { return position } - override fun stop() { + override suspend fun stop() { if (currentlyPlaying.value == null) return player.stop() stopListener() } - override fun stop(item: ChatItem) = stop(item.file?.fileName) + override suspend fun stop(item: ChatItem) = stop(item.file?.fileName) // FileName or filePath are ok - override fun stop(fileName: String?) { + override suspend fun stop(fileName: String?) { if (fileName != null && currentlyPlaying.value?.fileSource?.filePath?.endsWith(fileName) == true) { stop() } @@ -126,7 +126,7 @@ actual object AudioPlayer: AudioPlayerInterface { progressJob = null } - override fun play( + override suspend fun play( fileSource: CryptoFile, audioPlaying: MutableState, progress: MutableState, @@ -155,19 +155,19 @@ actual object AudioPlayer: AudioPlayerInterface { realDuration?.let { duration.value = it } } - override fun pause(audioPlaying: MutableState, pro: MutableState) { + override suspend fun pause(audioPlaying: MutableState, pro: MutableState) { pro.value = pause() audioPlaying.value = false } - override fun seekTo(ms: Int, pro: MutableState, filePath: String?) { + override suspend fun seekTo(ms: Int, pro: MutableState, filePath: String?) { pro.value = ms if (currentlyPlaying.value?.fileSource?.filePath == filePath) { player.seekTo(ms) } } - override fun duration(unencryptedFilePath: String): Int? { + override suspend fun duration(unencryptedFilePath: String): Int? { var res: Int? = null try { val helperPlayer = AudioPlayerComponent().mediaPlayer() @@ -215,7 +215,7 @@ actual object SoundPlayer: SoundPlayerInterface { tmpFile.deleteOnExit() SoundPlayer::class.java.getResource("/media/ring_once.mp3")!!.openStream()!!.use { it.copyTo(tmpFile.outputStream()) } playing = true - scope.launch(Dispatchers.Default) { + scope.launch { while (playing && sound) { AudioPlayer.play(CryptoFile.plain(tmpFile.absolutePath), mutableStateOf(true), mutableStateOf(0), mutableStateOf(0), resetOnEnd = true, smallView = false) delay(3500) @@ -225,7 +225,9 @@ actual object SoundPlayer: SoundPlayerInterface { override fun stop() { playing = false - AudioPlayer.stop() + withBGApi { + AudioPlayer.stop() + } } } @@ -259,6 +261,8 @@ actual object CallSoundsPlayer: CallSoundsPlayerInterface { override fun stop() { playingJob?.cancel() - AudioPlayer.stop() + withBGApi { + AudioPlayer.stop() + } } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt index 50eeaee604..d294853f13 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt @@ -60,7 +60,9 @@ actual class VideoPlayer actual constructor( if (soundEnabled.value) { RecorderInterface.stopRecording?.invoke() } - AudioPlayer.stop() + withBGApi { + AudioPlayer.stop() + } VideoPlayerHolder.stopAll() if (listener.value == null) { runCatching {