Skip to content

Commit

Permalink
Migrate downloader service to WorkManager (#10190)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivaniskandar authored Nov 29, 2023
1 parent 8ff2c01 commit 8ce8b60
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 169 deletions.
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ dependencies {

// RxJava
implementation(libs.rxjava)
implementation(libs.flowreactivenetwork)

// Networking
implementation(libs.bundles.okhttp)
Expand Down
11 changes: 7 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />

<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<!-- Remove permission from Firebase dependency -->
<uses-permission android:name="com.google.android.gms.permission.AD_ID"
Expand Down Expand Up @@ -137,10 +139,6 @@
android:name=".data.notification.NotificationReceiver"
android:exported="false" />

<service
android:name=".data.download.DownloadService"
android:exported="false" />

<service
android:name=".extension.util.ExtensionInstallService"
android:exported="false" />
Expand All @@ -154,6 +152,11 @@
android:value="true" />
</service>

<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
Expand Down
121 changes: 121 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package eu.kanade.tachiyomi.data.download

import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.lifecycle.asFlow
import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.notificationBuilder
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.download.service.DownloadPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get

/**
* This worker is used to manage the downloader. The system can decide to stop the worker, in
* which case the downloader is also stopped. It's also stopped while there's no network available.
*/
class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {

private val downloadManager: DownloadManager = Injekt.get()
private val downloadPreferences: DownloadPreferences = Injekt.get()

override suspend fun getForegroundInfo(): ForegroundInfo {
val notification = applicationContext.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setContentTitle(applicationContext.getString(R.string.download_notifier_downloader_title))
setSmallIcon(android.R.drawable.stat_sys_download)
}.build()
return ForegroundInfo(
Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS,
notification,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
},
)
}

override suspend fun doWork(): Result {
try {
setForeground(getForegroundInfo())
} catch (e: IllegalStateException) {
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
}

var networkCheck = checkConnectivity()
var active = networkCheck
downloadManager.downloaderStart()

// Keep the worker running when needed
while (active) {
delay(100)
networkCheck = checkConnectivity()
active = !isStopped && networkCheck && downloadManager.isRunning
}

return Result.success()
}

private fun checkConnectivity(): Boolean {
return with(applicationContext) {
if (isOnline()) {
val noWifi = downloadPreferences.downloadOnlyOverWifi().get() && !isConnectedToWifi()
if (noWifi) {
downloadManager.downloaderStop(
applicationContext.getString(R.string.download_notifier_text_only_wifi),
)
}
!noWifi
} else {
downloadManager.downloaderStop(applicationContext.getString(R.string.download_notifier_no_network))
false
}
}
}

companion object {
private const val TAG = "Downloader"

fun start(context: Context) {
val request = OneTimeWorkRequestBuilder<DownloadJob>()
.addTag(TAG)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
}

fun stop(context: Context) {
WorkManager.getInstance(context)
.cancelUniqueWork(TAG)
}

fun isRunning(context: Context): Boolean {
return WorkManager.getInstance(context)
.getWorkInfosForUniqueWork(TAG)
.get()
.let { list -> list.count { it.state == WorkInfo.State.RUNNING } == 1 }
}

fun isRunningFlow(context: Context): Flow<Boolean> {
return WorkManager.getInstance(context)
.getWorkInfosForUniqueWorkLiveData(TAG)
.asFlow()
.map { list -> list.count { it.state == WorkInfo.State.RUNNING } == 1 }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class DownloadManager(
*/
private val downloader = Downloader(context, provider, cache)

val isRunning: Boolean
get() = downloader.isRunning

/**
* Queue to delay the deletion of a list of chapters until triggered.
*/
Expand All @@ -59,13 +62,13 @@ class DownloadManager(
fun downloaderStop(reason: String? = null) = downloader.stop(reason)

val isDownloaderRunning
get() = DownloadService.isRunning
get() = DownloadJob.isRunningFlow(context)

/**
* Tells the downloader to begin downloads.
*/
fun startDownloads() {
DownloadService.start(context)
DownloadJob.start(context)
}

/**
Expand Down Expand Up @@ -104,10 +107,10 @@ class DownloadManager(
queue.add(0, toAdd)
reorderQueue(queue)
if (!downloader.isRunning) {
if (DownloadService.isRunning(context)) {
if (DownloadJob.isRunning(context)) {
downloader.start()
} else {
DownloadService.start(context)
DownloadJob.start(context)
}
}
}
Expand Down Expand Up @@ -143,7 +146,7 @@ class DownloadManager(
addAll(0, downloads)
reorderQueue(this)
}
if (!DownloadService.isRunning(context)) DownloadService.start(context)
if (!DownloadJob.isRunning(context)) DownloadJob.start(context)
}

/**
Expand Down
151 changes: 0 additions & 151 deletions app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,7 @@ class Downloader(

isPaused = false

// Prevent recursion when DownloadService.onDestroy() calls downloader.stop()
if (DownloadService.isRunning.value) {
DownloadService.stop(context)
}
DownloadJob.stop(context)
}

/**
Expand Down Expand Up @@ -310,7 +307,7 @@ class Downloader(
)
}
}
DownloadService.start(context)
DownloadJob.start(context)
}
}
}
Expand Down
Loading

0 comments on commit 8ce8b60

Please sign in to comment.