Home > front end >  Android Studio, MediaPlayer fast forward/rewind not working properly
Android Studio, MediaPlayer fast forward/rewind not working properly

Time:09-30

I'm trying to create fast forward and rewind buttons in my music player app. Currently when I tap the fast forward button, it seeks to the same position in the song no matter where the current position is. Rewind just restarts the song at 0.

It seems to me that SONG_POSITION is always 0 here. seekForward and seekBackward = 5000ms. Therefore 0 seekForward always == 5000ms, which moves the song position to 5000ms no matter the current position. And 0 - seekBackward always == -5000ms, -5000ms < duration, so it starts the song at 0.

FastForward:

fastForwardBtn!!.setOnClickListener {
            refreshRecyclerView(false)
            SONG_POSITION = getPosition(currentSong)

            if (SONG_POSITION   seekForward <= mMediaPlayer!!.duration) {
                mMediaPlayer!!.seekTo(SONG_POSITION   seekForward)
            } else {
                mMediaPlayer!!.seekTo(mMediaPlayer!!.duration)
            }
        }

Rewind:

rewindBtn!!.setOnClickListener {
            refreshRecyclerView(false)
            SONG_POSITION = getPosition(currentSong)

            if (SONG_POSITION - seekBackward >= mMediaPlayer!!.duration) {
                mMediaPlayer!!.seekTo(SONG_POSITION - seekBackward)
            } else {
                mMediaPlayer!!.seekTo(0)
            }
        }

Full MainActivity, fwd/rwnd are in handleEvents() about half way down:

package com.duncan.repea.ui.view

import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.Typeface
import android.media.MediaPlayer
import android.media.PlaybackParams
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.view.View
import android.view.WindowManager
import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.duncan.repea.data.model.Song
import com.duncan.repea.data.model.SongDao
import com.duncan.repea.databinding.ModelBinding
import com.duncan.repea.ui.viewmodel.SongsViewModel
import com.duncan.repea.utilities.constants.Constants
import com.duncan.repea.utilities.constants.Constants.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
import com.duncan.repea.R
import info.camposha.pollux.PolluxAdapter
import kotlinx.android.synthetic.main.content_home.*
import java.util.ArrayList

class MainActivity : AppCompatActivity() {

    private var hasFinished = false
    private var currentSong: Song? = null
    var mMediaPlayer: MediaPlayer? = null
    var mHandler = Handler()
    var adapter: PolluxAdapter<Song>? = null
    private var sv: SongsViewModel? = null


    var SONG_POSITION = 0
    var seekForward = 5000
    var seekBackward = 5000
    var SONGS_CACHE = ArrayList<Song>()
    var playbackParams = PlaybackParams()

    private fun initializeViews() {
        songsRV!!.layoutManager = LinearLayoutManager(this)
        progressSPD.isEnabled = false

    }

    private fun getPosition(s: Song?): Int {
        val pos = 0
        for (song in SONGS_CACHE) {
            if (s!!.id.equals(song.id, ignoreCase = true)) {
                return SONGS_CACHE.indexOf(s)
            }
        }
        return pos
    }

    private val player: MediaPlayer?
        private get() {
            if (mMediaPlayer == null) {
                if (currentSong == null) {
                    currentSong = if (SONGS_CACHE.size > 0) {
                        SONGS_CACHE[0]
                    } else {
                        return null
                    }
                }
                mMediaPlayer = MediaPlayer.create(this, Uri.parse(currentSong!!.data))
            }
            return mMediaPlayer
        }

