From 3ac6f5829ceb572c85d1da3575c6bb8965ace327 Mon Sep 17 00:00:00 2001 From: Jay Date: Sat, 21 Dec 2019 23:28:02 -0800 Subject: [PATCH] Auto check for extension updates Setting lives in Extensions -> Overflow menu Checks every day --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 + .../tachiyomi/data/backup/BackupCreatorJob.kt | 6 +- .../data/library/LibraryUpdateJob.kt | 8 +- .../data/notification/NotificationReceiver.kt | 15 ++ .../data/notification/Notifications.kt | 42 +++-- .../data/preference/PreferenceKeys.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../tachiyomi/data/updater/UpdaterJob.kt | 4 +- .../tachiyomi/extension/ExtensionUpdateJob.kt | 164 ++++++++++++++++++ .../ui/extension/ExtensionController.kt | 20 ++- .../kanade/tachiyomi/ui/main/MainActivity.kt | 23 ++- .../res/drawable-hdpi/ic_extension_update.png | Bin 0 -> 404 bytes .../res/drawable-mdpi/ic_extension_update.png | Bin 0 -> 284 bytes .../drawable-xhdpi/ic_extension_update.png | Bin 0 -> 516 bytes .../drawable-xxhdpi/ic_extension_update.png | Bin 0 -> 784 bytes .../drawable-xxxhdpi/ic_extension_update.png | Bin 0 -> 1150 bytes app/src/main/res/menu/extension_main.xml | 7 + app/src/main/res/values/strings.xml | 3 + 18 files changed, 274 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt create mode 100644 app/src/main/res/drawable-hdpi/ic_extension_update.png create mode 100644 app/src/main/res/drawable-mdpi/ic_extension_update.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_extension_update.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_extension_update.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_extension_update.png diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 3a1373558a..435780ffff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -15,6 +15,7 @@ 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.data.updater.UpdaterJob +import eu.kanade.tachiyomi.extension.ExtensionUpdateJob import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.LocaleHelper import org.acra.ACRA @@ -79,6 +80,7 @@ open class App : Application(), LifecycleObserver { LibraryUpdateJob.TAG -> LibraryUpdateJob() UpdaterJob.TAG -> UpdaterJob() BackupCreatorJob.TAG -> BackupCreatorJob() + ExtensionUpdateJob.TAG -> ExtensionUpdateJob() else -> null } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt index a1f5aca922..f18340ac52 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.concurrent.TimeUnit class BackupCreatorJob : Job() { @@ -25,10 +26,11 @@ class BackupCreatorJob : Job() { fun setupTask(prefInterval: Int? = null) { val preferences = Injekt.get() - val interval = prefInterval ?: preferences.backupInterval().getOrDefault() + val interval = (prefInterval ?: preferences.backupInterval().getOrDefault()).toLong() if (interval > 0) { JobRequest.Builder(TAG) - .setPeriodic(interval * 60 * 60 * 1000L, 10 * 60 * 1000) + .setPeriodic(TimeUnit.HOURS.toMillis(interval), TimeUnit.MINUTES.toMillis + (10)) .setUpdateCurrent(true) .build() .schedule() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index 2fb2e5d83a..3cad6a2206 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.concurrent.TimeUnit class LibraryUpdateJob : Job() { @@ -20,7 +21,8 @@ class LibraryUpdateJob : Job() { fun setupTask(prefInterval: Int? = null) { val preferences = Injekt.get() - val interval = prefInterval ?: preferences.libraryUpdateInterval().getOrDefault() + val interval = (prefInterval ?: preferences.libraryUpdateInterval().getOrDefault()) + .toLong() if (interval > 0) { val restrictions = preferences.libraryUpdateRestriction()!! val acRestriction = "ac" in restrictions @@ -30,7 +32,9 @@ class LibraryUpdateJob : Job() { JobRequest.NetworkType.CONNECTED JobRequest.Builder(TAG) - .setPeriodic(interval * 60 * 60 * 1000L, 10 * 60 * 1000) + .setPeriodic( + TimeUnit.HOURS.toMillis(interval), TimeUnit.MINUTES.toMillis + (10)) .setRequiredNetworkType(wifiRestriction) .setRequiresCharging(acRestriction) .setRequirementsEnforced(true) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 84e49f19b1..b067006710 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -395,6 +395,21 @@ class NotificationReceiver : BroadcastReceiver() { ) } + /** + * Returns [PendingIntent] that opens the extensions controller, + * + * @param context context of application + * @param manga manga of chapter + */ + 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 + ) + } + /** * Returns [PendingIntent] that marks a chapter as read and deletes it if preferred * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt index b99c44cadd..03555be83b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt @@ -38,6 +38,11 @@ object Notifications { const val CHANNEL_NEW_CHAPTERS = "new_chapters_channel" 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. @@ -48,18 +53,31 @@ object Notifications { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return val channels = listOf( - NotificationChannel(CHANNEL_COMMON, context.getString(R.string.channel_common), - NotificationManager.IMPORTANCE_LOW), - NotificationChannel(CHANNEL_LIBRARY, context.getString(R.string.channel_library_updates), - NotificationManager.IMPORTANCE_LOW).apply { - setShowBadge(false) - }, - NotificationChannel(CHANNEL_DOWNLOADER, context.getString(R.string.channel_downloader), - NotificationManager.IMPORTANCE_LOW).apply { - setShowBadge(false) - }, - NotificationChannel(CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters), - NotificationManager.IMPORTANCE_DEFAULT) + NotificationChannel( + CHANNEL_COMMON, + context.getString(R.string.channel_common), + NotificationManager.IMPORTANCE_LOW + ), NotificationChannel( + CHANNEL_LIBRARY, + context.getString(R.string.channel_library_updates), + NotificationManager.IMPORTANCE_LOW + ).apply { + setShowBadge(false) + }, NotificationChannel( + CHANNEL_DOWNLOADER, + context.getString(R.string.channel_downloader), + NotificationManager.IMPORTANCE_LOW + ).apply { + setShowBadge(false) + }, NotificationChannel( + CHANNEL_UPDATES_TO_EXTS, + context.getString(R.string.channel_ext_updates), + NotificationManager.IMPORTANCE_DEFAULT + ), NotificationChannel( + CHANNEL_NEW_CHAPTERS, + context.getString(R.string.channel_new_chapters), + NotificationManager.IMPORTANCE_DEFAULT + ) ) context.notificationManager.createNotificationChannels(channels) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 4668ac1972..63ffd7898e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -101,6 +101,8 @@ object PreferenceKeys { const val automaticUpdates = "automatic_updates" + const val automaticExtUpdates = "automatic_ext_updates" + const val startScreen = "start_screen" const val downloadNew = "download_new" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index fe1269c6f9..63e99e6377 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -164,6 +164,8 @@ class PreferencesHelper(val context: Context) { fun automaticUpdates() = prefs.getBoolean(Keys.automaticUpdates, false) + fun automaticExtUpdates() = rxPrefs.getBoolean(Keys.automaticExtUpdates, false) + fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", mutableSetOf()) fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt index deda43a7a9..40ae3ebe9f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt @@ -9,8 +9,8 @@ import com.evernote.android.job.JobManager import com.evernote.android.job.JobRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.Notifications -import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.util.notificationManager +import java.util.concurrent.TimeUnit class UpdaterJob : Job() { @@ -54,7 +54,7 @@ class UpdaterJob : Job() { fun setupTask() { JobRequest.Builder(TAG) - .setPeriodic(24 * 60 * 60 * 1000, 60 * 60 * 1000) + .setPeriodic(TimeUnit.DAYS.toMillis(1), TimeUnit.HOURS.toMillis(1)) .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) .setRequirementsEnforced(true) .setUpdateCurrent(true) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt new file mode 100644 index 0000000000..9f506fea53 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt @@ -0,0 +1,164 @@ +package eu.kanade.tachiyomi.extension + + +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import com.evernote.android.job.Job +import com.evernote.android.job.JobManager +import com.evernote.android.job.JobRequest +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.notification.NotificationReceiver +import eu.kanade.tachiyomi.data.notification.Notifications +import eu.kanade.tachiyomi.util.notification +import rx.Observable +import rx.schedulers.Schedulers +import timber.log.Timber +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.util.concurrent.TimeUnit + +class ExtensionUpdateJob : Job() { + + override fun onRunJob(params: Params): Result { + val extensionManager: ExtensionManager = Injekt.get() + extensionManager.findAvailableExtensions() + /*return extensionManager.getInstalledExtensionsObservable() + .map { list -> + val pendingUpdates = list.filter { it.hasUpdate } + 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.getString( + R.string.update_check_notification_ext_updates, names.size + ) + ) + val extNames = if (names.size > 5) { + "${names.take(4).joinToString(", ")}, " + context.getString( + R.string.notification_and_n_more, (names.size - 4) + ) + } else names.joinToString(", ") + setContentText(extNames) + setSmallIcon(R.drawable.ic_extension_update) + color = ContextCompat.getColor(context, R.color.colorAccentLight) + setContentIntent( + NotificationReceiver.openExtensionsPendingActivity( + context + ) + ) + setAutoCancel(true) + }) + } + } + Result.SUCCESS + } + .onErrorReturn { Result.FAILURE } + // Sadly, the task needs to be synchronous. + .toBlocking() + .single()*/ + Observable.defer { + extensionManager.getInstalledExtensionsObservable().map { list -> + val pendingUpdates = list.filter { it.hasUpdate } + 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.getString( + R.string.update_check_notification_ext_updates, names.size + ) + ) + val extNames = if (names.size > 5) { + "${names.take(4).joinToString(", ")}, " + context.getString( + R.string.notification_and_n_more, (names.size - 4) + ) + } else names.joinToString(", ") + setContentText(extNames) + setSmallIcon(R.drawable.ic_extension_update) + color = ContextCompat.getColor(context, R.color.colorAccentLight) + setContentIntent( + NotificationReceiver.openExtensionsPendingActivity( + context + ) + ) + setAutoCancel(true) + }) + } + } + Result.SUCCESS + }.onErrorReturn { Result.FAILURE } + }.subscribeOn(Schedulers.io()) + .subscribe({ + }, { + Timber.e(it) + }, { + }) + return Result.SUCCESS + } + + /*fun runStuff(context: Context) { + val extensionManager: ExtensionManager = Injekt.get() + extensionManager.findAvailableExtensions() + Observable.defer { + extensionManager.getInstalledExtensionsObservable().map { list -> + val pendingUpdates = list.filter { it.hasUpdate } + 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.getString( + R.string.update_check_notification_ext_updates, names.size + ) + ) + val extNames = if (names.size > 5) { + "${names.take(4).joinToString(", ")}, " + context.getString( + R.string.notification_and_n_more, (names.size - 4) + ) + } else names.joinToString(", ") + setContentText(extNames) + setSmallIcon(R.drawable.ic_extension_update) + color = ContextCompat.getColor(context, R.color.colorAccentLight) + setContentIntent( + NotificationReceiver.openExtensionsPendingActivity( + context + ) + ) + setAutoCancel(true) + }) + } + } + Result.SUCCESS + }.onErrorReturn { Result.FAILURE } + }.subscribeOn(Schedulers.io()) + .subscribe({ + }, { + Timber.e(it) + }, { + }) + }*/ + + + + companion object { + const val TAG = "ExtensionUpdate" + + fun setupTask() { + JobRequest.Builder(TAG).setPeriodic(TimeUnit.DAYS.toMillis(1), + TimeUnit.HOURS.toMillis(1)) + .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) + .setRequirementsEnforced(true) + .setUpdateCurrent(true) + .build().schedule() + } + + fun cancelTask() { + JobManager.instance().cancelAllForTag(TAG) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt index cb9c5dbb28..3cd347c5d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt @@ -16,12 +16,16 @@ 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 eu.kanade.tachiyomi.util.RecyclerWindowInsetsListener 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,16 @@ open class ExtensionController : NucleusController(), .popChangeHandler(SettingsExtensionsFadeChangeHandler()) .pushChangeHandler(FadeChangeHandler())) } + R.id.action_auto_check -> { + item.isChecked = !item.isChecked + val preferences:PreferencesHelper = Injekt.get() + preferences.automaticExtUpdates().set(item.isChecked) + + if (item.isChecked) + ExtensionUpdateJob.setupTask() + else + ExtensionUpdateJob.cancelTask() + } else -> return super.onOptionsItemSelected(item) } return true @@ -139,6 +153,10 @@ open class ExtensionController : NucleusController(), // Fixes problem with the overflow icon showing up in lieu of search searchItem.fixExpand() + + 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 { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 95c4e10647..b787a69d3c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -9,16 +9,23 @@ import android.graphics.Rect import android.os.Build import android.os.Bundle import android.view.MotionEvent -import androidx.core.view.GravityCompat -import androidx.appcompat.app.AppCompatDelegate.* -import androidx.appcompat.graphics.drawable.DrawerArrowDrawable import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES +import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable import androidx.biometric.BiometricManager import androidx.core.graphics.ColorUtils -import com.bluelinelabs.conductor.* +import androidx.core.view.GravityCompat +import com.bluelinelabs.conductor.Conductor +import com.bluelinelabs.conductor.Controller +import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.Router +import com.bluelinelabs.conductor.RouterTransaction import com.google.android.material.snackbar.Snackbar import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.R @@ -26,7 +33,11 @@ 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.ui.base.activity.BaseActivity -import eu.kanade.tachiyomi.ui.base.controller.* +import eu.kanade.tachiyomi.ui.base.controller.DialogController +import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController +import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController +import eu.kanade.tachiyomi.ui.base.controller.TabbedController +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 @@ -282,6 +293,7 @@ class MainActivity : BaseActivity() { SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_drawer_recent_updates) SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read) SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues) + SHORTCUT_EXTENSIONS -> setSelectedDrawerItem(R.id.nav_drawer_extensions) SHORTCUT_MANGA -> { val extras = intent.extras ?: return false router.setRoot(RouterTransaction.with(MangaController(extras))) @@ -425,6 +437,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" diff --git a/app/src/main/res/drawable-hdpi/ic_extension_update.png b/app/src/main/res/drawable-hdpi/ic_extension_update.png new file mode 100644 index 0000000000000000000000000000000000000000..add124edace2b0268e952ec1530dbf8ade474aef GIT binary patch literal 404 zcmV;F0c-w=P)_7pwhnmAkiDTG+csdY&`A;421!^7}xejs%;=li?3qa<% zlk1?G|Fi)$FuCj(9#x! z-+)2}Nv#YJzd(}zO=|}o1!8p|7J*`6bI?>EP6T2d67;(P@m?Ul4aEOwmF+<}$bv+3xPW*KEz6}1CrC_t@c;k+H4ba| zj@)udnh+J1#QU7}k+H*{_x2wj$cOOj8;bq-qb~nLAlXraRjj2ihI=1lp`Cy@(?|WN zhz+csb>drfD!7hhI>_)IDf9T$7qF2>q0X<9Rk$O-x>tz13P^PdaqBS@+6!nu3VhA^ zNz+7dg6zrFofnKJ#&G{*Dzuf`>f>;xdCDe_O$mkqubB*u<+l1d{F(L0gKg8(h961- z^O=)4vs^z3DWtV@r7B5I;t)+{Im&M-cA)FEy+aL0$9CqAp(@UL&PVwiA9p-?G;5nE f-`Uk?6xkWNye=01FI}n$^e%&^tDnm{r-UW|VPj~l literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_extension_update.png b/app/src/main/res/drawable-xhdpi/ic_extension_update.png new file mode 100644 index 0000000000000000000000000000000000000000..0ebd6ca1db94784a756011b6c128fb07ba99d3f5 GIT binary patch literal 516 zcmV+f0{i`mP)4by zfh8~p#z3uL&Q@>)PQat;w@|%@r)zoW17%%(An7u$3ao&0a0W6#ZDdK8r=H;~#15iB z3ujCm9CH?O;F7aX=N4LUenPM}$^~a3r<{CI55JmH1@eyMuV9^RnIGoC17{&c$2iI+ z`xH@jxEuQ8^^}GSj+E>xCR^bZlP&d%DfE|oCQO(JZhQmNk70!lySeQE0000L=; zAwoh)JX@g^@mS~_|C9SAWXzmvD)*lA_5T0KZ#BK=ocYb|%=yl_r>>4+7=~dOh8b0i zG4)^}*aJGjVb65RSnQbUeak6;v!EY*0VU%zm6huK3L_ripL0J&=1Dpdb($+Yz5?rL9 zC^x~J3T@5<53(-kC)ns{cRTo&WkH|7d`G)$!P_hg`Un~w?XCfD#=4-A+$jSUcn;<| z+Fb=6R=IX~v}@oY33>tcfmX1n0!^;%7`!$aEU0qr3a}r%u5||`6hZc`*_3dT>VL8ri!6#AYBE@>2W8Z@NP_atzcf+#3^f=;IN9i&>&R1-ZClS{B~ zJ6F1{QIMVO-WP1eegrH|pl|yDYy$^{6X1~)!s==~O6f{zu;vKnLiPU&!0woof*-6hX&jCY_fzkxQs= zwKM6OziaUKUsg562&0Yz{Jbb}=n6kWSsb~h@OB~6(YWM7v%rds;qe)Yz> zpmnl&SS`;$+dl}pqEXNdFf-=5Eyn$n{(x)mUfO7sB^7mozMJT*R^D;l<`_Zm!G@TE z^ni(uJgMASeI<)n%T08st(?6vZ|;NneITx!HgKrcO+#OTh7>(uHsnK4QIVjc4?(+( zo&$9|eF!Q@p~xedF_L;sxQBXsy~!!7t3o>#zT1RwESzB&hG7^+aee_#a=$|C*qIps O0000%SAF^o&Zl%$cEX{20I9#DjoLWB_>gi_=dxjm439;PJB2oG{8 ziU$vrTqbvgafY+}zs~n?Ugn&=oqf((d$0e$elJFQueE?b3&$Fdg_pPzA1l=inoVneZj1ZGWzPkv7`0)SC4>37X`?urgxs(*b)p2;7dD{0+w3R&W>P>$*hPtlmfRVK5$c|s{* zH&_K$gB4&N7z;Y(VHi5uByP65t=tE1Oyc&V$O`xlKI=^9C-71`zZUe$@pMENl|N1q zA@jj;ZKqS~0q;!WnG9Vm_<`0R?C`L}8My@+kubwp@GJ(jp$E9+;)9; zAj^tnG$O<#t%CP3DTmxJ~e^g|vvwZ$a?P5S8{Xh9#^ z>S*k>3j(D2tw9g|B$*<}@pMGTB)WqUT7mtV2>h2z_t2;h)4C8@Ki7u3j9X-H|FztW7J1D^4Hhb$Azc)0l^Ar2R8Yb##rrhqRq+5xC$TJl3%AMecbG^ z-{*A0B9qzUl|w1ukk@Oq3OM6Kz)>IVlohWNE|#ai1=XN~M*+uu2sq$F>|&Go6MjP} zKu-CmS_L6@stibh%+;Ltcrc{Q{B)Y_4bTa{&EQ2;Z&9lDP;)QEQ1bYJ2jE5u>h%A4 zomG94W_!XzlXxLRrjb5Y+bE#IxbyJt3L6ECGP%EDIv=SIVqPm70;G~)tBL*#9BPcM zFZD5eg-PB8@FhMLNZ@O%Ec#9;H{pJf+d31r7r8ME!!QiPFbu;m48t^^e<&R%Lh^sh Ql>h($07*qoM6N<$g5%)}!T + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ce9c5a058b..1eef24d258 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -103,6 +103,7 @@ Log in Back Forward + Auto-check for updates Deleting… @@ -515,6 +516,7 @@ Download complete Download error Update available + %1$d extension updates available Backdrop image of manga @@ -541,6 +543,7 @@ Common Library Downloader + Extension Updates Updating Library New Chapters