Merge pull request #3704 from Ruggero1912/master

Resumes playback of VoiceMessages on screen rotation and continue playing a voice message if chatActivity in background but still open
This commit is contained in:
Marcel Hibbe 2024-03-15 16:01:07 +01:00 committed by GitHub
commit 007c407549
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,6 +11,7 @@
* Copyright (C) 2021-2022 Marcel Hibbe <dev@mhibbe.de> * Copyright (C) 2021-2022 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com> * Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
* Copyright (C) 2023 Ezhil Shanmugham <ezhil56x.contact@gmail.com> * Copyright (C) 2023 Ezhil Shanmugham <ezhil56x.contact@gmail.com>
* Copyright (C) 2024 Giacomo Pacini <giacomo@paciosoft.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -405,6 +406,9 @@ class ChatActivity :
private val onBackPressedCallback = object : OnBackPressedCallback(true) { private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
if (currentlyPlayedVoiceMessage != null) {
stopMediaPlayer(currentlyPlayedVoiceMessage!!)
}
val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java) val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java)
intent.putExtras(Bundle()) intent.putExtras(Bundle())
startActivity(intent) startActivity(intent)
@ -416,6 +420,9 @@ class ChatActivity :
val typingParticipants = HashMap<String, TypingParticipant>() val typingParticipants = HashMap<String, TypingParticipant>()
var callStarted = false var callStarted = false
private var voiceMessageToRestoreId = ""
private var voiceMessageToRestoreAudioPosition = 0
private var voiceMessageToRestoreWasPlaying = false
private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener { private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener {
override fun onSwitchTo(token: String?) { override fun onSwitchTo(token: String?) {
@ -487,6 +494,30 @@ class ChatActivity :
onBackPressedDispatcher.addCallback(this, onBackPressedCallback) onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
initObservers() initObservers()
if (savedInstanceState != null) {
// Restore value of members from saved state
var voiceMessageId = savedInstanceState.getString(CURRENT_AUDIO_MESSAGE_KEY, "")
var voiceMessagePosition = savedInstanceState.getInt(CURRENT_AUDIO_POSITION_KEY, 0)
var wasAudioPLaying = savedInstanceState.getBoolean(CURRENT_AUDIO_WAS_PLAYING_KEY, false)
if (!voiceMessageId.equals("")) {
Log.d(RESUME_AUDIO_TAG, "restored voice messageID: " + voiceMessageId)
Log.d(RESUME_AUDIO_TAG, "audio position: " + voiceMessagePosition)
Log.d(RESUME_AUDIO_TAG, "audio was playing: " + wasAudioPLaying.toString())
voiceMessageToRestoreId = voiceMessageId
voiceMessageToRestoreAudioPosition = voiceMessagePosition
voiceMessageToRestoreWasPlaying = wasAudioPLaying
} else {
Log.d(RESUME_AUDIO_TAG, "stored voice message id is empty, not resuming audio playing")
voiceMessageToRestoreId = ""
voiceMessageToRestoreAudioPosition = 0
voiceMessageToRestoreWasPlaying = false
}
} else {
voiceMessageToRestoreId = ""
voiceMessageToRestoreAudioPosition = 0
voiceMessageToRestoreWasPlaying = false
}
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
@ -548,6 +579,20 @@ class ChatActivity :
this.lifecycle.addObserver(ChatViewModel.LifeCycleObserver) this.lifecycle.addObserver(ChatViewModel.LifeCycleObserver)
} }
override fun onSaveInstanceState(outState: Bundle) {
if (currentlyPlayedVoiceMessage != null) {
outState.putString(CURRENT_AUDIO_MESSAGE_KEY, currentlyPlayedVoiceMessage!!.getId())
outState.putInt(CURRENT_AUDIO_POSITION_KEY, currentlyPlayedVoiceMessage!!.voiceMessagePlayedSeconds)
outState.putBoolean(CURRENT_AUDIO_WAS_PLAYING_KEY, currentlyPlayedVoiceMessage!!.isPlayingVoiceMessage)
Log.d(RESUME_AUDIO_TAG, "Stored current audio message ID: " + currentlyPlayedVoiceMessage!!.getId())
Log.d(
RESUME_AUDIO_TAG, "Audio Position: " + currentlyPlayedVoiceMessage!!.voiceMessagePlayedSeconds
.toString() + " | isPLaying: " + currentlyPlayedVoiceMessage!!.isPlayingVoiceMessage
)
}
super.onSaveInstanceState(outState)
}
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
active = false active = false
@ -558,9 +603,6 @@ class ChatActivity :
if (mediaRecorderState == MediaRecorderState.RECORDING) { if (mediaRecorderState == MediaRecorderState.RECORDING) {
stopAudioRecording() stopAudioRecording()
} }
if (currentlyPlayedVoiceMessage != null) {
stopMediaPlayer(currentlyPlayedVoiceMessage!!)
}
val text = binding.messageInputView.messageInput.text.toString() val text = binding.messageInputView.messageInput.text.toString()
val cursor = binding.messageInputView.messageInput.selectionStart val cursor = binding.messageInputView.messageInput.selectionStart
val previous = context.getSharedPreferences(localClassName, MODE_PRIVATE).getString(roomToken, "null") val previous = context.getSharedPreferences(localClassName, MODE_PRIVATE).getString(roomToken, "null")
@ -1352,7 +1394,7 @@ class ChatActivity :
} }
} }
private fun setUpWaveform(message: ChatMessage) { private fun setUpWaveform(message: ChatMessage, thenPlay: Boolean = true) {
val filename = message.selectedIndividualHashMap!!["name"] val filename = message.selectedIndividualHashMap!!["name"]
val file = File(context.cacheDir, filename!!) val file = File(context.cacheDir, filename!!)
if (file.exists() && message.voiceMessageFloatArray == null) { if (file.exists() && message.voiceMessageFloatArray == null) {
@ -1363,11 +1405,11 @@ class ChatActivity :
appPreferences.saveWaveFormForFile(filename, r.toTypedArray()) appPreferences.saveWaveFormForFile(filename, r.toTypedArray())
message.voiceMessageFloatArray = r message.voiceMessageFloatArray = r
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
startPlayback(message) startPlayback(message, thenPlay)
} }
} }
} else { } else {
startPlayback(message) startPlayback(message, thenPlay)
} }
} }
@ -2321,7 +2363,7 @@ class ChatActivity :
} }
} }
private fun startPlayback(message: ChatMessage) { 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. // 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. // this situation might happen if file is downloading but user already left the chatview.
@ -2333,7 +2375,7 @@ class ChatActivity :
initMediaPlayer(message) initMediaPlayer(message)
mediaPlayer?.let { mediaPlayer?.let {
if (!it.isPlaying) { if (!it.isPlaying && doPlay) {
audioFocusRequest(true) { audioFocusRequest(true) {
it.start() it.start()
handleBecomingNoisyBroadcast(register = true) handleBecomingNoisyBroadcast(register = true)
@ -2365,7 +2407,11 @@ class ChatActivity :
}) })
message.isDownloadingVoiceMessage = false message.isDownloadingVoiceMessage = false
message.isPlayingVoiceMessage = true message.isPlayingVoiceMessage = doPlay
//message.voiceMessagePlayedSeconds = lastRecordMediaPosition / VOICE_MESSAGE_SEEKBAR_BASE
//message.voiceMessageSeekbarProgress = lastRecordMediaPosition
// the commented instructions objective was to update audio seekbarprogress
// in the case in which audio status is paused when the position is resumed
adapter?.update(message) adapter?.update(message)
} }
} }
@ -2408,6 +2454,8 @@ class ChatActivity :
lastRecordedSeeked = true lastRecordedSeeked = true
} }
} }
// this ensures that audio can be resumed at a given position
this.seekTo(lastRecordMediaPosition)
} }
setOnCompletionListener { setOnCompletionListener {
stopMediaPlayer(message) stopMediaPlayer(message)
@ -2426,6 +2474,7 @@ class ChatActivity :
adapter?.update(message) adapter?.update(message)
currentlyPlayedVoiceMessage = null currentlyPlayedVoiceMessage = null
lastRecordMediaPosition = 0 //this ensures that if audio track is changed, then it is played from the beginning
mediaPlayerHandler.removeCallbacksAndMessages(null) mediaPlayerHandler.removeCallbacksAndMessages(null)
@ -3750,6 +3799,8 @@ class ChatActivity :
adapter?.addToEnd(chatMessageList, false) adapter?.addToEnd(chatMessageList, false)
} }
scrollToRequestedMessageIfNeeded() scrollToRequestedMessageIfNeeded()
//FENOM: add here audio resume policy
resumeAudioPlaybackIfNeeded()
} }
private fun scrollToFirstUnreadMessage() { private fun scrollToFirstUnreadMessage() {
@ -3854,6 +3905,54 @@ class ChatActivity :
} }
} }
/**
* this method must be called after that the adatper has finished loading ChatMessages items
* it searches by ID the message that was playing,
* then, if it finds it, it restores audio position
* and eventually resumes audio playback
* @author Giacomo Pacini
*/
private fun resumeAudioPlaybackIfNeeded() {
if (!voiceMessageToRestoreId.equals("")) {
Log.d(RESUME_AUDIO_TAG, "begin method to resume audio playback")
if (adapter != null) {
Log.d(RESUME_AUDIO_TAG, "adapter is not null, proceeding")
val voiceMessagePosition = adapter!!.items!!.indexOfFirst {
it.item is ChatMessage && (it.item as ChatMessage).id == voiceMessageToRestoreId
}
if (voiceMessagePosition >= 0) {
val currentItem = adapter?.items?.get(voiceMessagePosition)?.item
if (currentItem is ChatMessage && currentItem.id == voiceMessageToRestoreId) {
currentlyPlayedVoiceMessage = currentItem
lastRecordMediaPosition = voiceMessageToRestoreAudioPosition * 1000
Log.d(RESUME_AUDIO_TAG, "trying to resume audio")
binding.messagesListView.scrollToPosition(voiceMessagePosition)
// WORKAROUND TO FETCH FILE INFO:
currentlyPlayedVoiceMessage!!.getImageUrl()
// see getImageUrl() source code
setUpWaveform(currentlyPlayedVoiceMessage!!, voiceMessageToRestoreWasPlaying)
Log.d(RESUME_AUDIO_TAG, "resume audio procedure completed")
} else {
Log.d(RESUME_AUDIO_TAG, "currentItem retrieved was not chatmessage or its id was not correct")
}
} else {
Log.d(
RESUME_AUDIO_TAG,
"voiceMessagePosition is -1, adapter # of items: " + adapter!!.getItemCount()
)
}
} else {
Log.d(RESUME_AUDIO_TAG, "TalkMessagesListAdapater is null")
}
} else {
Log.d(RESUME_AUDIO_TAG, "No voice message to restore")
}
voiceMessageToRestoreId = ""
voiceMessageToRestoreAudioPosition = 0
voiceMessageToRestoreWasPlaying = false
}
private fun scrollToRequestedMessageIfNeeded() { private fun scrollToRequestedMessageIfNeeded() {
intent.getStringExtra(BundleKeys.KEY_MESSAGE_ID)?.let { intent.getStringExtra(BundleKeys.KEY_MESSAGE_ID)?.let {
scrollToMessageWithId(it) scrollToMessageWithId(it)
@ -4380,7 +4479,7 @@ class ChatActivity :
val lon = data["longitude"]!! val lon = data["longitude"]!!
metaData = metaData =
"{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," + "{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," +
"\"longitude\":\"$lon\",\"name\":\"$name\"}" "\"longitude\":\"$lon\",\"name\":\"$name\"}"
} }
when (type) { when (type) {
@ -4861,5 +4960,9 @@ class ChatActivity :
private const val MILISEC_15: Long = 15 private const val MILISEC_15: Long = 15
private const val LINEBREAK = "\n" private const val LINEBREAK = "\n"
private const val CURSOR_KEY = "_cursor" private const val CURSOR_KEY = "_cursor"
private const val CURRENT_AUDIO_MESSAGE_KEY = "CURRENT_AUDIO_MESSAGE"
private const val CURRENT_AUDIO_POSITION_KEY = "CURRENT_AUDIO_POSITION"
private const val CURRENT_AUDIO_WAS_PLAYING_KEY = "CURRENT_AUDIO_PLAYING"
private const val RESUME_AUDIO_TAG = "RESUME_AUDIO_TAG"
} }
} }