    private fun cleanUpMediaPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer!!.release()
            mMediaPlayer = null
        }

    }

    private fun fetchAllSongs() {
        sv!!.loadAllSongs(this).observe(this, Observer { requestCall: SongDao ->
            val linkedHashSet = LinkedHashSet(requestCall.songs)
            SONGS_CACHE.clear()
            SONGS_CACHE.addAll(linkedHashSet)
            SONGS_CACHE.sortWith(compareBy({ it.title }))
            if (currentSong == null && SONGS_CACHE.size > 0) {
                currentSong = SONGS_CACHE.get(0)
            }
        })
    }

    private fun checkPermissionsThenLoadSongs() {
        // Here, thisActivity is the current activity
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED
        ) {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(
                this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
            )
            show("Please allow external storage access")
        } else {
            fetchAllSongs()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>,
        grantedResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantedResults)
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE -> {
                if (grantedResults.size > 0
                    && grantedResults[0] == PackageManager.PERMISSION_GRANTED
                ) {
                    fetchAllSongs()
                    // permission was granted, yay! Do the
                    // SONGS related task you need to do.
                } else {
                    show("Please allow external storage access")
                }
                return
            }
        }
    }

    private fun playOrPause(song: Song) {
        currentSong = song
        if (player == null) {
            show("Please add songs")
            return
        }
        hasFinished = false
        try {
            songsRV.smoothScrollToPosition(getPosition(song))
        } catch (e: Exception) {

        }
        if (player!!.isPlaying) {
            sv!!.pause(player, this, song).observe(this, Observer { requestCall: SongDao ->
                if (requestCall.status == Constants.STOPPED) {
                    progressSPD.isEnabled = false
                    song.isPlaying = false
                    refreshRecyclerView(song.isPlaying)
                    playBtn!!.setImageResource(R.drawable.ic_play)
                    waveLineView.stopAnim()
                }
            })
        } else {
            sv!!.play(player, this, song).observe(this, Observer { requestCall: SongDao ->
                if (requestCall.status == Constants.PLAYING) {
                    progressSPD.isEnabled = true
                    song.isPlaying = true
                    SONG_POSITION = getPosition(song)
                    refreshRecyclerView(song.isPlaying)
                    playBtn!!.setImageResource(R.drawable.ic_pause)
                    waveLineView.startAnim()
                    updateSongProgress()
                }
            })
        }
    }

    private fun handleEvents() {
        playBtn!!.setOnClickListener {
            if (currentSong != null) {
                playOrPause(currentSong!!)
            } else {
                show("Please add songs")
            }
        }
        nextBtn!!.setOnClickListener {
            refreshRecyclerView(false)
            SONG_POSITION = getPosition(currentSong)   1
            if (SONG_POSITION >= SONGS_CACHE.size) {
                SONG_POSITION = 0
            }
            cleanUpMediaPlayer()

            //keeps speedSeek at 100 when skipping
            findViewById<TextView>(R.id.speedPercentage).text = "100"
            progressSPD.progress = 100

            val nextSong: Song = SONGS_CACHE.get(SONG_POSITION)
            playOrPause(nextSong)
        }

        fastForwardBtn!!.setOnClickListener {
            refreshRecyclerView(false)
            SONG_POSITION = getPosition(currentSong)

            if (SONG_POSITION   seekForward <= mMediaPlayer!!.duration) {
                mMediaPlayer!!.seekTo(SONG_POSITION   seekForward)
            } else {
                mMediaPlayer!!.seekTo(mMediaPlayer!!.duration)
            }
        }

        prevBtn!!.setOnClickListener {
            refreshRecyclerView(false)
            SONG_POSITION--
            if (SONG_POSITION < 0) {
                if (SONGS_CACHE.size > 0) {
                    SONG_POSITION = SONGS_CACHE.size - 1
                } else {
                    SONG_POSITION = 0
                }
            }

            //keeps speedSeek at 100 when skipping
            findViewById<TextView>(R.id.speedPercentage).text = "100"
            progressSPD.progress = 100

            val prevSong: Song = SONGS_CACHE[SONG_POSITION]
            cleanUpMediaPlayer()
            playOrPause(prevSong)
        }

        rewindBtn!!.setOnClickListener {
            refreshRecyclerView(false)
            SONG_POSITION = getPosition(currentSong)

            if (SONG_POSITION - seekBackward >= mMediaPlayer!!.duration) {
                mMediaPlayer!!.seekTo(SONG_POSITION - seekBackward)
            } else {
                mMediaPlayer!!.seekTo(0)
            }
        }

        progressSB!!.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
                if (fromUser) {
                    if (player == null) {
                        show("Please add songs")
                        return
                    }
                    mMediaPlayer!!.seekTo(
                        (sv!!.getTimeFromProgress(
                            seekBar.progress,
                            player!!.duration
                        ))
                    )
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}
            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })
        progressSPD!!.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
                if (fromUser) {
                    var speedPercentage = findViewById<TextView>(R.id.speedPercentage)
                    var progString = progress.toString()
                    speedPercentage.text = progString   "%"
                    var speed = (progress.toFloat()) / 100
                    mMediaPlayer!!.playbackParams = mMediaPlayer!!.playbackParams!!.setSpeed(speed)
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}
            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })
    }
    
    private fun refreshRecyclerView(playing: Boolean) {
        if (currentSong != null && SONGS_CACHE.size > 0) {
            currentSong!!.isPlaying = playing
            val s = SONGS_CACHE.find { it.id == currentSong!!.id }
            if (s != null) {
                SONGS_CACHE[getPosition(s)] = currentSong!!
            }
            adapter!!.notifyDataSetChanged()
            currentSongTV.text = currentSong!!.title
            currentSongTV.marqueeRepeatLimit = -1
        } else {
            currentSongTV.text = "Repea"
        }
    }


    //toast
    private fun show(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }

    private fun setupRecycler(songs: ArrayList<Song>) {
        adapter =
            PolluxAdapter.with<Song, ModelBinding>(R.layout.model) { adapterPosition, song, mb ->
                //val color = mMaterialColors[Random().nextInt(mMaterialColors.size)]
                //binding.root.setBackgroundColor(color)
                mb.titleTV.text = song.title
                if (song.isPlaying && !hasFinished) {
                    mb.playBtn.setImageResource(R.drawable.ic_pause)
                    mb.titleTV.setTextColor(Color.rgb(203, 167, 255))
                    mb.titleTV.setTypeface(null, Typeface.BOLD_ITALIC)
                } else {
                    mb.playBtn.setImageResource(R.drawable.ic_play)
                    mb.titleTV.setTextColor(Color.GRAY)
                    mb.titleTV.setTypeface(null, Typeface.NORMAL)
                    if (hasFinished) {
                        mb.playBtn.setImageResource(R.drawable.ic_play)
                        mb.titleTV.setTextColor(Color.GRAY)
                        mb.titleTV.setTypeface(null, Typeface.NORMAL)
                    } else {
                        if (currentSong != null && currentSong!!.id === song.id) {
                            mb.playBtn.setImageResource(R.drawable.ic_play)
                            mb.titleTV.setTextColor(Color.rgb(203, 167, 255))
                            mb.titleTV.setTypeface(null, Typeface.NORMAL)
                        }
                    }
                }
                mb.playBtn.setOnClickListener { view: View? ->
                    if (!song.isPlaying) {
                        if (SONG_POSITION != getPosition(song)) {
                            if (mMediaPlayer != null) {
                                mMediaPlayer!!.release()
                                mMediaPlayer = null
                            }
                        }
                        SONG_POSITION = getPosition(song)
                        if (currentSong != null) {
                            currentSong!!.isPlaying = false
                            adapter!!.notifyDataSetChanged()
                        }
                        currentSong = SONGS_CACHE.get(SONG_POSITION)
                        playOrPause(song)
                        show("Now Playing: "   song.title)
                        mb.playBtn.setImageResource(android.R.drawable.ic_media_pause)
                        mb.titleTV.setTextColor(Color.GREEN)
                        mb.titleTV.setTypeface(null, Typeface.ITALIC)

                    } else {
                        if (SONG_POSITION != getPosition(song)) {
                            if (mMediaPlayer != null) {
                                mMediaPlayer!!.release()
                                mMediaPlayer = null
                            }
                        }
                        SONG_POSITION = getPosition(song)
                        show("Stopped: "   song.title)
                        playOrPause(song)
                        mb.playBtn.setImageResource(android.R.drawable.ic_media_play)
                        mb.titleTV.setTextColor(Color.RED)
                        mb.titleTV.setTypeface(null, Typeface.ITALIC)
                        //adapter!!.notifyDataSetChanged()
                    }
                }
            }
        songsRV.layoutManager = GridLayoutManager(this@MainActivity, 2)
        adapter!!.addAll(songs)
        songsRV!!.adapter = adapter
    }


    private fun updateSongProgress() {
        mHandler.postDelayed(runnable, 1000)
    }

    private var runnable: Runnable = object : Runnable {
        override fun run() {
            if (!hasFinished) {
                if (mMediaPlayer == null) {
                    return
                }
                val currentDuration = mMediaPlayer!!.currentPosition
                val totalDuration = mMediaPlayer!!.duration
                currentPosTV!!.text = sv!!.convertToTimerMode(currentDuration.toString())
                progressSB!!.progress = sv!!.getSongProgress(totalDuration, currentDuration)
                totalDurationTV!!.text = sv!!.convertToTimerMode(totalDuration.toString())
                if (progressSB!!.progress >= 99 && !mMediaPlayer!!.isPlaying) {
                    playBtn!!.setImageResource(android.R.drawable.ic_media_play)
                    hasFinished = true
                    adapter!!.notifyDataSetChanged()
                    //                    show("Finished");
                    //Click Next to play next song
                    nextBtn!!.performClick()
                } else {
                    hasFinished = false
                }
                mHandler.postDelayed(this, 1000)
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //keeps screen from locking
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

        sv = ViewModelProvider(this).get(SongsViewModel::class.java)
        initializeViews()
        handleEvents()
    }

    override fun onResume() {
        super.onResume()
        if (SONGS_CACHE.isEmpty()) {
            checkPermissionsThenLoadSongs()
            setupRecycler(SONGS_CACHE)
        }
    }

    override fun onPause() {
        super.onPause()
        if (currentSong != null && currentSong!!.isPlaying) {
            currentSong!!.isPlaying = false
            playOrPause(currentSong!!)
        }
        cleanUpMediaPlayer()
    }

    override fun onStop() {
        super.onStop()
        SONGS_CACHE.clear()
        cleanUpMediaPlayer()
    }

    override fun onDestroy() {
        super.onDestroy()
        waveLineView.release()
    }
}

Any help would be appreciated.

CodePudding user response:

I don't know why you don't use currentPosition() method to get the position of the currently playing song instead your method getPosition() Try to use this..

SONG_POSITION = mMediaPlayer!!.currentPosition

Take a look here to know more.. https://developer.android.com/reference/android/media/MediaPlayer#getCurrentPosition()

  • Related