Improved calls' recording player

This commit is contained in:
Sylvain Berfini 2020-06-29 15:28:44 +02:00
parent 785ae1ad4e
commit 0a33b33888
4 changed files with 66 additions and 30 deletions

View file

@ -20,7 +20,6 @@
package org.linphone.activities.main.recordings.adapters
import android.content.Context
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -73,23 +72,6 @@ class RecordingsListAdapter(val selectionViewModel: ListTopBarViewModel) : Lifec
}
}
recording.playStartedEvent.observe(this@ViewHolder, Observer {
it.consume { playing ->
recordCurrentTime.base = SystemClock.elapsedRealtime()
if (playing) {
recordCurrentTime.start()
} else {
recordCurrentTime.stop()
recordProgressionBar.progress = 0
}
}
})
recordCurrentTime.setOnChronometerTickListener {
recordProgressionBar.progress = (SystemClock.elapsedRealtime() - it.base).toInt()
}
executePendingBindings()
}
}

View file

@ -21,16 +21,20 @@ package org.linphone.activities.main.recordings.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
import java.util.regex.Pattern
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.ticker
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.AudioDevice
import org.linphone.core.Player
import org.linphone.core.PlayerListener
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingViewModel> {
@ -54,14 +58,17 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
val formattedDate: String
get() = DateFormat.getTimeInstance(DateFormat.SHORT).format(date)
val playStartedEvent = MutableLiveData<Event<Boolean>>()
val position = MutableLiveData<Int>()
val formattedPosition = MutableLiveData<String>()
val isPlaying = MutableLiveData<Boolean>()
private val tickerChannel = ticker(1000, 1000)
private var player: Player
private val listener = PlayerListener {
Log.i("[Recording] End of file reached")
pause()
stop()
}
init {
@ -72,6 +79,9 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
}
isPlaying.value = false
position.value = 0
formattedPosition.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(0)
// Use speaker sound card to play recordings, otherwise use earpiece
// If none are available, default one will be used
var speakerCard: String? = null
@ -90,6 +100,7 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
}
override fun onCleared() {
tickerChannel.cancel()
if (!isClosed()) player.close()
player.removeListener(listener)
@ -101,21 +112,49 @@ class RecordingViewModel(val path: String) : ViewModel(), Comparable<RecordingVi
}
fun play() {
if (isClosed()) player.open(path)
seek(0)
if (isClosed()) {
player.open(path)
player.seek(0)
}
player.start()
playStartedEvent.value = Event(true)
isPlaying.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
for (tick in tickerChannel) {
if (player.state == Player.State.Playing) {
updatePosition()
}
}
}
}
}
fun pause() {
player.pause()
isPlaying.value = false
playStartedEvent.value = Event(false)
}
private fun seek(position: Int) {
if (!isClosed()) player.seek(position)
fun onProgressChanged(progress: Any) {
if (progress is Int) {
if (player.state == Player.State.Playing) {
pause()
}
player.seek(progress)
updatePosition()
}
}
private fun updatePosition() {
val progress = if (isClosed()) 0 else player.currentPosition
position.postValue(progress)
formattedPosition.postValue(SimpleDateFormat("mm:ss", Locale.getDefault()).format(progress)) // is already in milliseconds
}
private fun stop() {
pause()
player.seek(0)
updatePosition()
}
private fun isClosed(): Boolean {

View file

@ -31,6 +31,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.*
import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.databinding.*
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
@ -181,6 +182,19 @@ fun spinnerSetting(spinner: Spinner, selectedIndex: Int, listener: SettingListen
}
}
@BindingAdapter("onProgressChanged")
fun setListener(view: SeekBar, lambda: (Any) -> Unit) {
view.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) lambda(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) { }
override fun onStopTrackingTouch(seekBar: SeekBar?) { }
})
}
@BindingAdapter("entries")
fun setEntries(
viewGroup: ViewGroup,

View file

@ -88,8 +88,8 @@
android:layout_alignParentRight="true"
android:orientation="horizontal">
<Chronometer
android:id="@+id/record_current_time"
<TextView
android:text="@{viewModel.formattedPosition}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
@ -108,8 +108,9 @@
<SeekBar
android:id="@+id/record_progression_bar"
android:enabled="false"
onProgressChanged="@{(progress) -> viewModel.onProgressChanged(progress)}"
android:max="@{viewModel.duration}"
android:progress="@{viewModel.position}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"