From 6a598357fc10f859a100138f37c0a13da5587443 Mon Sep 17 00:00:00 2001 From: Giacomo Pacini Date: Sun, 15 Dec 2024 23:56:47 +0100 Subject: [PATCH] when a voice message is played, starts the download of the next ones if any, computes their durations and starts playing the next one at the end of the current. Plays a doodle between them Signed-off-by: Giacomo Pacini --- .../IncomingVoiceMessageViewHolder.kt | 2 + .../OutcomingVoiceMessageViewHolder.kt | 2 + .../com/nextcloud/talk/chat/ChatActivity.kt | 74 +++++++++++++++++- .../res/raw/next_voice_message_doodle.ogg | Bin 0 -> 10580 bytes 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/raw/next_voice_message_doodle.ogg diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt index 68aa7d43e..3ea329720 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt @@ -185,6 +185,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : } private fun handleIsPlayingVoiceMessageState(message: ChatMessage) { + colorizeMessageBubble(message) if (message.isPlayingVoiceMessage) { showPlayButton() binding.playPauseBtn.icon = ContextCompat.getDrawable( @@ -199,6 +200,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : binding.seekbar.max = message.voiceMessageDuration * ONE_SEC binding.seekbar.progress = message.voiceMessageSeekbarProgress } else { + showVoiceMessageDuration(message) binding.playPauseBtn.visibility = View.VISIBLE binding.playPauseBtn.icon = ContextCompat.getDrawable( context!!, diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt index f58722534..6b8d36f4b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt @@ -219,6 +219,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : } private fun handleIsPlayingVoiceMessageState(message: ChatMessage) { + colorizeMessageBubble(message) if (message.isPlayingVoiceMessage) { showPlayButton() binding.playPauseBtn.icon = ContextCompat.getDrawable( @@ -233,6 +234,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : binding.seekbar.max = message.voiceMessageDuration * ONE_SEC binding.seekbar.progress = message.voiceMessageSeekbarProgress } else { + showVoiceMessageDuration(message) binding.playPauseBtn.visibility = View.VISIBLE binding.playPauseBtn.icon = ContextCompat.getDrawable( context!!, diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index f73547618..9255ee8c8 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -27,6 +27,11 @@ import android.database.Cursor import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable +import android.media.AudioFocusRequest +import android.media.AudioFormat +import android.media.AudioManager +import android.media.AudioRecord +import android.media.MediaMetadataRetriever import android.media.MediaPlayer import android.net.Uri import android.os.Build @@ -1614,16 +1619,70 @@ class ChatActivity : } private fun startPlayback(message: ChatMessage, doPlay: Boolean = true) { - if (!active) { + //if (!active) { // don't begin to play voice message if screen is not visible anymore. // this situation might happen if file is downloading but user already left the chatview. // If user returns to chatview, the old chatview instance is not attached anymore // and he has to click the play button again (which is considered to be okay) - return - } + // return + //} initMediaPlayer(message) + val id = message.id.toString() + val index = adapter?.getMessagePositionById(id) ?: 0 + + var nextMessage : ChatMessage? = null + for (i in 1..5) { + if(index - i < 0){ + break + } + val curMsg = adapter?.items?.get(index - i)?.item + if(curMsg is ChatMessage) { + if(nextMessage == null) { + nextMessage = curMsg as ChatMessage + } + + if(curMsg.isVoiceMessage){ + val filename = curMsg.selectedIndividualHashMap!!["name"] + val file = File(context.cacheDir, filename!!) + if (!file.exists()) { + downloadFileToCache(curMsg as ChatMessage, false) { + curMsg.isDownloadingVoiceMessage = false + curMsg.voiceMessageDuration = try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(file.absolutePath) // Set the audio file as the data source + val durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) + retriever.release() // Always release the retriever to free resources + (durationStr?.toIntOrNull() ?: 0 )/ 1000 // Convert to int (seconds) + } catch (e: Exception) { + e.printStackTrace() + 0 + } + adapter?.update(curMsg) + } + }else{ + if(curMsg.voiceMessageDuration == 0) { + curMsg.voiceMessageDuration = try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(file.absolutePath) // Set the audio file as the data source + val durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) + retriever.release() // Always release the retriever to free resources + (durationStr?.toIntOrNull() ?: 0 )/ 1000 // Convert to int (seconds) + } catch (e: Exception) { + e.printStackTrace() + 0 + } + adapter?.update(curMsg) + } + } + } + } + } + + val hasConsecutiveVoiceMessage = if(nextMessage != null) nextMessage.isVoiceMessage else false + + mediaPlayer?.let { if (!it.isPlaying && doPlay) { chatViewModel.audioRequest(true) { @@ -1656,6 +1715,15 @@ class ChatActivity : message.voiceMessageSeekbarProgress = 0 adapter?.update(message) stopMediaPlayer(message) + if(hasConsecutiveVoiceMessage){ + val defaultMediaPlayer = MediaPlayer.create(context, R.raw + .next_voice_message_doodle) + defaultMediaPlayer.setOnCompletionListener { + defaultMediaPlayer.release() + setUpWaveform(nextMessage as ChatMessage, doPlay) + } + defaultMediaPlayer.start() + } } } } diff --git a/app/src/main/res/raw/next_voice_message_doodle.ogg b/app/src/main/res/raw/next_voice_message_doodle.ogg new file mode 100644 index 0000000000000000000000000000000000000000..797871076c673174d234cf5376d15f85cdbcd551 GIT binary patch literal 10580 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