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:
parent
fd4876be24
commit
9585f9a1a6
16 changed files with 308 additions and 10 deletions
|
@ -437,5 +437,19 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [PendingIntent] that opens the extensions controller.
|
||||||
|
*
|
||||||
|
* @param context context of application
|
||||||
|
*/
|
||||||
|
internal fun openExtensionsPendingActivity(context: Context): PendingIntent {
|
||||||
|
val newIntent =
|
||||||
|
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_EXTENSIONS)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
context, 0, newIntent, PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,12 @@ object Notifications {
|
||||||
const val ID_NEW_CHAPTERS = -301
|
const val ID_NEW_CHAPTERS = -301
|
||||||
const val GROUP_NEW_CHAPTERS = "eu.kanade.tachiyomi.NEW_CHAPTERS"
|
const val GROUP_NEW_CHAPTERS = "eu.kanade.tachiyomi.NEW_CHAPTERS"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification channel and ids used by the library updater.
|
||||||
|
*/
|
||||||
|
const val CHANNEL_UPDATES_TO_EXTS = "updates_ext_channel"
|
||||||
|
const val ID_UPDATES_TO_EXTS = -401
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the notification channels introduced in Android Oreo.
|
* Creates the notification channels introduced in Android Oreo.
|
||||||
*
|
*
|
||||||
|
@ -59,7 +65,10 @@ object Notifications {
|
||||||
setShowBadge(false)
|
setShowBadge(false)
|
||||||
},
|
},
|
||||||
NotificationChannel(CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters),
|
NotificationChannel(CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters),
|
||||||
NotificationManager.IMPORTANCE_DEFAULT)
|
NotificationManager.IMPORTANCE_DEFAULT),
|
||||||
|
NotificationChannel(CHANNEL_UPDATES_TO_EXTS, context.getString(R.string.channel_ext_updates),
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
)
|
||||||
)
|
)
|
||||||
context.notificationManager.createNotificationChannels(channels)
|
context.notificationManager.createNotificationChannels(channels)
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,8 @@ object PreferenceKeys {
|
||||||
|
|
||||||
const val automaticUpdates = "automatic_updates"
|
const val automaticUpdates = "automatic_updates"
|
||||||
|
|
||||||
|
const val automaticExtUpdates = "automatic_ext_updates"
|
||||||
|
|
||||||
const val startScreen = "start_screen"
|
const val startScreen = "start_screen"
|
||||||
|
|
||||||
const val useBiometricLock = "use_biometric_lock"
|
const val useBiometricLock = "use_biometric_lock"
|
||||||
|
|
|
@ -190,6 +190,12 @@ class PreferencesHelper(val context: Context) {
|
||||||
|
|
||||||
fun automaticUpdates() = prefs.getBoolean(Keys.automaticUpdates, true)
|
fun automaticUpdates() = prefs.getBoolean(Keys.automaticUpdates, true)
|
||||||
|
|
||||||
|
fun automaticExtUpdates() = rxPrefs.getBoolean(Keys.automaticExtUpdates, false)
|
||||||
|
|
||||||
|
fun extensionUpdatesCount() = rxPrefs.getInteger("ext_updates_count", 0)
|
||||||
|
|
||||||
|
fun lastExtCheck() = rxPrefs.getLong("last_ext_check", 0)
|
||||||
|
|
||||||
fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", emptySet())
|
fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", emptySet())
|
||||||
|
|
||||||
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
|
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
|
||||||
|
|
|
@ -162,6 +162,7 @@ class ExtensionManager(
|
||||||
*/
|
*/
|
||||||
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
|
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
|
||||||
if (availableExtensions.isEmpty()) {
|
if (availableExtensions.isEmpty()) {
|
||||||
|
preferences.extensionUpdatesCount().set(0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +187,7 @@ class ExtensionManager(
|
||||||
if (changed) {
|
if (changed) {
|
||||||
installedExtensions = mutInstalledExtensions
|
installedExtensions = mutInstalledExtensions
|
||||||
}
|
}
|
||||||
|
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -316,10 +318,12 @@ class ExtensionManager(
|
||||||
|
|
||||||
override fun onExtensionInstalled(extension: Extension.Installed) {
|
override fun onExtensionInstalled(extension: Extension.Installed) {
|
||||||
registerNewExtension(extension.withUpdateCheck())
|
registerNewExtension(extension.withUpdateCheck())
|
||||||
|
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onExtensionUpdated(extension: Extension.Installed) {
|
override fun onExtensionUpdated(extension: Extension.Installed) {
|
||||||
registerUpdatedExtension(extension.withUpdateCheck())
|
registerUpdatedExtension(extension.withUpdateCheck())
|
||||||
|
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
||||||
|
@ -328,6 +332,7 @@ class ExtensionManager(
|
||||||
|
|
||||||
override fun onPackageUninstalled(pkgName: String) {
|
override fun onPackageUninstalled(pkgName: String) {
|
||||||
unregisterExtension(pkgName)
|
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
|
package eu.kanade.tachiyomi.extension.api
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import com.github.salomonbrys.kotson.fromJson
|
import com.github.salomonbrys.kotson.fromJson
|
||||||
import com.github.salomonbrys.kotson.get
|
import com.github.salomonbrys.kotson.get
|
||||||
import com.github.salomonbrys.kotson.int
|
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.Gson
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
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.extension.util.ExtensionLoader
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
|
@ -23,13 +25,41 @@ internal class ExtensionGithubApi {
|
||||||
private val gson: Gson by injectLazy()
|
private val gson: Gson by injectLazy()
|
||||||
|
|
||||||
suspend fun findExtensions(): List<Extension.Available> {
|
suspend fun findExtensions(): List<Extension.Available> {
|
||||||
val call = GET("$REPO_URL/index.json")
|
val call = GET(EXT_URL)
|
||||||
|
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
parseResponse(network.client.newCall(call).await())
|
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> {
|
private fun parseResponse(response: Response): List<Extension.Available> {
|
||||||
val text = response.body?.use { it.string() } ?: return emptyList()
|
val text = response.body?.use { it.string() } ?: return emptyList()
|
||||||
|
|
||||||
|
@ -60,5 +90,6 @@ internal class ExtensionGithubApi {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val REPO_URL = "https://raw.githubusercontent.com/inorichi/tachiyomi-extensions/repo"
|
private const val REPO_URL = "https://raw.githubusercontent.com/inorichi/tachiyomi-extensions/repo"
|
||||||
|
private const val EXT_URL = "$REPO_URL/index.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,15 @@ import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
|
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import kotlinx.android.synthetic.main.extension_controller.ext_recycler
|
import kotlinx.android.synthetic.main.extension_controller.*
|
||||||
import kotlinx.android.synthetic.main.extension_controller.ext_swipe_refresh
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller to manage the catalogues available in the app.
|
* Controller to manage the catalogues available in the app.
|
||||||
|
@ -86,6 +90,13 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(),
|
||||||
.popChangeHandler(SettingsExtensionsFadeChangeHandler())
|
.popChangeHandler(SettingsExtensionsFadeChangeHandler())
|
||||||
.pushChangeHandler(FadeChangeHandler()))
|
.pushChangeHandler(FadeChangeHandler()))
|
||||||
}
|
}
|
||||||
|
R.id.action_auto_check -> {
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
val preferences: PreferencesHelper = Injekt.get()
|
||||||
|
preferences.automaticExtUpdates().set(item.isChecked)
|
||||||
|
ExtensionUpdateJob.setupTask(activity!!, item.isChecked)
|
||||||
|
}
|
||||||
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
@ -138,6 +149,10 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(),
|
||||||
|
|
||||||
// Fixes problem with the overflow icon showing up in lieu of search
|
// Fixes problem with the overflow icon showing up in lieu of search
|
||||||
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
||||||
|
|
||||||
|
val autoItem = menu.findItem(R.id.action_auto_check)
|
||||||
|
val preferences: PreferencesHelper = Injekt.get()
|
||||||
|
autoItem.isChecked = preferences.automaticExtUpdates().getOrDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(view: View, position: Int): Boolean {
|
override fun onItemClick(view: View, position: Int): Boolean {
|
||||||
|
|
|
@ -13,6 +13,9 @@ import com.bluelinelabs.conductor.RouterTransaction
|
||||||
import eu.kanade.tachiyomi.Migrations
|
import eu.kanade.tachiyomi.Migrations
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
|
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.ui.base.activity.BaseActivity
|
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
||||||
|
@ -23,16 +26,20 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
|
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
|
||||||
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
||||||
import eu.kanade.tachiyomi.ui.download.DownloadController
|
import eu.kanade.tachiyomi.ui.download.DownloadController
|
||||||
|
import eu.kanade.tachiyomi.ui.extension.ExtensionController
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.more.MoreController
|
import eu.kanade.tachiyomi.ui.more.MoreController
|
||||||
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
|
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
|
||||||
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
|
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
|
||||||
import kotlinx.android.synthetic.main.main_activity.appbar
|
import java.util.Date
|
||||||
import kotlinx.android.synthetic.main.main_activity.bottom_nav
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlinx.android.synthetic.main.main_activity.drawer
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
import kotlinx.android.synthetic.main.main_activity.tabs
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.android.synthetic.main.main_activity.toolbar
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class MainActivity : BaseActivity() {
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
|
@ -133,6 +140,10 @@ class MainActivity : BaseActivity() {
|
||||||
ChangelogDialogController().showDialog(router)
|
ChangelogDialogController().showDialog(router)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
preferences.extensionUpdatesCount().asObservable().subscribe {
|
||||||
|
setExtensionsBadge()
|
||||||
|
}
|
||||||
|
setExtensionsBadge()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
@ -141,6 +152,37 @@ class MainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setExtensionsBadge() {
|
||||||
|
val updates = preferences.extensionUpdatesCount().getOrDefault()
|
||||||
|
if (updates > 0) {
|
||||||
|
val badge = bottom_nav.getOrCreateBadge(R.id.nav_more)
|
||||||
|
badge.number = updates
|
||||||
|
} else {
|
||||||
|
bottom_nav.removeBadge(R.id.nav_more)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
getExtensionUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExtensionUpdates() {
|
||||||
|
if (Date().time >= preferences.lastExtCheck().getOrDefault() +
|
||||||
|
TimeUnit.HOURS.toMillis(2)) {
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
|
try {
|
||||||
|
val pendingUpdates = ExtensionGithubApi().checkForUpdates(this@MainActivity)
|
||||||
|
preferences.extensionUpdatesCount().set(pendingUpdates.size)
|
||||||
|
preferences.lastExtCheck().set(Date().time)
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleIntentAction(intent: Intent): Boolean {
|
private fun handleIntentAction(intent: Intent): Boolean {
|
||||||
val notificationId = intent.getIntExtra("notificationId", -1)
|
val notificationId = intent.getIntExtra("notificationId", -1)
|
||||||
if (notificationId > -1) {
|
if (notificationId > -1) {
|
||||||
|
@ -152,6 +194,10 @@ class MainActivity : BaseActivity() {
|
||||||
SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_updates)
|
SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_updates)
|
||||||
SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_history)
|
SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_history)
|
||||||
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_catalogues)
|
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_catalogues)
|
||||||
|
SHORTCUT_EXTENSIONS -> {
|
||||||
|
setSelectedDrawerItem(R.id.nav_more)
|
||||||
|
router.pushController(ExtensionController().withFadeTransaction())
|
||||||
|
}
|
||||||
SHORTCUT_MANGA -> {
|
SHORTCUT_MANGA -> {
|
||||||
val extras = intent.extras ?: return false
|
val extras = intent.extras ?: return false
|
||||||
setSelectedDrawerItem(R.id.nav_library)
|
setSelectedDrawerItem(R.id.nav_library)
|
||||||
|
@ -267,6 +313,7 @@ class MainActivity : BaseActivity() {
|
||||||
const val SHORTCUT_CATALOGUES = "eu.kanade.tachiyomi.SHOW_CATALOGUES"
|
const val SHORTCUT_CATALOGUES = "eu.kanade.tachiyomi.SHOW_CATALOGUES"
|
||||||
const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS"
|
const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS"
|
||||||
const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA"
|
const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA"
|
||||||
|
const val SHORTCUT_EXTENSIONS = "eu.kanade.tachiyomi.EXTENSIONS"
|
||||||
|
|
||||||
const val INTENT_SEARCH = "eu.kanade.tachiyomi.SEARCH"
|
const val INTENT_SEARCH = "eu.kanade.tachiyomi.SEARCH"
|
||||||
const val INTENT_SEARCH_QUERY = "query"
|
const val INTENT_SEARCH_QUERY = "query"
|
||||||
|
|
|
@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.ui.extension.ExtensionController
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationController
|
import eu.kanade.tachiyomi.ui.migration.MigrationController
|
||||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||||
|
import eu.kanade.tachiyomi.util.preference.extensionPreference
|
||||||
import eu.kanade.tachiyomi.util.preference.iconRes
|
import eu.kanade.tachiyomi.util.preference.iconRes
|
||||||
import eu.kanade.tachiyomi.util.preference.iconTint
|
import eu.kanade.tachiyomi.util.preference.iconTint
|
||||||
import eu.kanade.tachiyomi.util.preference.onClick
|
import eu.kanade.tachiyomi.util.preference.onClick
|
||||||
|
@ -25,7 +26,7 @@ class MoreController : SettingsController(), RootController {
|
||||||
|
|
||||||
val tintColor = context.getResourceColor(R.attr.colorAccent)
|
val tintColor = context.getResourceColor(R.attr.colorAccent)
|
||||||
|
|
||||||
preference {
|
extensionPreference {
|
||||||
titleRes = R.string.label_extensions
|
titleRes = R.string.label_extensions
|
||||||
iconRes = R.drawable.ic_extension_24dp
|
iconRes = R.drawable.ic_extension_24dp
|
||||||
iconTint = tintColor
|
iconTint = tintColor
|
||||||
|
|
|
@ -13,6 +13,7 @@ import androidx.preference.PreferenceManager
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
||||||
|
import eu.kanade.tachiyomi.widget.preference.ExtensionPreference
|
||||||
import eu.kanade.tachiyomi.widget.preference.IntListPreference
|
import eu.kanade.tachiyomi.widget.preference.IntListPreference
|
||||||
import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
|
import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
|
||||||
|
|
||||||
|
@ -56,6 +57,10 @@ inline fun PreferenceGroup.multiSelectListPreference(block: (@DSL MultiSelectLis
|
||||||
return initThenAdd(MultiSelectListPreference(context), block).also(::initDialog)
|
return initThenAdd(MultiSelectListPreference(context), block).also(::initDialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun PreferenceGroup.extensionPreference(block: (@DSL Preference).() -> Unit): ExtensionPreference {
|
||||||
|
return initThenAdd(ExtensionPreference(context), block)
|
||||||
|
}
|
||||||
|
|
||||||
inline fun PreferenceScreen.preferenceCategory(block: (@DSL PreferenceCategory).() -> Unit): PreferenceCategory {
|
inline fun PreferenceScreen.preferenceCategory(block: (@DSL PreferenceCategory).() -> Unit): PreferenceCategory {
|
||||||
return addThenInit(PreferenceCategory(context), block)
|
return addThenInit(PreferenceCategory(context), block)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package eu.kanade.tachiyomi.widget.preference
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceViewHolder
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
|
import kotlinx.android.synthetic.main.preference_update_text.view.*
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class ExtensionPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||||
|
Preference(context, attrs) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
widgetLayoutResource = R.layout.preference_update_text
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||||
|
super.onBindViewHolder(holder)
|
||||||
|
|
||||||
|
val extUpdateText = holder.itemView.textView
|
||||||
|
|
||||||
|
val updates = Injekt.get<PreferencesHelper>().extensionUpdatesCount().getOrDefault()
|
||||||
|
if (updates > 0) {
|
||||||
|
extUpdateText.text = updates.toString()
|
||||||
|
extUpdateText.visible()
|
||||||
|
} else {
|
||||||
|
extUpdateText.text = null
|
||||||
|
extUpdateText.gone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
app/src/main/res/drawable/round_textview_background.xml
Normal file
9
app/src/main/res/drawable/round_textview_background.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="13dp"/>
|
||||||
|
<size
|
||||||
|
android:height="25dp"
|
||||||
|
android:width="25dp" />
|
||||||
|
<solid android:color="@color/material_red_900"/>
|
||||||
|
</shape>
|
17
app/src/main/res/layout/preference_update_text.xml
Normal file
17
app/src/main/res/layout/preference_update_text.xml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||||
|
android:background="@drawable/round_textview_background"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="3"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingEnd="3dp"/>
|
|
@ -16,4 +16,10 @@
|
||||||
app:iconTint="?attr/colorOnPrimary"
|
app:iconTint="?attr/colorOnPrimary"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_auto_check"
|
||||||
|
android:title="@string/action_auto_check_extensions"
|
||||||
|
android:checkable="true"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -211,6 +211,7 @@
|
||||||
<string name="ext_version_info">Version: %1$s</string>
|
<string name="ext_version_info">Version: %1$s</string>
|
||||||
<string name="ext_language_info">Language: %1$s</string>
|
<string name="ext_language_info">Language: %1$s</string>
|
||||||
<string name="ext_empty_preferences">No preferences to edit for this extension</string>
|
<string name="ext_empty_preferences">No preferences to edit for this extension</string>
|
||||||
|
<string name="action_auto_check_extensions">Auto-check for updates</string>
|
||||||
|
|
||||||
<!-- Reader section -->
|
<!-- Reader section -->
|
||||||
<string name="pref_fullscreen">Fullscreen</string>
|
<string name="pref_fullscreen">Fullscreen</string>
|
||||||
|
@ -566,6 +567,12 @@
|
||||||
<string name="update_check_notification_download_error">Download error</string>
|
<string name="update_check_notification_download_error">Download error</string>
|
||||||
<string name="update_check_notification_update_available">Update available</string>
|
<string name="update_check_notification_update_available">Update available</string>
|
||||||
|
|
||||||
|
<!--Extension Updates Notifications-->
|
||||||
|
<plurals name="update_check_notification_ext_updates">
|
||||||
|
<item quantity="one">Extension update available</item>
|
||||||
|
<item quantity="other">%d extension updates available</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
<!--Content Description-->
|
<!--Content Description-->
|
||||||
<string name="description_backdrop">Backdrop image of manga</string>
|
<string name="description_backdrop">Backdrop image of manga</string>
|
||||||
<string name="description_cover">Cover of manga</string>
|
<string name="description_cover">Cover of manga</string>
|
||||||
|
@ -594,5 +601,6 @@
|
||||||
<string name="channel_library">Library</string>
|
<string name="channel_library">Library</string>
|
||||||
<string name="channel_downloader">Downloader</string>
|
<string name="channel_downloader">Downloader</string>
|
||||||
<string name="channel_new_chapters">Chapter updates</string>
|
<string name="channel_new_chapters">Chapter updates</string>
|
||||||
|
<string name="channel_ext_updates">Extension Updates</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Reference in a new issue