diff --git a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt index 74ce64c14..8178a8218 100644 --- a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt +++ b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt @@ -20,6 +20,7 @@ package org.oxycblt.auxio import android.annotation.SuppressLint import android.content.Intent +import android.os.IBinder import androidx.core.app.ServiceCompat import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaSession @@ -41,6 +42,27 @@ class AuxioService : MediaLibraryService(), ForegroundListener { indexingFragment.attach(this) } + override fun onBind(intent: Intent?): IBinder? { + handleIntent(intent) + return super.onBind(intent) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + // TODO: Start command occurring from a foreign service basically implies a detached + // service, we might need more handling here. + handleIntent(intent) + return super.onStartCommand(intent, flags, startId) + } + + private fun handleIntent(intent: Intent?) { + val nativeStart = intent?.getBooleanExtra(INTENT_KEY_NATIVE_START, false) ?: false + if (!nativeStart) { + // Some foreign code started us, no guarantees about foreground stability. Figure + // out what to do. + mediaSessionFragment.handleNonNativeStart() + } + } + override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) mediaSessionFragment.handleTaskRemoved() @@ -78,6 +100,11 @@ class AuxioService : MediaLibraryService(), ForegroundListener { } } } + + companion object { + // This is only meant for Auxio to internally ensure that it's state management will work. + const val INTENT_KEY_NATIVE_START = BuildConfig.APPLICATION_ID + ".service.NATIVE_START" + } } interface ForegroundListener { diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index e727316fb..42ad2a134 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -69,7 +69,9 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() - startService(Intent(this, AuxioService::class.java)) + startService( + Intent(this, AuxioService::class.java) + .putExtra(AuxioService.INTENT_KEY_NATIVE_START, true)) if (!startIntentAction(intent)) { // No intent action to do, just restore the previously saved state. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt index 3d8099b7e..a93aa019a 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/service/MediaSessionServiceFragment.kt @@ -50,7 +50,9 @@ import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R import org.oxycblt.auxio.image.service.MediaSessionBitmapLoader import org.oxycblt.auxio.music.service.MediaItemBrowser +import org.oxycblt.auxio.playback.state.DeferredPlayback import org.oxycblt.auxio.playback.state.PlaybackStateManager +import org.oxycblt.auxio.util.logD class MediaSessionServiceFragment @Inject @@ -103,14 +105,11 @@ constructor( } } - fun release() { - waitJob.cancel() - mediaSession.release() - actionHandler.release() - exoHolder.release() - playbackManager.removeListener(this) - mediaSession.release() - foregroundListener = null + fun handleNonNativeStart() { + // At minimum we want to ensure an active playback state. + // TODO: Possibly also force to go foreground? + logD("Handling non-native start.") + playbackManager.playDeferred(DeferredPlayback.RestoreState) } fun hasNotification(): Boolean = exoHolder.sessionOngoing @@ -124,6 +123,16 @@ constructor( post(wrapMediaNotification(notification)) } + fun release() { + waitJob.cancel() + mediaSession.release() + actionHandler.release() + exoHolder.release() + playbackManager.removeListener(this) + mediaSession.release() + foregroundListener = null + } + private fun wrapMediaNotification(notification: MediaNotification): MediaNotification { // Pulled from MediaNotificationManager: Need to specify MediaSession token manually // in notification