From 22254b784663f9570ae8ed2796244243621dd8f9 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 6 May 2024 14:34:41 +0200 Subject: [PATCH] Added very simple media player for audio files --- .../linphone/ui/main/chat/model/FileModel.kt | 1 + .../ui/main/chat/model/MessageModel.kt | 36 ++++++++++-- .../ConversationDocumentsListViewModel.kt | 3 +- .../ConversationMediaListViewModel.kt | 3 +- .../SendMessageInConversationViewModel.kt | 3 +- .../fragment/MediaViewerFragment.kt | 21 +++++-- .../viewmodel/MediaViewModel.kt | 57 +++++++++++++++++++ .../file_media_viewer_child_fragment.xml | 15 +++++ app/src/main/res/values/strings.xml | 1 + 9 files changed, 127 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt index 91d0a95f7..8c5970e30 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt @@ -38,6 +38,7 @@ class FileModel @AnyThread constructor( val file: String, val fileName: String, val fileSize: Long, + val fileCreationTimestamp: Long, private val isEncrypted: Boolean, val isWaitingToBeDownloaded: Boolean = false, private val onClicked: ((model: FileModel) -> Unit)? = null diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt index 226176041..13933211c 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt @@ -370,9 +370,16 @@ class MessageModel @WorkerThread constructor( ) val fileSize = content.fileSize.toLong() + val timestamp = -1L // TODO FIXME: use content.creationTimestamp when (content.type) { "image", "video" -> { - val fileModel = FileModel(path, name, fileSize, isFileEncrypted) { model -> + val fileModel = FileModel( + path, + name, + fileSize, + timestamp, + isFileEncrypted + ) { model -> onContentClicked?.invoke(model.file) } filesPath.add(fileModel) @@ -380,7 +387,13 @@ class MessageModel @WorkerThread constructor( displayableContentFound = true } else -> { - val fileModel = FileModel(path, name, fileSize, isFileEncrypted) { model -> + val fileModel = FileModel( + path, + name, + fileSize, + timestamp, + isFileEncrypted + ) { model -> onContentClicked?.invoke(model.file) } filesPath.add(fileModel) @@ -398,14 +411,29 @@ class MessageModel @WorkerThread constructor( allFilesDownloaded = false filesContentCount += 1 val name = content.name ?: "" + val timestamp = -1L // TODO FIXME: use content.creationTimestamp if (name.isNotEmpty()) { val fileModel = if (isOutgoing && chatMessage.isFileTransferInProgress) { val path = content.filePath ?: "" - FileModel(path, name, content.fileSize.toLong(), isFileEncrypted, false) { model -> + FileModel( + path, + name, + content.fileSize.toLong(), + timestamp, + isFileEncrypted, + false + ) { model -> onContentClicked?.invoke(model.file) } } else { - FileModel(name, name, content.fileSize.toLong(), isFileEncrypted, true) { model -> + FileModel( + name, + name, + content.fileSize.toLong(), + timestamp, + isFileEncrypted, + true + ) { model -> downloadContent(model, content) } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt index 8deafc42e..86ef4b601 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt @@ -72,8 +72,9 @@ class ConversationDocumentsListViewModel @UiThread constructor() : AbstractConve } val name = documentContent.name.orEmpty() val size = documentContent.size.toLong() + val timestamp = -1L // TODO FIXME: use documentContent.creationTimestamp if (path.isNotEmpty() && name.isNotEmpty()) { - val model = FileModel(path, name, size, isEncrypted) { + val model = FileModel(path, name, size, timestamp, isEncrypted) { openDocumentEvent.postValue(Event(it)) } list.add(model) diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt index 79002eb2f..2c348cfed 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt @@ -76,8 +76,9 @@ class ConversationMediaListViewModel @UiThread constructor() : AbstractConversat } val name = mediaContent.name.orEmpty() val size = mediaContent.size.toLong() + val timestamp = -1L // TODO FIXME: use mediaContent.creationTimestamp if (path.isNotEmpty() && name.isNotEmpty()) { - val model = FileModel(path, name, size, isEncrypted) { + val model = FileModel(path, name, size, timestamp, isEncrypted) { openMediaEvent.postValue(Event(it)) } list.add(model) diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt index a88ab8a7e..d696f6a02 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt @@ -324,7 +324,8 @@ class SendMessageInConversationViewModel @UiThread constructor() : ViewModel() { list.addAll(attachments.value.orEmpty()) val fileName = FileUtils.getNameFromFilePath(file) - val model = FileModel(file, fileName, 0, isEncrypted = false) { model -> + val timestamp = System.currentTimeMillis() / 1000 + val model = FileModel(file, fileName, 0, timestamp, isEncrypted = false) { model -> removeAttachment(model.file) } diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt b/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt index b1311d1b2..281ec3b4f 100644 --- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt @@ -77,12 +77,7 @@ class MediaViewerFragment : GenericFragment() { viewModel.isVideo.observe(viewLifecycleOwner) { isVideo -> if (isVideo) { - Log.i("$TAG Creating video player for file [$path]") - binding.videoPlayer.setVideoPath(path) - binding.videoPlayer.setOnCompletionListener { - Log.i("$TAG End of file reached") - viewModel.isVideoPlaying.value = false - } + initVideoPlayer(path) } } @@ -124,6 +119,11 @@ class MediaViewerFragment : GenericFragment() { viewModel.isVideoPlaying.value = false } + if (viewModel.isAudioPlaying.value == true) { + Log.i("$TAG Paused, stopping audio player") + viewModel.pauseAudio() + } + super.onPause() } @@ -132,4 +132,13 @@ class MediaViewerFragment : GenericFragment() { super.onDestroyView() } + + private fun initVideoPlayer(path: String) { + Log.i("$TAG Creating video player for file [$path]") + binding.videoPlayer.setVideoPath(path) + binding.videoPlayer.setOnCompletionListener { + Log.i("$TAG End of file reached") + viewModel.isVideoPlaying.value = false + } + } } diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt b/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt index e0cd6c3d7..e4e224ce8 100644 --- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt @@ -19,6 +19,8 @@ */ package org.linphone.ui.main.file_media_viewer.viewmodel +import android.media.AudioAttributes +import android.media.MediaPlayer import androidx.annotation.UiThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -45,12 +47,24 @@ class MediaViewModel @UiThread constructor() : ViewModel() { val isAudio = MutableLiveData() + val isAudioPlaying = MutableLiveData() + val toggleVideoPlayPauseEvent: MutableLiveData> by lazy { MutableLiveData>() } private lateinit var filePath: String + private lateinit var mediaPlayer: MediaPlayer + + override fun onCleared() { + if (::mediaPlayer.isInitialized) { + mediaPlayer.release() + } + + super.onCleared() + } + @UiThread fun loadFile(file: String) { filePath = file @@ -73,6 +87,8 @@ class MediaViewModel @UiThread constructor() : ViewModel() { FileUtils.MimeType.Audio -> { Log.i("$TAG File [$file] seems to be an audio file") isAudio.value = true + + initMediaPlayer() } else -> { } } @@ -89,4 +105,45 @@ class MediaViewModel @UiThread constructor() : ViewModel() { isVideoPlaying.value = playVideo toggleVideoPlayPauseEvent.value = Event(playVideo) } + + @UiThread + fun playPauseAudio() { + if (::mediaPlayer.isInitialized) { + if (mediaPlayer.isPlaying) { + mediaPlayer.pause() + isAudioPlaying.value = false + } else { + mediaPlayer.start() + isAudioPlaying.value = true + } + } + } + + @UiThread + fun pauseAudio() { + if (::mediaPlayer.isInitialized) { + mediaPlayer.pause() + } + } + + @UiThread + private fun initMediaPlayer() { + isAudioPlaying.value = false + mediaPlayer = MediaPlayer().apply { + setAudioAttributes( + AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).setUsage( + AudioAttributes.USAGE_MEDIA + ).build() + ) + setDataSource(filePath) + setOnCompletionListener { + Log.i("$TAG Media player reached the end of file") + isAudioPlaying.postValue(false) + } + prepare() + start() + isAudioPlaying.value = true + } + Log.i("$TAG Media player for file [$filePath] created") + } } diff --git a/app/src/main/res/layout/file_media_viewer_child_fragment.xml b/app/src/main/res/layout/file_media_viewer_child_fragment.xml index caff69b7d..3921a0af7 100644 --- a/app/src/main/res/layout/file_media_viewer_child_fragment.xml +++ b/app/src/main/res/layout/file_media_viewer_child_fragment.xml @@ -16,6 +16,21 @@ android:layout_height="match_parent" android:background="@color/black"> + + Participant is muted Participant is speaking Add participants + Plays/pauses the audio playback Plays/pauses the video playback Share file Save file