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 a56be07a7..148205be7 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -11,6 +11,7 @@ * Copyright (C) 2021-2022 Marcel Hibbe * Copyright (C) 2017-2019 Mario Danic * Copyright (C) 2023 Ezhil Shanmugham + * Copyright (C) 2024 Giacomo Pacini * * 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 @@ -405,6 +406,9 @@ class ChatActivity : private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { + if (currentlyPlayedVoiceMessage != null) { + stopMediaPlayer(currentlyPlayedVoiceMessage!!) + } val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java) intent.putExtras(Bundle()) startActivity(intent) @@ -416,6 +420,9 @@ class ChatActivity : val typingParticipants = HashMap() var callStarted = false + private var voiceMessageToRestoreId = "" + private var voiceMessageToRestoreAudioPosition = 0 + private var voiceMessageToRestoreWasPlaying = false private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener { override fun onSwitchTo(token: String?) { @@ -487,6 +494,30 @@ class ChatActivity : onBackPressedDispatcher.addCallback(this, onBackPressedCallback) 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) { @@ -548,6 +579,20 @@ class ChatActivity : 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() { super.onStop() active = false @@ -558,9 +603,6 @@ class ChatActivity : if (mediaRecorderState == MediaRecorderState.RECORDING) { stopAudioRecording() } - if (currentlyPlayedVoiceMessage != null) { - stopMediaPlayer(currentlyPlayedVoiceMessage!!) - } val text = binding.messageInputView.messageInput.text.toString() val cursor = binding.messageInputView.messageInput.selectionStart 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 file = File(context.cacheDir, filename!!) if (file.exists() && message.voiceMessageFloatArray == null) { @@ -1363,11 +1405,11 @@ class ChatActivity : appPreferences.saveWaveFormForFile(filename, r.toTypedArray()) message.voiceMessageFloatArray = r withContext(Dispatchers.Main) { - startPlayback(message) + startPlayback(message, thenPlay) } } } 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) { // 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. @@ -2333,7 +2375,7 @@ class ChatActivity : initMediaPlayer(message) mediaPlayer?.let { - if (!it.isPlaying) { + if (!it.isPlaying && doPlay) { audioFocusRequest(true) { it.start() handleBecomingNoisyBroadcast(register = true) @@ -2365,7 +2407,11 @@ class ChatActivity : }) 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) } } @@ -2408,6 +2454,8 @@ class ChatActivity : lastRecordedSeeked = true } } + // this ensures that audio can be resumed at a given position + this.seekTo(lastRecordMediaPosition) } setOnCompletionListener { stopMediaPlayer(message) @@ -2426,6 +2474,7 @@ class ChatActivity : adapter?.update(message) currentlyPlayedVoiceMessage = null + lastRecordMediaPosition = 0 //this ensures that if audio track is changed, then it is played from the beginning mediaPlayerHandler.removeCallbacksAndMessages(null) @@ -3750,6 +3799,8 @@ class ChatActivity : adapter?.addToEnd(chatMessageList, false) } scrollToRequestedMessageIfNeeded() + //FENOM: add here audio resume policy + resumeAudioPlaybackIfNeeded() } 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() { intent.getStringExtra(BundleKeys.KEY_MESSAGE_ID)?.let { scrollToMessageWithId(it) @@ -4380,7 +4479,7 @@ class ChatActivity : val lon = data["longitude"]!! metaData = "{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," + - "\"longitude\":\"$lon\",\"name\":\"$name\"}" + "\"longitude\":\"$lon\",\"name\":\"$name\"}" } when (type) { @@ -4861,5 +4960,9 @@ class ChatActivity : private const val MILISEC_15: Long = 15 private const val LINEBREAK = "\n" 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" } }