diff --git a/composeApp/src/androidJvmMain/kotlin/org/spotube/spotube/core/newpipe/NewPipeDownloader.kt b/composeApp/src/androidJvmMain/kotlin/org/spotube/spotube/core/newpipe/NewPipeDownloader.kt index fc0fa47e..5e3def2b 100644 --- a/composeApp/src/androidJvmMain/kotlin/org/spotube/spotube/core/newpipe/NewPipeDownloader.kt +++ b/composeApp/src/androidJvmMain/kotlin/org/spotube/spotube/core/newpipe/NewPipeDownloader.kt @@ -82,10 +82,16 @@ class NewPipeDownloader(private val cookieJar: PersistentCookieJar) : Downloader companion object { private var instance: NewPipeDownloader? = null + /** + * Safe to call on every Activity creation: the process (and so this singleton) outlives + * the Activity, and a second [PersistentCookieJar] would open a second DataStore on the + * same cookies file, which DataStore rejects. + */ + @Synchronized fun init(paths: Paths) { + if (instance != null) return val cookieJar = PersistentCookieJar(paths) - instance = NewPipeDownloader(cookieJar) - NewPipe.init(instance) + instance = NewPipeDownloader(cookieJar).also { NewPipe.init(it) } } } } diff --git a/composeApp/src/androidMain/kotlin/org/spotube/spotube/media/PlaybackService.kt b/composeApp/src/androidMain/kotlin/org/spotube/spotube/media/PlaybackService.kt index bc3fdaf1..74df39e7 100644 --- a/composeApp/src/androidMain/kotlin/org/spotube/spotube/media/PlaybackService.kt +++ b/composeApp/src/androidMain/kotlin/org/spotube/spotube/media/PlaybackService.kt @@ -23,6 +23,7 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Intent import android.os.Build +import android.os.Process import android.util.Log import androidx.annotation.OptIn import androidx.core.app.NotificationCompat @@ -61,6 +62,8 @@ import spotube.composeapp.generated.resources.notification_playback_channel_name @OptIn(UnstableApi::class) class PlaybackService : MediaLibraryService(), KoinComponent { + private var taskRemoved = false + private val audioPlayer = inject().value as AudioPlayer private val audioPlayerQueue: AudioPlayerQueue by inject() @@ -161,9 +164,13 @@ class PlaybackService : MediaLibraryService(), KoinComponent { removeSession(librarySession) librarySession.release() super.onDestroy() + // Swiping the app out of recents should fully close it: Android otherwise keeps this + // process cached with the proxy server, MQTT client and every singleton still alive. + if (taskRemoved) Process.killProcess(Process.myPid()) } override fun onTaskRemoved(rootIntent: Intent?) { + taskRemoved = true pauseAllPlayersAndStopSelf() }