Skip to content

Commit

Permalink
WIP replicating functionality 13 - Seekbar is functional - still bugg…
Browse files Browse the repository at this point in the history
…y though

Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
  • Loading branch information
rapterjet2004 committed Apr 16, 2024
1 parent 8c32597 commit 32a78cf
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.nextcloud.talk.chat

import android.animation.ObjectAnimator
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
Expand Down Expand Up @@ -31,7 +30,6 @@ class MessageInputVoiceRecordingFragment : Fragment() {

lateinit var binding: FragmentMessageInputVoiceRecordingBinding
private lateinit var chatActivity: ChatActivity
private var voicePreviewObjectAnimator: ObjectAnimator? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -62,13 +60,17 @@ class MessageInputVoiceRecordingFragment : Fragment() {
chatActivity.messageInputViewModel.micInputAudioObserver.observe(viewLifecycleOwner) {
binding.micInputCloud.setRotationSpeed(it.first, it.second)
}
chatActivity.messageInputViewModel.mediaPlayerSeekbarObserver.observe(viewLifecycleOwner) {
binding.seekbar.progress = it
}
chatActivity.messageInputViewModel.isPlayerFinished.observe(viewLifecycleOwner) {
if (it) {
binding.playPauseBtn.icon = ContextCompat.getDrawable(
requireContext(),
R.drawable.ic_baseline_play_arrow_voice_message_24
)
}
// TODO set up auto replay
// if (it) {
// binding.playPauseBtn.icon = ContextCompat.getDrawable(
// requireContext(),
// R.drawable.ic_baseline_play_arrow_voice_message_24
// )
// }
}
}

Expand Down Expand Up @@ -101,7 +103,10 @@ class MessageInputVoiceRecordingFragment : Fragment() {

binding.seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
chatActivity.messageInputViewModel.seekMediaPlayerTo(progress)
// FIXME this causes infinite loop for some weird reason
if (fromUser) {
chatActivity.messageInputViewModel.seekMediaPlayerTo(progress)
}
}

override fun onStartTrackingTouch(seekBar: SeekBar?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import android.os.Build
import android.util.Log
import androidx.lifecycle.MutableLiveData
import com.nextcloud.talk.chat.ChatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
* Abstraction over the [MediaPlayer](https://developer.android.com/reference/android/media/MediaPlayer) class used
Expand All @@ -21,18 +27,25 @@ class MediaPlayerManager : LifecycleAwareManager {
companion object {
val TAG: String = MediaPlayerManager::class.java.simpleName
private const val ONE_SECOND_IN_MILLIS = 1000L
private const val DIVIDER = 100f
private const val SEEKBAR_UPDATE_DELAY = 15L
const val DIVIDER = 100f
}

private var mediaPlayer: MediaPlayer? = null
private var mediaPlayerPosition: Int = 0
private var loop = false
private var scope = MainScope()
var mediaPlayerDuration: Int = 0

/**
* Starts playing audio from the given path, initializes or resumes if the player is already created.
*/
fun start(path: String, callback: MutableLiveData<Boolean>) {
init(path, callback)
fun start(
path: String,
completionObserver: MutableLiveData<Boolean>,
seekbarObserver: MutableLiveData<Int>
) {
init(path, completionObserver, seekbarObserver)
}

/**
Expand All @@ -41,6 +54,7 @@ class MediaPlayerManager : LifecycleAwareManager {
fun stop() {
if (mediaPlayer != null) {
Log.d("Julius", "media player destroyed")
loop = false
mediaPlayer!!.stop()
mediaPlayer!!.release()
mediaPlayer = null
Expand All @@ -54,19 +68,41 @@ class MediaPlayerManager : LifecycleAwareManager {
if (mediaPlayer != null) {
val pos = mediaPlayer!!.duration * (progress / DIVIDER)
mediaPlayer!!.seekTo(pos.toInt())
mediaPlayerPosition = progress
mediaPlayerPosition = pos.toInt()
}
}

private suspend fun seekbarUpdateObserver(callback: MutableLiveData<Int>) = withContext(Dispatchers.IO) {
while (true) {
if (!loop) {
return@withContext
}
if (mediaPlayer != null && mediaPlayer!!.isPlaying) { // TODO include check for if user is seeking
val pos = mediaPlayer!!.currentPosition
val progress = (pos.toFloat() / mediaPlayerDuration) * DIVIDER
Log.d("SeekbarUpdate", "progress: $progress")
callback.postValue(progress.toInt())
}

delay(SEEKBAR_UPDATE_DELAY)
}
}

@Suppress("Detekt.TooGenericExceptionCaught")
private fun init(path: String, callback: MutableLiveData<Boolean>) {
private fun init(
path: String,
completionObserver: MutableLiveData<Boolean>,
seekbarObserver: MutableLiveData<Int>
) {
try {
mediaPlayer = MediaPlayer().apply {
setDataSource(path)
prepareAsync()
setOnPreparedListener {
mediaPlayerDuration = it.duration
start()
loop = true
scope.launch { seekbarUpdateObserver(seekbarObserver) }
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setOnMediaTimeDiscontinuityListener { mp, _ ->
Expand All @@ -78,7 +114,7 @@ class MediaPlayerManager : LifecycleAwareManager {
this.seekTo(mediaPlayerPosition)
}
setOnCompletionListener {
callback.postValue(true)
completionObserver.postValue(true)
}
}
} catch (e: Exception) {
Expand All @@ -96,5 +132,6 @@ class MediaPlayerManager : LifecycleAwareManager {

override fun handleOnStop() {
stop()
scope.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class MessageInputViewModel @Inject constructor(
val micInputAudioObserver: LiveData<Pair<Float, Float>>
get() = _micInputAudioObserver

private val _mediaPlayerSeekbarObserver: MutableLiveData<Int> = MutableLiveData()
val mediaPlayerSeekbarObserver: LiveData<Int>
get() = _mediaPlayerSeekbarObserver

private val _getEditChatMessage: MutableLiveData<IMessage> = MutableLiveData()
val getEditChatMessage: LiveData<IMessage>
get() = _getEditChatMessage
Expand Down Expand Up @@ -184,7 +188,7 @@ class MessageInputViewModel @Inject constructor(

fun startMediaPlayer(path: String) {
_isPlayerFinished.value = false
mediaPlayerManager.start(path, _isPlayerFinished)
mediaPlayerManager.start(path, _isPlayerFinished, _mediaPlayerSeekbarObserver)
_isVoicePreviewPlaying.postValue(true)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,7 @@ abstract class ViewModelModule {
@ViewModelKey(MessageInputViewModel::class)
abstract fun messageInputViewModel(viewModel: MessageInputViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(CallNotificationViewModel::class)
abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel
// TODO I had a merge conflict here that went weird. choose their version

@Binds
@IntoMap
Expand Down
6 changes: 0 additions & 6 deletions app/src/main/res/layout/activity_chat.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,6 @@

</RelativeLayout>

<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_message_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="com.nextcloud.talk.chat.MessageInputFragment" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down

0 comments on commit 32a78cf

Please sign in to comment.