Simplify controller logic and catch cancellation exception

This commit is contained in:
Naveen 2023-08-19 13:12:09 +05:30
parent 493ab65fc3
commit a4a5938a38
No known key found for this signature in database
GPG key ID: 0E155DAD31671DA3
2 changed files with 38 additions and 40 deletions

View file

@ -9,9 +9,10 @@ import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.MoreExecutors
import com.simplemobiletools.musicplayer.extensions.getOrNull
import com.simplemobiletools.musicplayer.extensions.runOnPlayerThread import com.simplemobiletools.musicplayer.extensions.runOnPlayerThread
import com.simplemobiletools.musicplayer.services.playback.PlaybackService import com.simplemobiletools.musicplayer.services.playback.PlaybackService
import java.util.concurrent.CancellationException import com.simplemobiletools.musicplayer.services.playback.PlaybackService.Companion.updatePlaybackInfo
import java.util.concurrent.Executors import java.util.concurrent.Executors
/** /**
@ -19,7 +20,7 @@ import java.util.concurrent.Executors
*/ */
abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener { abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener {
private val executorService by lazy { private val executorService by lazy {
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(4)) MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())
} }
private lateinit var controllerFuture: ListenableFuture<MediaController> private lateinit var controllerFuture: ListenableFuture<MediaController>
@ -39,38 +40,27 @@ abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener {
releaseController() releaseController()
} }
private fun getController(): MediaController? {
if (controllerFuture.isDone) {
val activeController = controllerFuture.get()
if (!activeController.isConnected) {
activeController.runOnPlayerThread {
release()
}
newControllerAsync()
}
}
return try {
controllerFuture.get()
} catch (e: CancellationException) {
null
}
}
private fun newControllerAsync() { private fun newControllerAsync() {
controllerFuture = MediaController controllerFuture = MediaController
.Builder(applicationContext, SessionToken(this, ComponentName(this, PlaybackService::class.java))) .Builder(applicationContext, SessionToken(this, ComponentName(this, PlaybackService::class.java)))
.buildAsync() .buildAsync()
controllerFuture.addListener({ controllerFuture.addListener({
getController()?.addListener(this) controllerFuture.getOrNull()?.addListener(this)
}, MoreExecutors.directExecutor()) }, MoreExecutors.directExecutor())
} }
private fun acquireController() { private fun shouldCreateNewController(): Boolean {
if (controllerFuture.isDone && !controllerFuture.get().isConnected) { return controllerFuture.isCancelled || controllerFuture.isDone && controllerFuture.getOrNull()?.isConnected == false
newControllerAsync() }
private fun acquireController(callback: (() -> Unit)? = null) {
executorService.execute {
if (shouldCreateNewController()) {
newControllerAsync()
}
callback?.invoke()
} }
} }
@ -78,33 +68,28 @@ abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener {
MediaController.releaseFuture(controllerFuture) MediaController.releaseFuture(controllerFuture)
} }
private fun callOnPlayerThread(callback: MediaController.() -> Unit) {
getController()?.runOnPlayerThread {
callback(this)
}
}
/** /**
* The [callback] is executed on a background player thread. When performing UI operations, callers should use [runOnUiThread]. * The [callback] is executed on a background player thread. When performing UI operations, callers should use [runOnUiThread].
*/ */
fun withPlayer(callback: MediaController.() -> Unit) { fun withPlayer(callback: MediaController.() -> Unit) {
if (controllerFuture.isDone && controllerFuture.get().isConnected) { acquireController {
callOnPlayerThread(callback) controllerFuture.getOrNull()?.runOnPlayerThread {
} else { callback(this)
executorService.execute {
callOnPlayerThread(callback)
} }
} }
} }
fun playMediaItems(mediaItems: List<MediaItem>, startIndex: Int = 0, startPosition: Long = 0) { fun playMediaItems(mediaItems: List<MediaItem>, startIndex: Int = 0, startPosition: Long = 0, startActivity: Boolean = true) {
withPlayer { withPlayer {
if (startActivity) {
startActivity(
Intent(this@SimpleControllerActivity, TrackActivity::class.java)
)
}
setMediaItems(mediaItems, startIndex, startPosition) setMediaItems(mediaItems, startIndex, startPosition)
prepare() prepare()
play() play()
startActivity(
Intent(this@SimpleControllerActivity, TrackActivity::class.java)
)
} }
} }
} }

View file

@ -0,0 +1,13 @@
package com.simplemobiletools.musicplayer.extensions
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.CancellationException
import java.util.concurrent.ExecutionException
fun <T> ListenableFuture<T>.getOrNull() = try {
get() as T
} catch (e: CancellationException) {
null
} catch (e: ExecutionException) {
null
}