Created
June 17, 2020 14:01
-
-
Save AhmedBadrSayed/b90a58e21585e4cbf80f945018657012 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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