Skip to content

Instantly share code, notes, and snippets.

@AhmedBadrSayed
Created June 17, 2020 14:01
Show Gist options
  • Save AhmedBadrSayed/b90a58e21585e4cbf80f945018657012 to your computer and use it in GitHub Desktop.
Save AhmedBadrSayed/b90a58e21585e4cbf80f945018657012 to your computer and use it in GitHub Desktop.
package com.mondiamedia.ahmedbadr.audioplayerdemo
import android.content.ComponentName
import android.os.Bundle
import android.os.Handler
import android.os.SystemClock
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE
import android.support.v4.media.session.MediaControllerCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.text.format.DateUtils
import android.util.Log
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import com.mondiamedia.ahmedbadr.audioplayerdemo.musicserviceutils.MediaPlaybackService
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.content_main.*
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
private val PROGRESS_UPDATE_INTERNAL: Long = 1000
private val PROGRESS_UPDATE_INITIAL_INTERVAL: Long = 100
private lateinit var mMediaBrowserCompat: MediaBrowserCompat
private lateinit var mMediaController: MediaControllerCompat
private var mLastPlaybackState: PlaybackStateCompat? = null
private val mHandler = Handler()
private val mUpdateProgressTask = Runnable { updateProgress() }
private lateinit var mExecutorService: ScheduledExecutorService
private var mScheduleFuture: ScheduledFuture<*>? = null
val mMediaItemsList = ArrayList<MediaBrowserCompat.MediaItem>()
private val connectionCallback: MediaBrowserCompat.ConnectionCallback =
object : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
// The browser connected to the session successfully, use the token to create the controller
super.onConnected()
mMediaBrowserCompat.sessionToken.also { token ->
val mediaController = MediaControllerCompat(this@MainActivity, token)
MediaControllerCompat.setMediaController(this@MainActivity, mediaController)
}
playPauseBuild()
mMediaBrowserCompat.subscribe(
"media_root_id", mSubscriptionCallback
)
Log.d("onConnected", "Controller Connected")
}
override fun onConnectionFailed() {
super.onConnectionFailed()
Log.d("onConnectionFailed", "Connection Failed")
}
}
private val mControllerCallback = object : MediaControllerCompat.Callback() {
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
super.onPlaybackStateChanged(state)
Log.d("onPlaybackStateChanged ", "state ${state?.state}")
updatePlaybackState(state)
}
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
super.onMetadataChanged(metadata)
Log.d(
"onMetadataChanged ", "metadata changed : ${metadata?.description?.title
?: "No title"} then for max : ${
(metadata?.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)?.toInt()) ?: 2
}"
)
if (mMediaBrowserCompat.isConnected) {
track_title.text = metadata?.getString(METADATA_KEY_DISPLAY_TITLE)
updateDuration(metadata)
}
}
}
private val mSubscriptionCallback = object : MediaBrowserCompat.SubscriptionCallback() {
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowserCompat.MediaItem>
) {
super.onChildrenLoaded(parentId, children)
Log.d("MainActivity ", "onChildrenLoaded : $parentId //\\ ${children.size}")
mMediaItemsList.addAll(children)
// only take the playable children
}
}
fun playPauseBuild() {
mMediaController = MediaControllerCompat.getMediaController(this@MainActivity)
play_pause.setOnClickListener {
val state = mMediaController.playbackState.state
// if it is not playing then what are you waiting for ? PLAY !
if (state == PlaybackStateCompat.STATE_PAUSED ||
state == PlaybackStateCompat.STATE_STOPPED ||
state == PlaybackStateCompat.STATE_NONE
) {
mMediaController.transportControls.play()
play_pause.setImageDrawable(getDrawable(R.drawable.ic_pause_black_24dp))
}
// you are playing ? knock it off !
else if (state == PlaybackStateCompat.STATE_PLAYING ||
state == PlaybackStateCompat.STATE_BUFFERING ||
state == PlaybackStateCompat.STATE_CONNECTING
) {
mMediaController.transportControls.pause()
play_pause.setImageDrawable(getDrawable(R.drawable.ic_play_arrow_black_24dp))
}
}
next.setOnClickListener {
// mMediaController.transportControls.playFromMediaId(mMediaItemsList[1].mediaId, null)
mMediaController.transportControls.skipToNext()
}
prev.setOnClickListener {
// mMediaController.transportControls.playFromMediaId(mMediaItemsList[0].mediaId, null)
mMediaController.transportControls.skipToPrevious()
}
mMediaController.registerCallback(mControllerCallback)
mControllerCallback.onPlaybackStateChanged(mMediaController.playbackState)
mControllerCallback.onMetadataChanged(mMediaController.metadata)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
mExecutorService = Executors.newSingleThreadScheduledExecutor()
seekBar1.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
startText.text = DateUtils.formatElapsedTime((progress).toLong() / 1000)
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
stopSeekbarUpdate()
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
mMediaController.transportControls.seekTo(seekBar.progress.toLong())
scheduleSeekbarUpdate()
}
})
val componentName = ComponentName(this, MediaPlaybackService::class.java)
// initialize the browser
mMediaBrowserCompat = MediaBrowserCompat(
this, componentName, //Identifier for the service
connectionCallback,
null
)
}
private fun updatePlaybackState(state: PlaybackStateCompat?) {
if (state == null) {
return
}
mLastPlaybackState = state
progressBar1.visibility = GONE
when (state.state) {
PlaybackStateCompat.STATE_PLAYING -> {
Log.d("updatePlaybackState ", " state " + state.state)
play_pause.visibility = VISIBLE
play_pause.setImageDrawable(getDrawable(R.drawable.ic_pause_black_24dp))
controllers.visibility = VISIBLE
scheduleSeekbarUpdate()
}
PlaybackStateCompat.STATE_PAUSED -> {
Log.d("updatePlaybackState ", " state " + state.state)
controllers.visibility = VISIBLE
play_pause.visibility = VISIBLE
play_pause.setImageDrawable(getDrawable(R.drawable.ic_play_arrow_black_24dp))
stopSeekbarUpdate()
}
PlaybackStateCompat.STATE_STOPPED -> {
Log.d("updatePlaybackState ", " state " + state.state)
play_pause.visibility = VISIBLE
play_pause.setImageDrawable(getDrawable(R.drawable.ic_play_arrow_black_24dp))
stopSeekbarUpdate()
// mMediaController.transportControls.skipToNext()
}
PlaybackStateCompat.STATE_BUFFERING -> {
progressBar1.visibility = VISIBLE
stopSeekbarUpdate()
}
PlaybackStateCompat.STATE_NONE -> {
Log.d("updatePlaybackState ", " state " + state.state)
play_pause.visibility = VISIBLE
play_pause.setImageDrawable(getDrawable(R.drawable.ic_play_arrow_black_24dp))
stopSeekbarUpdate()
}
else -> Log.d("updatePlaybackState ", "Unhandled state " + state.state)
}
}
private fun updateProgress() {
mLastPlaybackState?.let {
var currentPos = it.position.toInt()
if (it.state == PlaybackStateCompat.STATE_PLAYING) {
val timeDelta = SystemClock.elapsedRealtime() - it.lastPositionUpdateTime
currentPos += (timeDelta * it.playbackSpeed).toInt()
}
Log.d(this::class.java.simpleName, "current pos = $currentPos")
seekBar1.progress = currentPos
}
}
private fun scheduleSeekbarUpdate() {
stopSeekbarUpdate()
if (!mExecutorService.isShutdown) {
mScheduleFuture = mExecutorService.scheduleAtFixedRate(
{
mHandler.post(mUpdateProgressTask)
},
PROGRESS_UPDATE_INITIAL_INTERVAL,
PROGRESS_UPDATE_INTERNAL,
TimeUnit.MILLISECONDS
)
}
}
private fun stopSeekbarUpdate() {
mScheduleFuture?.cancel(false)
}
private fun updateDuration(metadata: MediaMetadataCompat?) {
if (metadata == null) {
return
}
Log.d("updateDuration ", "updateDuration called ")
val duration = metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION).toInt()
seekBar1.max = duration
endText.text = "0:0"
endText.text = DateUtils.formatElapsedTime((duration / 1000).toLong())
}
override fun onStart() {
super.onStart()
// connect the controllers again to the session
// without this connect() you won't be able to start the service neither control it with the controller
if (!mMediaBrowserCompat.isConnected) {
mMediaBrowserCompat.connect()
}
}
override fun onStop() {
super.onStop()
// Release the resources
val controllerCompat = MediaControllerCompat.getMediaController(this)
controllerCompat?.unregisterCallback(mControllerCallback)
mMediaBrowserCompat.unsubscribe("media_root_id")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment