Option to auto check for extension updates (#2680)
* Option to auto check for extension updates * Addressing comments * Added foreground check for extensions * Added Extension Preference widget
This commit is contained in:
@@ -162,6 +162,7 @@ class ExtensionManager(
|
||||
*/
|
||||
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
|
||||
if (availableExtensions.isEmpty()) {
|
||||
preferences.extensionUpdatesCount().set(0)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -186,6 +187,7 @@ class ExtensionManager(
|
||||
if (changed) {
|
||||
installedExtensions = mutInstalledExtensions
|
||||
}
|
||||
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,10 +318,12 @@ class ExtensionManager(
|
||||
|
||||
override fun onExtensionInstalled(extension: Extension.Installed) {
|
||||
registerNewExtension(extension.withUpdateCheck())
|
||||
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||
}
|
||||
|
||||
override fun onExtensionUpdated(extension: Extension.Installed) {
|
||||
registerUpdatedExtension(extension.withUpdateCheck())
|
||||
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||
}
|
||||
|
||||
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
||||
@@ -328,6 +332,7 @@ class ExtensionManager(
|
||||
|
||||
override fun onPackageUninstalled(pkgName: String) {
|
||||
unregisterExtension(pkgName)
|
||||
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package eu.kanade.tachiyomi.extension
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.util.system.notification
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams) {
|
||||
|
||||
override suspend fun doWork(): Result = coroutineScope {
|
||||
val pendingUpdates = try {
|
||||
ExtensionGithubApi().checkForUpdates(context)
|
||||
} catch (e: Exception) {
|
||||
return@coroutineScope Result.failure()
|
||||
}
|
||||
if (pendingUpdates.isNotEmpty()) {
|
||||
val names = pendingUpdates.map { it.name }
|
||||
NotificationManagerCompat.from(context).apply {
|
||||
notify(Notifications.ID_UPDATES_TO_EXTS,
|
||||
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
||||
setContentTitle(
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.update_check_notification_ext_updates,
|
||||
names.size,
|
||||
names.size
|
||||
)
|
||||
)
|
||||
val extNames = names.joinToString(", ")
|
||||
setContentText(extNames)
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
|
||||
setSmallIcon(R.drawable.ic_extension_24dp)
|
||||
setContentIntent(
|
||||
NotificationReceiver.openExtensionsPendingActivity(
|
||||
context
|
||||
)
|
||||
)
|
||||
setAutoCancel(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ExtensionUpdate"
|
||||
|
||||
fun setupTask(context: Context, forceAutoUpdateJob: Boolean? = null) {
|
||||
val preferences = Injekt.get<PreferencesHelper>()
|
||||
val autoUpdateJob = forceAutoUpdateJob ?: preferences.automaticExtUpdates().getOrDefault()
|
||||
if (autoUpdateJob) {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
|
||||
val request = PeriodicWorkRequestBuilder<ExtensionUpdateJob>(
|
||||
12, TimeUnit.HOURS,
|
||||
1, TimeUnit.HOURS)
|
||||
.addTag(TAG)
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
||||
} else {
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.extension.api
|
||||
|
||||
import android.content.Context
|
||||
import com.github.salomonbrys.kotson.fromJson
|
||||
import com.github.salomonbrys.kotson.get
|
||||
import com.github.salomonbrys.kotson.int
|
||||
@@ -7,6 +8,7 @@ import com.github.salomonbrys.kotson.string
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
@@ -23,13 +25,41 @@ internal class ExtensionGithubApi {
|
||||
private val gson: Gson by injectLazy()
|
||||
|
||||
suspend fun findExtensions(): List<Extension.Available> {
|
||||
val call = GET("$REPO_URL/index.json")
|
||||
val call = GET(EXT_URL)
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
parseResponse(network.client.newCall(call).await())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkForUpdates(context: Context): List<Extension.Installed> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val call = GET(EXT_URL)
|
||||
val response = network.client.newCall(call).await()
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val extensions = parseResponse(response)
|
||||
val extensionsWithUpdate = mutableListOf<Extension.Installed>()
|
||||
|
||||
val installedExtensions = ExtensionLoader.loadExtensions(context)
|
||||
.filterIsInstance<LoadResult.Success>()
|
||||
.map { it.extension }
|
||||
for (installedExt in installedExtensions) {
|
||||
val pkgName = installedExt.pkgName
|
||||
val availableExt = extensions.find { it.pkgName == pkgName } ?: continue
|
||||
|
||||
val hasUpdate = availableExt.versionCode > installedExt.versionCode
|
||||
if (hasUpdate) extensionsWithUpdate.add(installedExt)
|
||||
}
|
||||
|
||||
extensionsWithUpdate
|
||||
} else {
|
||||
response.close()
|
||||
throw Exception("Failed to get extensions")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseResponse(response: Response): List<Extension.Available> {
|
||||
val text = response.body?.use { it.string() } ?: return emptyList()
|
||||
|
||||
@@ -60,5 +90,6 @@ internal class ExtensionGithubApi {
|
||||
|
||||
companion object {
|
||||
private const val REPO_URL = "https://raw.githubusercontent.com/inorichi/tachiyomi-extensions/repo"
|
||||
private const val EXT_URL = "$REPO_URL/index.json"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user