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" />