mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-21 12:35:30 +03:00
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:
commit
007c407549
1 changed files with 113 additions and 10 deletions
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue