[Playback] Code Cleanup

This commit is contained in:
Prathamesh More 2022-06-05 23:19:25 +05:30
parent 7186071772
commit 7efbbc3f11
7 changed files with 78 additions and 114 deletions

View file

@ -33,19 +33,15 @@ class CastPlayer(castSession: CastSession) : Playback,
override var callbacks: Playback.PlaybackCallbacks? = null override var callbacks: Playback.PlaybackCallbacks? = null
override fun setDataSource( override fun setDataSource(song: Song, force: Boolean): Boolean {
song: Song, return try {
force: Boolean,
completion: (success: Boolean) -> Unit,
) {
try {
val mediaLoadOptions = val mediaLoadOptions =
MediaLoadOptions.Builder().setPlayPosition(0).setAutoplay(true).build() MediaLoadOptions.Builder().setPlayPosition(0).setAutoplay(true).build()
remoteMediaClient?.load(song.toMediaInfo()!!, mediaLoadOptions) remoteMediaClient?.load(song.toMediaInfo()!!, mediaLoadOptions)
completion(true) true
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
completion(false) false
} }
} }

View file

@ -120,25 +120,17 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
override val isPlaying: Boolean override val isPlaying: Boolean
get() = mIsInitialized && getCurrentPlayer()?.isPlaying == true get() = mIsInitialized && getCurrentPlayer()?.isPlaying == true
override fun setDataSource( override fun setDataSource(song: Song, force: Boolean): Boolean {
song: Song,
force: Boolean,
completion: (success: Boolean) -> Unit,
) {
if (force) hasDataSource = false if (force) hasDataSource = false
mIsInitialized = false mIsInitialized = false
/* We've already set DataSource if initialized is true in setNextDataSource */ /* We've already set DataSource if initialized is true in setNextDataSource */
if (!hasDataSource) { return if (!hasDataSource) {
getCurrentPlayer()?.let { mIsInitialized = setDataSourceImpl(getCurrentPlayer()!!, song.uri.toString())
setDataSourceImpl(it, song.uri.toString()) { success ->
mIsInitialized = success
completion(success)
}
}
hasDataSource = true hasDataSource = true
mIsInitialized
} else { } else {
completion(true)
mIsInitialized = true mIsInitialized = true
true
} }
} }
@ -293,8 +285,8 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
val nextSong = MusicPlayerRemote.nextSong val nextSong = MusicPlayerRemote.nextSong
// Switch to other player (Crossfade) only if next song exists // Switch to other player (Crossfade) only if next song exists
if (nextSong != null) { if (nextSong != null) {
setDataSourceImpl(player, nextSong.uri.toString()) { success -> if (setDataSourceImpl(player, nextSong.uri.toString())) {
if (success) switchPlayer() switchPlayer()
} }
} }
} }

View file

@ -109,13 +109,10 @@ abstract class LocalPlayback(val context: Context) : Playback, MediaPlayer.OnErr
* @param path The path of the file, or the http/rtsp URL of the stream you want to play * @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise * @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/ */
fun setDataSourceImpl( fun setDataSourceImpl(player: MediaPlayer, path: String): Boolean {
player: MediaPlayer,
path: String,
completion: (success: Boolean) -> Unit,
) {
player.reset()
try { try {
player.reset()
player.setOnPreparedListener(null)
if (path.startsWith("content://")) { if (path.startsWith("content://")) {
player.setDataSource(context, path.toUri()) player.setDataSource(context, path.toUri())
} else { } else {
@ -126,17 +123,14 @@ abstract class LocalPlayback(val context: Context) : Playback, MediaPlayer.OnErr
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build() .build()
) )
player.setOnPreparedListener { player.prepare()
player.setOnPreparedListener(null)
completion(true)
}
player.prepareAsync()
} catch (e: Exception) { } catch (e: Exception) {
completion(false)
e.printStackTrace() e.printStackTrace()
return false
} }
player.setOnCompletionListener(this) player.setOnCompletionListener(this)
player.setOnErrorListener(this) player.setOnErrorListener(this)
return true
} }
private fun unregisterBecomingNoisyReceiver() { private fun unregisterBecomingNoisyReceiver() {

View file

@ -46,19 +46,13 @@ class MultiPlayer(context: Context) : LocalPlayback(context) {
* @param song The song object you want to play * @param song The song object you want to play
* @return True if the `player` has been prepared and is ready to play, false otherwise * @return True if the `player` has been prepared and is ready to play, false otherwise
*/ */
override fun setDataSource( override fun setDataSource(song: Song, force: Boolean): Boolean {
song: Song,
force: Boolean,
completion: (success: Boolean) -> Unit,
) {
isInitialized = false isInitialized = false
setDataSourceImpl(mCurrentMediaPlayer, song.uri.toString()) { success -> isInitialized = setDataSourceImpl(mCurrentMediaPlayer, song.uri.toString())
isInitialized = success if (isInitialized) {
if (isInitialized) { setNextDataSource(null)
setNextDataSource(null)
}
completion(isInitialized)
} }
return isInitialized
} }
/** /**
@ -86,28 +80,26 @@ class MultiPlayer(context: Context) : LocalPlayback(context) {
mNextMediaPlayer = MediaPlayer() mNextMediaPlayer = MediaPlayer()
mNextMediaPlayer?.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) mNextMediaPlayer?.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
mNextMediaPlayer?.audioSessionId = audioSessionId mNextMediaPlayer?.audioSessionId = audioSessionId
setDataSourceImpl(mNextMediaPlayer!!, path) { success -> if (setDataSourceImpl(mNextMediaPlayer!!, path)) {
if (success) { try {
try { mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer)
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer) } catch (e: IllegalArgumentException) {
} catch (e: IllegalArgumentException) { Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
if (mNextMediaPlayer != null) {
mNextMediaPlayer?.release()
mNextMediaPlayer = null
}
} catch (e: IllegalStateException) {
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
if (mNextMediaPlayer != null) {
mNextMediaPlayer?.release()
mNextMediaPlayer = null
}
}
} else {
if (mNextMediaPlayer != null) { if (mNextMediaPlayer != null) {
mNextMediaPlayer?.release() mNextMediaPlayer?.release()
mNextMediaPlayer = null mNextMediaPlayer = null
} }
} catch (e: IllegalStateException) {
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
if (mNextMediaPlayer != null) {
mNextMediaPlayer?.release()
mNextMediaPlayer = null
}
}
} else {
if (mNextMediaPlayer != null) {
mNextMediaPlayer?.release()
mNextMediaPlayer = null
} }
} }
} }

View file

@ -117,6 +117,11 @@ class MusicService : MediaBrowserServiceCompat(),
private var trackEndedByCrossfade = false private var trackEndedByCrossfade = false
private val serviceScope = CoroutineScope(Job() + Main) private val serviceScope = CoroutineScope(Job() + Main)
// Every chromecast method needs to run on main thread or you are greeted with IllegalStateException
// So it will use Main dispatcher
// And by using Default dispatcher for local playback we are reducing the burden from main thread
private val playerDispatcher get() = if (playbackManager.isLocalPlayback) Default else Main
@JvmField @JvmField
var position = -1 var position = -1
private val appWidgetBig = AppWidgetBig.instance private val appWidgetBig = AppWidgetBig.instance
@ -436,11 +441,8 @@ class MusicService : MediaBrowserServiceCompat(),
} }
private fun setPosition(position: Int) { private fun setPosition(position: Int) {
openTrackAndPrepareNextAt(position) { success -> openTrackAndPrepareNextAt(position)
if (success) { notifyChange(PLAY_STATE_CHANGED)
notifyChange(PLAY_STATE_CHANGED)
}
}
} }
private fun getPreviousPosition(force: Boolean): Int { private fun getPreviousPosition(force: Boolean): Int {
@ -755,16 +757,15 @@ class MusicService : MediaBrowserServiceCompat(),
} }
@Synchronized @Synchronized
fun openTrackAndPrepareNextAt(position: Int, completion: (success: Boolean) -> Unit) { fun openTrackAndPrepareNextAt(position: Int): Boolean {
this.position = position this.position = position
openCurrent { success -> val prepared = openCurrent()
completion(success) if (prepared) {
notifyChange(META_CHANGED) prepareNextImpl()
notHandledMetaChangedForCurrentTrack = false
if (success) {
prepareNextImpl()
}
} }
notifyChange(META_CHANGED)
notHandledMetaChangedForCurrentTrack = false
return prepared
} }
fun pause(force: Boolean = false) { fun pause(force: Boolean = false) {
@ -793,16 +794,11 @@ class MusicService : MediaBrowserServiceCompat(),
} }
fun playSongAt(position: Int) { fun playSongAt(position: Int) {
// Every chromecast method needs to run on main thread or you are greeted with IllegalStateException serviceScope.launch(playerDispatcher) {
// So it will use Main dispatcher if (openTrackAndPrepareNextAt(position)) {
// And by using Default dispatcher for local playback we are reduce the burden of main thread play()
serviceScope.launch(if(playbackManager.isLocalPlayback) Default else Main) { } else {
openTrackAndPrepareNextAt(position) { success -> showToast(R.string.unplayable_file)
if (success) {
play()
} else {
showToast(resources.getString(R.string.unplayable_file))
}
} }
} }
} }
@ -917,15 +913,14 @@ class MusicService : MediaBrowserServiceCompat(),
originalPlayingQueue = ArrayList(restoredOriginalQueue) originalPlayingQueue = ArrayList(restoredOriginalQueue)
playingQueue = ArrayList(restoredQueue) playingQueue = ArrayList(restoredQueue)
position = restoredPosition position = restoredPosition
withContext(Main) { withContext(playerDispatcher) {
openCurrent { openCurrent()
prepareNext() prepareNext()
if (restoredPositionInTrack > 0) { if (restoredPositionInTrack > 0) {
seek(restoredPositionInTrack) seek(restoredPositionInTrack)
}
notHandledMetaChangedForCurrentTrack = true
sendChangeInternal(META_CHANGED)
} }
notHandledMetaChangedForCurrentTrack = true
sendChangeInternal(META_CHANGED)
if (receivedHeadsetConnected) { if (receivedHeadsetConnected) {
play() play()
receivedHeadsetConnected = false receivedHeadsetConnected = false
@ -1169,15 +1164,18 @@ class MusicService : MediaBrowserServiceCompat(),
} }
@Synchronized @Synchronized
private fun openCurrent(completion: (success: Boolean) -> Unit) { private fun openCurrent(): Boolean {
val force = if (!trackEndedByCrossfade) { val force = if (!trackEndedByCrossfade) {
true true
} else { } else {
trackEndedByCrossfade = false trackEndedByCrossfade = false
false false
} }
playbackManager.setDataSource(currentSong, force) { success -> return try {
completion(success) playbackManager.setDataSource(currentSong, force)
} catch (e: Exception) {
e.printStackTrace()
false
} }
} }
@ -1191,12 +1189,10 @@ class MusicService : MediaBrowserServiceCompat(),
private fun restorePlaybackState(wasPlaying: Boolean, progress: Int) { private fun restorePlaybackState(wasPlaying: Boolean, progress: Int) {
playbackManager.setCallbacks(this) playbackManager.setCallbacks(this)
openTrackAndPrepareNextAt(position) { success -> if (openTrackAndPrepareNextAt(position)) {
if (success) { seek(progress)
seek(progress) if (wasPlaying) {
if (wasPlaying) { play()
play()
}
} }
} }
playbackManager.setCrossFadeDuration(crossFadeDuration) playbackManager.setCrossFadeDuration(crossFadeDuration)

View file

@ -16,7 +16,7 @@ class PlaybackManager(val context: Context) {
var playback: Playback? = null var playback: Playback? = null
private var playbackLocation = PlaybackLocation.LOCAL private var playbackLocation = PlaybackLocation.LOCAL
val isLocalPlayback get() = playbackLocation== PlaybackLocation.LOCAL val isLocalPlayback get() = playbackLocation == PlaybackLocation.LOCAL
val audioSessionId: Int val audioSessionId: Int
get() = if (playback != null) { get() = if (playback != null) {
@ -86,12 +86,8 @@ class PlaybackManager(val context: Context) {
fun seek(millis: Int): Int = playback!!.seek(millis) fun seek(millis: Int): Int = playback!!.seek(millis)
fun setDataSource( fun setDataSource(song: Song, force: Boolean): Boolean {
song: Song, return playback?.setDataSource(song, force) == true
force: Boolean,
completion: (success: Boolean) -> Unit,
) {
playback?.setDataSource(song, force, completion)
} }
fun setNextDataSource(trackUri: String) { fun setNextDataSource(trackUri: String) {

View file

@ -25,9 +25,7 @@ interface Playback {
val audioSessionId: Int val audioSessionId: Int
fun setDataSource( fun setDataSource(song: Song, force: Boolean):Boolean
song: Song, force: Boolean, completion: (success: Boolean) -> Unit,
)
fun setNextDataSource(path: String?) fun setNextDataSource(path: String?)