From 0173ace37e1c132f762bbc322595049dea12c05c Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Mon, 6 Feb 2023 06:47:07 +0530 Subject: [PATCH 1/4] Add Lifecycle Service library. --- app/build.gradle | 1 + gradle/libs.versions.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index ff5d81a1a..04b0bb8ec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,6 +92,7 @@ dependencies { implementation libs.lifecycle.viewmodel implementation libs.lifecycle.runtime implementation libs.lifecycle.livedata + implementation libs.lifecycle.service /* Testing */ androidTestImplementation libs.androidx.test.junit diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2e6af9a8a..8c2bb8556 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,6 +45,7 @@ square-leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-andr lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" } lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" } +lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "lifecycle" } room = { group = "androidx.room", name="room-ktx", version.ref = "room" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" } From 5e6d171675fcdc43f63b397369ea3eb5779aa5bb Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Mon, 6 Feb 2023 07:13:38 +0530 Subject: [PATCH 2/4] Convert DownloadService and BackgroundMode to lifecycle services. --- .../libretube/services/BackgroundMode.kt | 33 ++++++++----------- .../libretube/services/DownloadService.kt | 32 ++++++++---------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index 01541746a..7d5d6c39e 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -3,7 +3,6 @@ package com.github.libretube.services import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager -import android.app.Service import android.content.Intent import android.os.Binder import android.os.Build @@ -13,10 +12,11 @@ import android.os.Looper import android.util.Log import android.widget.Toast import androidx.core.app.ServiceCompat +import androidx.lifecycle.LifecycleService +import androidx.lifecycle.lifecycleScope import com.github.libretube.R import com.github.libretube.api.JsonHelper import com.github.libretube.api.RetrofitInstance -import com.github.libretube.api.obj.Segment import com.github.libretube.api.obj.SegmentData import com.github.libretube.api.obj.Streams import com.github.libretube.constants.BACKGROUND_CHANNEL_ID @@ -37,15 +37,15 @@ import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.PlaybackException import com.google.android.exoplayer2.Player -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString /** * Loads the selected videos audio in background mode with a notification area. */ -class BackgroundMode : Service() { +class BackgroundMode : LifecycleService() { /** * VideoId of the video */ @@ -171,7 +171,7 @@ class BackgroundMode : Service() { seekToPosition: Long = 0, keepQueue: Boolean = false ) { - CoroutineScope(Dispatchers.IO).launch { + lifecycleScope.launch(Dispatchers.IO) { streams = runCatching { RetrofitInstance.api.getStreams(videoId) }.getOrNull() ?: return@launch @@ -186,7 +186,7 @@ class BackgroundMode : Service() { PlayingQueue.updateCurrent(it) } - handler.post { + withContext(Dispatchers.Main) { playAudio(seekToPosition) } } @@ -292,18 +292,12 @@ class BackgroundMode : Service() { * Sets the [MediaItem] with the [streams] into the [player] */ private fun setMediaItem() { + val streams = streams streams ?: return - val uri = if (streams!!.audioStreams.orEmpty().isNotEmpty()) { - PlayerHelper.getAudioSource( - this, - streams!!.audioStreams!! - ) - } else if (streams!!.hls != null) { - streams!!.hls - } else { - return - } + val uri = if (streams.audioStreams.isNotEmpty()) { + PlayerHelper.getAudioSource(this, streams.audioStreams) + } else streams.hls ?: return val mediaItem = MediaItem.Builder() .setUri(uri) @@ -315,7 +309,7 @@ class BackgroundMode : Service() { * fetch the segments for SponsorBlock */ private fun fetchSponsorBlockSegments() { - CoroutineScope(Dispatchers.IO).launch { + lifecycleScope.launch(Dispatchers.IO) { runCatching { val categories = PlayerHelper.getSponsorBlockCategories() if (categories.isEmpty()) return@runCatching @@ -336,7 +330,7 @@ class BackgroundMode : Service() { if (segmentData == null || segmentData!!.segments.isEmpty()) return - segmentData!!.segments.forEach { segment: Segment -> + segmentData!!.segments.forEach { segment -> val segmentStart = (segment.segment[0] * 1000f).toLong() val segmentEnd = (segment.segment[1] * 1000f).toLong() val currentPosition = player?.currentPosition @@ -399,7 +393,8 @@ class BackgroundMode : Service() { fun getService(): BackgroundMode = this@BackgroundMode } - override fun onBind(p0: Intent?): IBinder { + override fun onBind(intent: Intent): IBinder { + super.onBind(intent) return binder } diff --git a/app/src/main/java/com/github/libretube/services/DownloadService.kt b/app/src/main/java/com/github/libretube/services/DownloadService.kt index eefe9affd..9fe3f4859 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -2,7 +2,6 @@ package com.github.libretube.services import android.app.NotificationManager import android.app.PendingIntent -import android.app.Service import android.content.Intent import android.os.Binder import android.os.IBinder @@ -11,6 +10,8 @@ import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.core.util.set import androidx.core.util.valueIterator +import androidx.lifecycle.LifecycleService +import androidx.lifecycle.lifecycleScope import com.github.libretube.R import com.github.libretube.api.CronetHelper import com.github.libretube.api.RetrofitInstance @@ -40,13 +41,9 @@ import java.io.File import java.net.HttpURLConnection import java.net.SocketTimeoutException import java.net.URL -import java.util.concurrent.Executors import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -59,12 +56,8 @@ import okio.source /** * Download service with custom implementation of downloading using [HttpURLConnection]. */ -class DownloadService : Service() { - +class DownloadService : LifecycleService() { private val binder = LocalBinder() - private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() - private val jobMain = SupervisorJob() - private val scope = CoroutineScope(dispatcher + jobMain) private lateinit var notificationManager: NotificationManager private lateinit var summaryNotificationBuilder: NotificationCompat.Builder @@ -81,6 +74,7 @@ class DownloadService : Service() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + super.onStartCommand(intent, flags, startId) when (intent?.action) { ACTION_DOWNLOAD_RESUME -> resume(intent.getIntExtra("id", -1)) ACTION_DOWNLOAD_PAUSE -> pause(intent.getIntExtra("id", -1)) @@ -94,9 +88,11 @@ class DownloadService : Service() { val audioQuality = intent.getStringExtra(IntentData.audioQuality) val subtitleCode = intent.getStringExtra(IntentData.subtitleCode) - scope.launch { + lifecycleScope.launch { try { - val streams = RetrofitInstance.api.getStreams(videoId) + val streams = withContext(Dispatchers.IO) { + RetrofitInstance.api.getStreams(videoId) + } val thumbnailTargetFile = getDownloadFile(DownloadHelper.THUMBNAIL_DIR, fileName) @@ -153,7 +149,7 @@ class DownloadService : Service() { Database.downloadDao().insertDownloadItem(item) }.toInt() - scope.launch { + lifecycleScope.launch { downloadFile(item) } } @@ -297,7 +293,7 @@ class DownloadService : Service() { val downloadCount = downloadQueue.valueIterator().asSequence().count { it } if (downloadCount >= DownloadHelper.getMaxConcurrentDownloads()) { toastFromMainThread(getString(R.string.concurrent_downloads_limit_reached)) - scope.launch { + lifecycleScope.launch { _downloadFlow.emit(id to DownloadStatus.Paused) } return @@ -306,7 +302,7 @@ class DownloadService : Service() { val downloadItem = awaitQuery { Database.downloadDao().findDownloadItemById(id) } - scope.launch { + lifecycleScope.launch { downloadFile(downloadItem) } } @@ -468,9 +464,9 @@ class DownloadService : Service() { super.onDestroy() } - override fun onBind(intent: Intent?): IBinder { - val ids = intent?.getIntArrayExtra("ids") - ids?.forEach { id -> resume(id) } + override fun onBind(intent: Intent): IBinder { + super.onBind(intent) + intent.getIntArrayExtra("ids")?.forEach { resume(it) } return binder } From 2321ed22f193a83ad66b3a9361786a1666d564ec Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Wed, 8 Feb 2023 06:20:06 +0530 Subject: [PATCH 3/4] Restore coroutine context originally used in DownloadService. --- .../libretube/services/DownloadService.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/github/libretube/services/DownloadService.kt b/app/src/main/java/com/github/libretube/services/DownloadService.kt index 9fe3f4859..9739926f5 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -44,6 +44,8 @@ import java.net.URL import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -52,12 +54,15 @@ import okio.BufferedSink import okio.buffer import okio.sink import okio.source +import java.util.concurrent.Executors /** * Download service with custom implementation of downloading using [HttpURLConnection]. */ class DownloadService : LifecycleService() { private val binder = LocalBinder() + private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + private val coroutineContext = dispatcher + SupervisorJob() private lateinit var notificationManager: NotificationManager private lateinit var summaryNotificationBuilder: NotificationCompat.Builder @@ -88,7 +93,7 @@ class DownloadService : LifecycleService() { val audioQuality = intent.getStringExtra(IntentData.audioQuality) val subtitleCode = intent.getStringExtra(IntentData.subtitleCode) - lifecycleScope.launch { + lifecycleScope.launch(coroutineContext) { try { val streams = withContext(Dispatchers.IO) { RetrofitInstance.api.getStreams(videoId) @@ -149,7 +154,7 @@ class DownloadService : LifecycleService() { Database.downloadDao().insertDownloadItem(item) }.toInt() - lifecycleScope.launch { + lifecycleScope.launch(coroutineContext) { downloadFile(item) } } @@ -239,7 +244,7 @@ class DownloadService : LifecycleService() { notificationBuilder .setContentText( totalRead.formatAsFileSize() + " / " + - item.downloadSize.formatAsFileSize() + item.downloadSize.formatAsFileSize() ) .setProgress( item.downloadSize.toInt(), @@ -265,7 +270,8 @@ class DownloadService : LifecycleService() { sourceByte.close() con.disconnect() } - } catch (_: Exception) { } + } catch (_: Exception) { + } val completed = when { totalRead < item.downloadSize -> { @@ -293,7 +299,7 @@ class DownloadService : LifecycleService() { val downloadCount = downloadQueue.valueIterator().asSequence().count { it } if (downloadCount >= DownloadHelper.getMaxConcurrentDownloads()) { toastFromMainThread(getString(R.string.concurrent_downloads_limit_reached)) - lifecycleScope.launch { + lifecycleScope.launch(coroutineContext) { _downloadFlow.emit(id to DownloadStatus.Paused) } return @@ -302,7 +308,7 @@ class DownloadService : LifecycleService() { val downloadItem = awaitQuery { Database.downloadDao().findDownloadItemById(id) } - lifecycleScope.launch { + lifecycleScope.launch(coroutineContext) { downloadFile(downloadItem) } } From df565bb8da4732b07007973ae3f75463d52e8336 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Wed, 8 Feb 2023 06:31:01 +0530 Subject: [PATCH 4/4] Fix lint issues. --- .../main/java/com/github/libretube/services/BackgroundMode.kt | 4 +++- .../java/com/github/libretube/services/DownloadService.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index 7d5d6c39e..45dc415b3 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -297,7 +297,9 @@ class BackgroundMode : LifecycleService() { val uri = if (streams.audioStreams.isNotEmpty()) { PlayerHelper.getAudioSource(this, streams.audioStreams) - } else streams.hls ?: return + } else { + streams.hls ?: return + } val mediaItem = MediaItem.Builder() .setUri(uri) diff --git a/app/src/main/java/com/github/libretube/services/DownloadService.kt b/app/src/main/java/com/github/libretube/services/DownloadService.kt index 9739926f5..1b659610d 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -244,7 +244,7 @@ class DownloadService : LifecycleService() { notificationBuilder .setContentText( totalRead.formatAsFileSize() + " / " + - item.downloadSize.formatAsFileSize() + item.downloadSize.formatAsFileSize() ) .setProgress( item.downloadSize.toInt(),