From 61c79a86f7dc1078b155fde51aed820fea28b97f Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 5 Dec 2025 09:58:09 +0100 Subject: [PATCH] Added seek to recordings player & media player --- CHANGELOG.md | 1 + .../fragment/MediaViewerFragment.kt | 18 +++++++++++++++ .../ui/fileviewer/viewmodel/MediaViewModel.kt | 8 +++++++ .../fragment/RecordingMediaPlayerFragment.kt | 18 +++++++++++++++ .../RecordingMediaPlayerViewModel.kt | 20 +++++++++++++++++ .../org/linphone/utils/DataBindingUtils.kt | 6 +++++ .../res/drawable/media_player_seekbar.xml | 19 ++++++++++++++++ .../drawable/media_player_seekbar_thumb.xml | 10 +++++++++ .../file_media_viewer_child_fragment.xml | 22 +++++++++---------- .../res/layout/recording_player_fragment.xml | 22 +++++++++---------- 10 files changed, 122 insertions(+), 22 deletions(-) create mode 100644 app/src/main/res/drawable/media_player_seekbar.xml create mode 100644 app/src/main/res/drawable/media_player_seekbar_thumb.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 69119f8aa..5096dd44e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Group changes to describe their impact on the project, as follows: ### Added - Added the ability to edit/delete chat messages sent less than 24 hours ago. - Added keyboard shortcuts on IncomingCallFragment: Ctrl + Shift + A to answer the call, Ctrl + Shift + D to decline it +- Added seeking feature to recordings & media player within app - Added PDF preview in conversation (message bubble & documents list) - Added hover effect when using a mouse (useful for tablets or devices with desktop mode) - Support right click on some items to open bottom sheet/menu diff --git a/app/src/main/java/org/linphone/ui/fileviewer/fragment/MediaViewerFragment.kt b/app/src/main/java/org/linphone/ui/fileviewer/fragment/MediaViewerFragment.kt index d3e8b3dd4..7d278161f 100644 --- a/app/src/main/java/org/linphone/ui/fileviewer/fragment/MediaViewerFragment.kt +++ b/app/src/main/java/org/linphone/ui/fileviewer/fragment/MediaViewerFragment.kt @@ -26,6 +26,7 @@ import android.view.Surface import android.view.TextureView.SurfaceTextureListener import android.view.View import android.view.ViewGroup +import android.widget.SeekBar import androidx.annotation.UiThread import androidx.lifecycle.ViewModelProvider import org.linphone.core.tools.Log @@ -45,6 +46,21 @@ class MediaViewerFragment : GenericMainFragment() { private lateinit var viewModel: MediaViewModel + private val seekBarListener = object : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + viewModel.pause() + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + val newPosition = seekBar.progress + viewModel.seekTo(newPosition) + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -87,6 +103,8 @@ class MediaViewerFragment : GenericMainFragment() { sharedViewModel.mediaViewerFullScreenMode.value = fullScreenMode } + binding.setSeekBarListener(seekBarListener) + viewModel.videoSizeChangedEvent.observe(viewLifecycleOwner) { it.consume { pair -> val width = pair.first diff --git a/app/src/main/java/org/linphone/ui/fileviewer/viewmodel/MediaViewModel.kt b/app/src/main/java/org/linphone/ui/fileviewer/viewmodel/MediaViewModel.kt index 39f524b9d..9cbec6e52 100644 --- a/app/src/main/java/org/linphone/ui/fileviewer/viewmodel/MediaViewModel.kt +++ b/app/src/main/java/org/linphone/ui/fileviewer/viewmodel/MediaViewModel.kt @@ -161,6 +161,14 @@ class MediaViewModel } } + @UiThread + fun seekTo(position: Int) { + if (::mediaPlayer.isInitialized) { + mediaPlayer.seekTo(position) + play() + } + } + @UiThread private fun initMediaPlayer() { isMediaPlaying.value = false diff --git a/app/src/main/java/org/linphone/ui/main/recordings/fragment/RecordingMediaPlayerFragment.kt b/app/src/main/java/org/linphone/ui/main/recordings/fragment/RecordingMediaPlayerFragment.kt index 956940cb9..79ba55950 100644 --- a/app/src/main/java/org/linphone/ui/main/recordings/fragment/RecordingMediaPlayerFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/recordings/fragment/RecordingMediaPlayerFragment.kt @@ -27,6 +27,7 @@ import android.view.LayoutInflater import android.view.TextureView import android.view.View import android.view.ViewGroup +import android.widget.SeekBar import androidx.core.content.FileProvider import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -52,6 +53,21 @@ class RecordingMediaPlayerFragment : GenericMainFragment() { private lateinit var viewModel: RecordingMediaPlayerViewModel + private val seekBarListener = object : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + viewModel.pause() + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + val newPosition = seekBar.progress + viewModel.seekTo(newPosition) + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -84,6 +100,8 @@ class RecordingMediaPlayerFragment : GenericMainFragment() { exportFile(viewModel.recordingModel.filePath) } + binding.setSeekBarListener(seekBarListener) + val model = sharedViewModel.playingRecording if (model != null) { Log.i("$TAG Loading recording [${model.fileName}] from shared view model") diff --git a/app/src/main/java/org/linphone/ui/main/recordings/viewmodel/RecordingMediaPlayerViewModel.kt b/app/src/main/java/org/linphone/ui/main/recordings/viewmodel/RecordingMediaPlayerViewModel.kt index 2959a8764..36e7916c7 100644 --- a/app/src/main/java/org/linphone/ui/main/recordings/viewmodel/RecordingMediaPlayerViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/recordings/viewmodel/RecordingMediaPlayerViewModel.kt @@ -175,6 +175,14 @@ class RecordingMediaPlayerViewModel } } + @UiThread + fun seekTo(position: Int) { + coreContext.postOnCoreThread { + seekPlaybackTo(position) + startPlayback() + } + } + @WorkerThread private fun startPlayback() { if (!::player.isInitialized) return @@ -232,6 +240,18 @@ class RecordingMediaPlayerViewModel updatePositionJob = null } + @WorkerThread + private fun seekPlaybackTo(position: Int) { + if (!::player.isInitialized) return + + if (player.state == Player.State.Closed) { + player.open(recordingModel.filePath) + } + + Log.i("$TAG Seeking player to position [$position]") + player.seek(position) + } + @WorkerThread private fun stop() { if (!::player.isInitialized) return diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index d8adb9ef1..24134db9b 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -32,6 +32,7 @@ import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.EditText import android.widget.ImageView +import android.widget.SeekBar import androidx.annotation.ColorInt import androidx.annotation.ColorRes import androidx.annotation.DimenRes @@ -633,6 +634,11 @@ fun setFlexboxLayoutWrapBefore(view: View, wrap: Boolean = false) { view.layoutParams = params } +@BindingAdapter("seekBarListener") +fun setSeekBarListener(seekBar: SeekBar, listener: SeekBar.OnSeekBarChangeListener) { + seekBar.setOnSeekBarChangeListener(listener) +} + @BindingAdapter("emojiPickedListener") fun EmojiPickerView.setEmojiPickedListener(listener: EmojiPickedListener) { setOnEmojiPickedListener { emoji -> diff --git a/app/src/main/res/drawable/media_player_seekbar.xml b/app/src/main/res/drawable/media_player_seekbar.xml new file mode 100644 index 000000000..345aae3e1 --- /dev/null +++ b/app/src/main/res/drawable/media_player_seekbar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/media_player_seekbar_thumb.xml b/app/src/main/res/drawable/media_player_seekbar_thumb.xml new file mode 100644 index 000000000..22291800f --- /dev/null +++ b/app/src/main/res/drawable/media_player_seekbar_thumb.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file 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 d3274339e..4af9021f6 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 @@ -5,9 +5,13 @@ + + @@ -75,24 +79,20 @@ app:layout_constraintStart_toStartOf="parent" app:tint="@color/bc_white"/> - + app:layout_constraintEnd_toStartOf="@id/duration" /> + @@ -14,6 +15,9 @@ + @@ -62,24 +66,20 @@ app:layout_constraintStart_toStartOf="parent" app:tint="@color/bc_white"/> - + app:layout_constraintEnd_toStartOf="@id/duration" />