diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java index c5fc696b9e..595d2abd0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java @@ -147,8 +147,8 @@ public class VoiceNotePlaybackService extends MediaSessionService { @Override public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) { - int currentWindowIndex = newPosition.windowIndex; - if (currentWindowIndex == C.INDEX_UNSET) { + int mediaItemIndex = newPosition.mediaItemIndex; + if (mediaItemIndex == C.INDEX_UNSET) { return; } @@ -159,7 +159,7 @@ public class VoiceNotePlaybackService extends MediaSessionService { Log.d(TAG, "onPositionDiscontinuity: current window uri: " + currentMediaItem.playbackProperties.uri); } - PlaybackParameters playbackParameters = getPlaybackParametersForWindowPosition(currentWindowIndex); + PlaybackParameters playbackParameters = getPlaybackParametersForWindowPosition(mediaItemIndex); final float speed = playbackParameters != null ? playbackParameters.speed : 1f; if (speed != player.getPlaybackParameters().speed) { @@ -167,15 +167,16 @@ public class VoiceNotePlaybackService extends MediaSessionService { if (playbackParameters != null) { player.setPlaybackParameters(playbackParameters); } - player.seekTo(currentWindowIndex, 1); + player.seekTo(mediaItemIndex, 1); player.setPlayWhenReady(true); } } - boolean isWithinThreshold = currentWindowIndex < LOAD_MORE_THRESHOLD || - currentWindowIndex + LOAD_MORE_THRESHOLD >= player.getMediaItemCount(); - if (isWithinThreshold && currentWindowIndex % 2 == 0) { + boolean isWithinThreshold = mediaItemIndex < LOAD_MORE_THRESHOLD || + mediaItemIndex + LOAD_MORE_THRESHOLD >= player.getMediaItemCount(); + + if (isWithinThreshold && mediaItemIndex % 2 == 0) { voiceNotePlayerCallback.loadMoreVoiceNotes(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt index 203fb41269..e7dc6270a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt @@ -17,7 +17,6 @@ import androidx.media3.common.AudioAttributes import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem.LocalConfiguration -import androidx.media3.common.MediaMetadata import androidx.media3.common.PlaybackParameters import androidx.media3.common.Player import androidx.media3.common.Timeline @@ -36,12 +35,9 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.database.NoSuchMessageException import org.thoughtcrime.securesms.database.SignalDatabase.Companion.messages import org.thoughtcrime.securesms.database.model.MessageRecord -import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.hasAudio -import java.util.Objects import java.util.concurrent.Executor import java.util.concurrent.Executors -import java.util.stream.Collectors import kotlin.math.max /** @@ -146,7 +142,7 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer) ) { mediaItems: List -> player.clearMediaItems() if (mediaItems.isNotEmpty() && latestUri == uri) { - applyDescriptionsToQueue(mediaItems) + addItemsToPlaylist(mediaItems) val window = max(0, indexOfPlayerMediaItemByUri(uri)) player.addListener(object : Player.Listener { override fun onTimelineChanged(timeline: Timeline, reason: Int) { @@ -206,40 +202,14 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer) } @MainThread - private fun applyDescriptionsToQueue(mediaItems: List) { - for (mediaItem in mediaItems) { - val playbackProperties = mediaItem.localConfiguration ?: continue - val holderIndex = indexOfPlayerMediaItemByUri(playbackProperties.uri) - val next = VoiceNoteMediaItemFactory.buildNextVoiceNoteMediaItem(mediaItem) - val currentIndex: Int = player.currentMediaItemIndex - if (holderIndex != -1) { - if (currentIndex != holderIndex) { - player.removeMediaItem(holderIndex) - player.addMediaItem(holderIndex, mediaItem) - } - if (currentIndex != holderIndex + 1) { - if (player.mediaItemCount > 1) { - player.removeMediaItem(holderIndex + 1) - } - player.addMediaItem(holderIndex + 1, next) - } - } else { - val insertLocation = indexAfter(mediaItem) - player.addMediaItem(insertLocation, next) - player.addMediaItem(insertLocation, mediaItem) - } - } - val itemsCount: Int = player.mediaItemCount - if (itemsCount > 0) { - val lastIndex = itemsCount - 1 - val last: MediaItem = player.getMediaItemAt(lastIndex) - if (last.localConfiguration?.uri == VoiceNoteMediaItemFactory.NEXT_URI) { - player.removeMediaItem(lastIndex) - if (player.mediaItemCount > 1) { - val end = VoiceNoteMediaItemFactory.buildEndVoiceNoteMediaItem(last) - player.addMediaItem(lastIndex, end) - } - } + private fun addItemsToPlaylist(mediaItems: List) { + var mediaItemsWithNextTone = mediaItems.flatMap { listOf(it, VoiceNoteMediaItemFactory.buildNextVoiceNoteMediaItem(it)) }.toMutableList() + mediaItemsWithNextTone = mediaItemsWithNextTone.subList(0, mediaItemsWithNextTone.size - 1).toMutableList() + if (player.mediaItemCount == 0) { + mediaItemsWithNextTone += VoiceNoteMediaItemFactory.buildEndVoiceNoteMediaItem(mediaItemsWithNextTone.last()) + player.addMediaItems(mediaItemsWithNextTone) + } else { + player.addMediaItems(player.mediaItemCount, mediaItemsWithNextTone) } } @@ -253,69 +223,47 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer) return -1 } - private fun indexAfter(target: MediaItem): Int { - val size: Int = player.mediaItemCount - val targetMessageId = target.mediaMetadata.extras?.getLong(VoiceNoteMediaItemFactory.EXTRA_MESSAGE_ID) ?: 0L - for (i in 0 until size) { - val mediaMetadata: MediaMetadata = player.getMediaItemAt(i).mediaMetadata - val messageId = mediaMetadata.extras!!.getLong(VoiceNoteMediaItemFactory.EXTRA_MESSAGE_ID) - if (messageId > targetMessageId) { - return i - } - } - return size - } - fun loadMoreVoiceNotes() { if (!canLoadMore) { return } val currentMediaItem: MediaItem = player.currentMediaItem ?: return val messageId = currentMediaItem.mediaMetadata.extras!!.getLong(VoiceNoteMediaItemFactory.EXTRA_MESSAGE_ID) + val currentPlaylist = List(player.mediaItemCount) { index -> player.getMediaItemAt(index) }.mapNotNull { it.requestMetadata.mediaUri } SimpleTask.run( EXECUTOR, - { loadMediaItemsForConsecutivePlayback(messageId) } + { loadMediaItemsForConsecutivePlayback(messageId).filterNot { it.requestMetadata.mediaUri in currentPlaylist } } ) { mediaItems: List -> - if (Util.hasItems(mediaItems) && canLoadMore) { - applyDescriptionsToQueue(mediaItems) + if (mediaItems.isNotEmpty() && canLoadMore) { + addItemsToPlaylist(mediaItems) } } } - private fun loadMediaItemsForSinglePlayback(messageId: Long): List { - return try { - val messageRecord = messages - .getMessageRecord(messageId) - if (!messageRecord.hasAudio()) { - Log.w(TAG, "Message does not contain audio.") - return emptyList() - } - val mediaItem = VoiceNoteMediaItemFactory.buildMediaItem(context, messageRecord) - mediaItem?.let { listOf(it) } ?: emptyList() - } catch (e: NoSuchMessageException) { - Log.w(TAG, "Could not find message.", e) - emptyList() - } - } - private fun loadMediaItemsForDraftPlayback(threadId: Long, draftUri: Uri): List { return listOf(VoiceNoteMediaItemFactory.buildMediaItem(context, threadId, draftUri)) } - @WorkerThread - private fun loadMediaItemsForConsecutivePlayback(messageId: Long): List { + private fun loadMediaItemsForSinglePlayback(messageId: Long): List { return try { - val recordsAfter = messages.getMessagesAfterVoiceNoteInclusive(messageId, LIMIT) - recordsAfter.filter { it.hasAudio() }.stream() - .map { record: MessageRecord? -> - VoiceNoteMediaItemFactory - .buildMediaItem(context, record!!) - } - .filter { obj: MediaItem? -> Objects.nonNull(obj) } - .collect(Collectors.toList()) + listOf(messages.getMessageRecord(messageId)).messageRecordsToVoiceNoteMediaItems() } catch (e: NoSuchMessageException) { Log.w(TAG, "Could not find message.", e) emptyList() } } + + @WorkerThread + private fun loadMediaItemsForConsecutivePlayback(messageId: Long): List { + return try { + messages.getMessagesAfterVoiceNoteInclusive(messageId, LIMIT).messageRecordsToVoiceNoteMediaItems() + } catch (e: NoSuchMessageException) { + Log.w(TAG, "Could not find message.", e) + emptyList() + } + } + + private fun List.messageRecordsToVoiceNoteMediaItems(): List { + return this.filter { it.hasAudio() }.mapNotNull { VoiceNoteMediaItemFactory.buildMediaItem(context, it) } + } }