diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt index 10a2c64406..8f6551f7ea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt @@ -38,7 +38,7 @@ internal class UpdaterNotifier(private val context: Context) { * * @param title tile of notification. */ - fun onDownloadStarted(title: String) { + fun onDownloadStarted(title: String): NotificationCompat.Builder { with(notification) { setContentTitle(title) setContentText(context.getString(R.string.downloading)) @@ -46,6 +46,7 @@ internal class UpdaterNotifier(private val context: Context) { setOngoing(true) } notification.show() + return notification } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt index 16974b7352..d6c4bc9b1b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt @@ -1,36 +1,86 @@ package eu.kanade.tachiyomi.data.updater -import android.app.IntentService import android.app.PendingIntent +import android.app.Service import android.content.Context import android.content.Intent +import android.os.Build +import android.os.IBinder +import android.os.PowerManager import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.ProgressListener +import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.newCallWithProgress import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.saveTo +import eu.kanade.tachiyomi.util.system.isServiceRunning +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import java.io.File import timber.log.Timber import uy.kohesive.injekt.injectLazy -import java.io.File -class UpdaterService : IntentService(UpdaterService::class.java.name) { +class UpdaterService : Service() { private val network: NetworkHelper by injectLazy() /** - * Notifier for the updater state and progress. + * Wake lock that will be held until the service is destroyed. */ - private val notifier by lazy { UpdaterNotifier(this) } + private lateinit var wakeLock: PowerManager.WakeLock - override fun onHandleIntent(intent: Intent?) { - if (intent == null) return + private lateinit var notifier: UpdaterNotifier + override fun onCreate() { + super.onCreate() + notifier = UpdaterNotifier(this) + + startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build()) + + wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock" + ) + wakeLock.acquire() + } + + /** + * This method needs to be implemented, but it's not used/needed. + */ + override fun onBind(intent: Intent): IBinder? = null + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent == null) return START_NOT_STICKY + + val url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return START_NOT_STICKY val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name) - val url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return - downloadApk(title, url) + + GlobalScope.launch(Dispatchers.IO) { + downloadApk(title, url) + } + + stopSelf(startId) + return START_NOT_STICKY + } + + override fun stopService(name: Intent?): Boolean { + destroyJob() + return super.stopService(name) + } + + override fun onDestroy() { + destroyJob() + super.onDestroy() + } + + private fun destroyJob() { + if (wakeLock.isHeld) { + wakeLock.release() + } } /** @@ -38,12 +88,11 @@ class UpdaterService : IntentService(UpdaterService::class.java.name) { * * @param url url location of file */ - private fun downloadApk(title: String, url: String) { + private suspend fun downloadApk(title: String, url: String) { // Show notification download starting. notifier.onDownloadStarted(title) val progressListener = object : ProgressListener { - // Progress of the download var savedProgress = 0 @@ -63,7 +112,7 @@ class UpdaterService : IntentService(UpdaterService::class.java.name) { try { // Download the new update. - val response = network.client.newCallWithProgress(GET(url), progressListener).execute() + val response = network.client.newCallWithProgress(GET(url), progressListener).await() // File where the apk will be saved. val apkFile = File(externalCacheDir, "update.apk") @@ -82,27 +131,36 @@ class UpdaterService : IntentService(UpdaterService::class.java.name) { } companion object { - /** - * Download url. - */ + internal const val EXTRA_DOWNLOAD_URL = "${BuildConfig.APPLICATION_ID}.UpdaterService.DOWNLOAD_URL" + internal const val EXTRA_DOWNLOAD_TITLE = "${BuildConfig.APPLICATION_ID}.UpdaterService.DOWNLOAD_TITLE" /** - * Download title + * Returns the status of the service. + * + * @param context the application context. + * @return true if the service is running, false otherwise. */ - internal const val EXTRA_DOWNLOAD_TITLE = "${BuildConfig.APPLICATION_ID}.UpdaterService.DOWNLOAD_TITLE" + private fun isRunning(context: Context): Boolean = + context.isServiceRunning(UpdaterService::class.java) /** * Downloads a new update and let the user install the new version from a notification. * @param context the application context. * @param url the url to the new update. */ - fun downloadUpdate(context: Context, url: String, title: String = context.getString(R.string.app_name)) { - val intent = Intent(context, UpdaterService::class.java).apply { - putExtra(EXTRA_DOWNLOAD_TITLE, title) - putExtra(EXTRA_DOWNLOAD_URL, url) + fun start(context: Context, url: String, title: String = context.getString(R.string.app_name)) { + if (!isRunning(context)) { + val intent = Intent(context, UpdaterService::class.java).apply { + putExtra(EXTRA_DOWNLOAD_TITLE, title) + putExtra(EXTRA_DOWNLOAD_URL, url) + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + context.startService(intent) + } else { + context.startForegroundService(intent) + } } - context.startService(intent) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt similarity index 98% rename from app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt index bd873aee84..e861b4ebc8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt @@ -31,7 +31,7 @@ import java.text.SimpleDateFormat import java.util.Locale import java.util.TimeZone -class SettingsAboutController : SettingsController() { +class AboutController : SettingsController() { /** * Checks for new releases @@ -172,7 +172,7 @@ class SettingsAboutController : SettingsController() { if (appContext != null) { // Start download val url = args.getString(URL_KEY) ?: "" - UpdaterService.downloadUpdate(appContext, url) + UpdaterService.start(appContext, url) } } .negativeButton(R.string.ignore) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt index 2487b495bd..61d62f1f98 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt @@ -75,7 +75,7 @@ class SettingsMainController : SettingsController() { iconRes = R.drawable.ic_info_black_24dp iconTint = tintColor titleRes = R.string.about - onClick { navigateTo(SettingsAboutController()) } + onClick { navigateTo(AboutController()) } } } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {