mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	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:
		| @@ -437,5 +437,19 @@ class NotificationReceiver : BroadcastReceiver() { | ||||
|             } | ||||
|             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 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. | ||||
|      * | ||||
| @@ -59,7 +65,10 @@ object Notifications { | ||||
|                     setShowBadge(false) | ||||
|                 }, | ||||
|                 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) | ||||
|     } | ||||
|   | ||||
| @@ -107,6 +107,8 @@ object PreferenceKeys { | ||||
|  | ||||
|     const val automaticUpdates = "automatic_updates" | ||||
|  | ||||
|     const val automaticExtUpdates = "automatic_ext_updates" | ||||
|  | ||||
|     const val startScreen = "start_screen" | ||||
|  | ||||
|     const val useBiometricLock = "use_biometric_lock" | ||||
|   | ||||
| @@ -190,6 +190,12 @@ class PreferencesHelper(val context: Context) { | ||||
|  | ||||
|     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 downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false) | ||||
|   | ||||
| @@ -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" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,11 +17,15 @@ import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges | ||||
| import eu.davidea.flexibleadapter.FlexibleAdapter | ||||
| import eu.davidea.flexibleadapter.items.IFlexible | ||||
| 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.ui.base.controller.NucleusController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import kotlinx.android.synthetic.main.extension_controller.ext_recycler | ||||
| import kotlinx.android.synthetic.main.extension_controller.ext_swipe_refresh | ||||
| import kotlinx.android.synthetic.main.extension_controller.* | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| /** | ||||
|  * Controller to manage the catalogues available in the app. | ||||
| @@ -86,6 +90,13 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(), | ||||
|                         .popChangeHandler(SettingsExtensionsFadeChangeHandler()) | ||||
|                         .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) | ||||
|     } | ||||
| @@ -138,6 +149,10 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(), | ||||
|  | ||||
|         // Fixes problem with the overflow icon showing up in lieu of search | ||||
|         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 { | ||||
|   | ||||
| @@ -13,6 +13,9 @@ import com.bluelinelabs.conductor.RouterTransaction | ||||
| import eu.kanade.tachiyomi.Migrations | ||||
| import eu.kanade.tachiyomi.R | ||||
| 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.controller.DialogController | ||||
| 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.global_search.CatalogueSearchController | ||||
| 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.manga.MangaController | ||||
| import eu.kanade.tachiyomi.ui.more.MoreController | ||||
| import eu.kanade.tachiyomi.ui.recent.history.HistoryController | ||||
| import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController | ||||
| import kotlinx.android.synthetic.main.main_activity.appbar | ||||
| import kotlinx.android.synthetic.main.main_activity.bottom_nav | ||||
| import kotlinx.android.synthetic.main.main_activity.drawer | ||||
| import kotlinx.android.synthetic.main.main_activity.tabs | ||||
| import kotlinx.android.synthetic.main.main_activity.toolbar | ||||
| import java.util.Date | ||||
| import java.util.concurrent.TimeUnit | ||||
| import kotlinx.android.synthetic.main.main_activity.* | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.GlobalScope | ||||
| import kotlinx.coroutines.launch | ||||
| import timber.log.Timber | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| class MainActivity : BaseActivity() { | ||||
|  | ||||
| @@ -133,6 +140,10 @@ class MainActivity : BaseActivity() { | ||||
|                 ChangelogDialogController().showDialog(router) | ||||
|             } | ||||
|         } | ||||
|         preferences.extensionUpdatesCount().asObservable().subscribe { | ||||
|             setExtensionsBadge() | ||||
|         } | ||||
|         setExtensionsBadge() | ||||
|     } | ||||
|  | ||||
|     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 { | ||||
|         val notificationId = intent.getIntExtra("notificationId", -1) | ||||
|         if (notificationId > -1) { | ||||
| @@ -152,6 +194,10 @@ class MainActivity : BaseActivity() { | ||||
|             SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_updates) | ||||
|             SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_history) | ||||
|             SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_catalogues) | ||||
|             SHORTCUT_EXTENSIONS -> { | ||||
|                 setSelectedDrawerItem(R.id.nav_more) | ||||
|                 router.pushController(ExtensionController().withFadeTransaction()) | ||||
|             } | ||||
|             SHORTCUT_MANGA -> { | ||||
|                 val extras = intent.extras ?: return false | ||||
|                 setSelectedDrawerItem(R.id.nav_library) | ||||
| @@ -267,6 +313,7 @@ class MainActivity : BaseActivity() { | ||||
|         const val SHORTCUT_CATALOGUES = "eu.kanade.tachiyomi.SHOW_CATALOGUES" | ||||
|         const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS" | ||||
|         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_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.setting.SettingsController | ||||
| 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.iconTint | ||||
| import eu.kanade.tachiyomi.util.preference.onClick | ||||
| @@ -25,7 +26,7 @@ class MoreController : SettingsController(), RootController { | ||||
|  | ||||
|         val tintColor = context.getResourceColor(R.attr.colorAccent) | ||||
|  | ||||
|         preference { | ||||
|         extensionPreference { | ||||
|             titleRes = R.string.label_extensions | ||||
|             iconRes = R.drawable.ic_extension_24dp | ||||
|             iconTint = tintColor | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import androidx.preference.PreferenceManager | ||||
| import androidx.preference.PreferenceScreen | ||||
| import androidx.preference.SwitchPreferenceCompat | ||||
| 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.SwitchPreferenceCategory | ||||
|  | ||||
| @@ -56,6 +57,10 @@ inline fun PreferenceGroup.multiSelectListPreference(block: (@DSL MultiSelectLis | ||||
|     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 { | ||||
|     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:showAsAction="ifRoom" /> | ||||
|  | ||||
|     <item | ||||
|         android:id="@+id/action_auto_check" | ||||
|         android:title="@string/action_auto_check_extensions" | ||||
|         android:checkable="true" | ||||
|         app:showAsAction="never"/> | ||||
|  | ||||
| </menu> | ||||
|   | ||||
| @@ -211,6 +211,7 @@ | ||||
|     <string name="ext_version_info">Version: %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="action_auto_check_extensions">Auto-check for updates</string> | ||||
|  | ||||
|       <!-- Reader section --> | ||||
|     <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_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--> | ||||
|     <string name="description_backdrop">Backdrop image of manga</string> | ||||
|     <string name="description_cover">Cover of manga</string> | ||||
| @@ -594,5 +601,6 @@ | ||||
|     <string name="channel_library">Library</string> | ||||
|     <string name="channel_downloader">Downloader</string> | ||||
|     <string name="channel_new_chapters">Chapter updates</string> | ||||
|     <string name="channel_ext_updates">Extension Updates</string> | ||||
|  | ||||
| </resources> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user