Skip to content

Commit

Permalink
fix(android): foreground issues (close #2045)
Browse files Browse the repository at this point in the history
- move execution of foregrounding and backgrounding to NotificationState.POSTED in order to avoid race condition where notificationId has not been set yet
- avoid comparing to player states that don’t affect foregrounding, by ignoring LOADING, BUFFERING and READY states – all of which can occur both when playing or while paused
- also avoid the initial IDLE state, since we are only interested when the player would become idle (i.e. all tracks were removed)
- ENDED state should also cause foregrounding to stop (without removing notification)
  • Loading branch information
puckey committed Jun 29, 2023
1 parent 8331979 commit 04e42e9
Showing 1 changed file with 34 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ class MusicService : HeadlessJsTaskService() {
// Implementation based on https://github.com/Automattic/pocket-casts-android/blob/ee8da0c095560ef64a82d3a31464491b8d713104/modules/services/repositories/src/main/java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackService.kt#L218
var notificationId: Int? = null
var notification: Notification? = null
var playerState: AudioPlayerState? = null

fun startForegroundIfNecessary() {
if (isForegroundService()) {
Expand Down Expand Up @@ -535,30 +536,46 @@ class MusicService : HeadlessJsTaskService() {
Timber.d("notification posted with id=%s, ongoing=%s", it.notificationId, it.ongoing)
notificationId = it.notificationId;
notification = it.notification;
if (it.ongoing) {
if (player.playWhenReady) {
startForegroundIfNecessary()
}
} else {
when (playerState) {
AudioPlayerState.IDLE,
AudioPlayerState.ENDED,
AudioPlayerState.STOPPED,
AudioPlayerState.ERROR,
AudioPlayerState.PAUSED -> {
val removeNotification = playerState == AudioPlayerState.IDLE
|| playerState == AudioPlayerState.STOPPED
if (removeNotification || isForegroundService()) {
@Suppress("DEPRECATION")
stopForeground(removeNotification)
Timber.d("stopped foregrounding")
}
}
else -> {}
}
}
}
else -> {}
}
}
}
scope.launch {
event.stateChange.collect {
when (it) {
AudioPlayerState.BUFFERING,
AudioPlayerState.PLAYING -> {
startForegroundIfNecessary()
}
AudioPlayerState.IDLE,
AudioPlayerState.STOPPED,
AudioPlayerState.ERROR,
AudioPlayerState.PAUSED -> {
val removeNotification = it != AudioPlayerState.PAUSED && it != AudioPlayerState.ERROR
if (removeNotification || isForegroundService()) {
@Suppress("DEPRECATION")
stopForeground(removeNotification)
Timber.d("stopped foregrounding")
}
}
else -> {}
Timber.d("player state changed to %s", it)
// Skip states that don't affect foregrounding
if (
it != AudioPlayerState.LOADING &&
it != AudioPlayerState.READY &&
it != AudioPlayerState.BUFFERING &&
// Skip iniital idle state, since we are only interested when
// state becomes idle after not being idle
!(playerState == null && it == AudioPlayerState.IDLE)
) {
playerState = it
}
}
}
Expand Down

0 comments on commit 04e42e9

Please sign in to comment.