Auto check for extension updates

Setting lives in Extensions -> Overflow menu
Checks every day
This commit is contained in:
Jay 2019-12-21 23:28:02 -08:00
parent 1f25030197
commit 3ac6f5829c
18 changed files with 274 additions and 24 deletions

View File

@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.updater.UpdaterJob import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.LocaleHelper import eu.kanade.tachiyomi.util.LocaleHelper
import org.acra.ACRA import org.acra.ACRA
@ -79,6 +80,7 @@ open class App : Application(), LifecycleObserver {
LibraryUpdateJob.TAG -> LibraryUpdateJob() LibraryUpdateJob.TAG -> LibraryUpdateJob()
UpdaterJob.TAG -> UpdaterJob() UpdaterJob.TAG -> UpdaterJob()
BackupCreatorJob.TAG -> BackupCreatorJob() BackupCreatorJob.TAG -> BackupCreatorJob()
ExtensionUpdateJob.TAG -> ExtensionUpdateJob()
else -> null else -> null
} }
} }

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class BackupCreatorJob : Job() { class BackupCreatorJob : Job() {
@ -25,10 +26,11 @@ class BackupCreatorJob : Job() {
fun setupTask(prefInterval: Int? = null) { fun setupTask(prefInterval: Int? = null) {
val preferences = Injekt.get<PreferencesHelper>() val preferences = Injekt.get<PreferencesHelper>()
val interval = prefInterval ?: preferences.backupInterval().getOrDefault() val interval = (prefInterval ?: preferences.backupInterval().getOrDefault()).toLong()
if (interval > 0) { if (interval > 0) {
JobRequest.Builder(TAG) JobRequest.Builder(TAG)
.setPeriodic(interval * 60 * 60 * 1000L, 10 * 60 * 1000) .setPeriodic(TimeUnit.HOURS.toMillis(interval), TimeUnit.MINUTES.toMillis
(10))
.setUpdateCurrent(true) .setUpdateCurrent(true)
.build() .build()
.schedule() .schedule()

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class LibraryUpdateJob : Job() { class LibraryUpdateJob : Job() {
@ -20,7 +21,8 @@ class LibraryUpdateJob : Job() {
fun setupTask(prefInterval: Int? = null) { fun setupTask(prefInterval: Int? = null) {
val preferences = Injekt.get<PreferencesHelper>() val preferences = Injekt.get<PreferencesHelper>()
val interval = prefInterval ?: preferences.libraryUpdateInterval().getOrDefault() val interval = (prefInterval ?: preferences.libraryUpdateInterval().getOrDefault())
.toLong()
if (interval > 0) { if (interval > 0) {
val restrictions = preferences.libraryUpdateRestriction()!! val restrictions = preferences.libraryUpdateRestriction()!!
val acRestriction = "ac" in restrictions val acRestriction = "ac" in restrictions
@ -30,7 +32,9 @@ class LibraryUpdateJob : Job() {
JobRequest.NetworkType.CONNECTED JobRequest.NetworkType.CONNECTED
JobRequest.Builder(TAG) JobRequest.Builder(TAG)
.setPeriodic(interval * 60 * 60 * 1000L, 10 * 60 * 1000) .setPeriodic(
TimeUnit.HOURS.toMillis(interval), TimeUnit.MINUTES.toMillis
(10))
.setRequiredNetworkType(wifiRestriction) .setRequiredNetworkType(wifiRestriction)
.setRequiresCharging(acRestriction) .setRequiresCharging(acRestriction)
.setRequirementsEnforced(true) .setRequirementsEnforced(true)

View File

@ -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 * Returns [PendingIntent] that marks a chapter as read and deletes it if preferred
* *

View File

@ -38,6 +38,11 @@ object Notifications {
const val CHANNEL_NEW_CHAPTERS = "new_chapters_channel" const val CHANNEL_NEW_CHAPTERS = "new_chapters_channel"
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.
@ -48,18 +53,31 @@ object Notifications {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
val channels = listOf( val channels = listOf(
NotificationChannel(CHANNEL_COMMON, context.getString(R.string.channel_common), NotificationChannel(
NotificationManager.IMPORTANCE_LOW), CHANNEL_COMMON,
NotificationChannel(CHANNEL_LIBRARY, context.getString(R.string.channel_library_updates), context.getString(R.string.channel_common),
NotificationManager.IMPORTANCE_LOW).apply { NotificationManager.IMPORTANCE_LOW
), NotificationChannel(
CHANNEL_LIBRARY,
context.getString(R.string.channel_library_updates),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false) setShowBadge(false)
}, }, NotificationChannel(
NotificationChannel(CHANNEL_DOWNLOADER, context.getString(R.string.channel_downloader), CHANNEL_DOWNLOADER,
NotificationManager.IMPORTANCE_LOW).apply { context.getString(R.string.channel_downloader),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false) setShowBadge(false)
}, }, NotificationChannel(
NotificationChannel(CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters), CHANNEL_UPDATES_TO_EXTS,
NotificationManager.IMPORTANCE_DEFAULT) 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) context.notificationManager.createNotificationChannels(channels)
} }

View File

@ -101,6 +101,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 downloadNew = "download_new" const val downloadNew = "download_new"

View File

@ -164,6 +164,8 @@ class PreferencesHelper(val context: Context) {
fun automaticUpdates() = prefs.getBoolean(Keys.automaticUpdates, false) fun automaticUpdates() = prefs.getBoolean(Keys.automaticUpdates, false)
fun automaticExtUpdates() = rxPrefs.getBoolean(Keys.automaticExtUpdates, false)
fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", mutableSetOf()) fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", mutableSetOf())
fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false) fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)

View File

@ -9,8 +9,8 @@ import com.evernote.android.job.JobManager
import com.evernote.android.job.JobRequest import com.evernote.android.job.JobRequest
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.notificationManager import eu.kanade.tachiyomi.util.notificationManager
import java.util.concurrent.TimeUnit
class UpdaterJob : Job() { class UpdaterJob : Job() {
@ -54,7 +54,7 @@ class UpdaterJob : Job() {
fun setupTask() { fun setupTask() {
JobRequest.Builder(TAG) JobRequest.Builder(TAG)
.setPeriodic(24 * 60 * 60 * 1000, 60 * 60 * 1000) .setPeriodic(TimeUnit.DAYS.toMillis(1), TimeUnit.HOURS.toMillis(1))
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true) .setRequirementsEnforced(true)
.setUpdateCurrent(true) .setUpdateCurrent(true)

View File

@ -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)
}
}
}

View File

@ -16,12 +16,16 @@ 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 eu.kanade.tachiyomi.util.RecyclerWindowInsetsListener import eu.kanade.tachiyomi.util.RecyclerWindowInsetsListener
import kotlinx.android.synthetic.main.extension_controller.* 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. * Controller to manage the catalogues available in the app.
@ -86,6 +90,16 @@ 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)
if (item.isChecked)
ExtensionUpdateJob.setupTask()
else
ExtensionUpdateJob.cancelTask()
}
else -> return super.onOptionsItemSelected(item) else -> return super.onOptionsItemSelected(item)
} }
return true return true
@ -139,6 +153,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() 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 { override fun onItemClick(view: View?, position: Int): Boolean {

View File

@ -9,16 +9,23 @@ import android.graphics.Rect
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.MotionEvent 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.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout 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.biometric.BiometricManager
import androidx.core.graphics.ColorUtils 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 com.google.android.material.snackbar.Snackbar
import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.Migrations
import eu.kanade.tachiyomi.R 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.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity 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.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
@ -282,6 +293,7 @@ class MainActivity : BaseActivity() {
SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_drawer_recent_updates) SHORTCUT_RECENTLY_UPDATED -> setSelectedDrawerItem(R.id.nav_drawer_recent_updates)
SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read) SHORTCUT_RECENTLY_READ -> setSelectedDrawerItem(R.id.nav_drawer_recently_read)
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues) SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues)
SHORTCUT_EXTENSIONS -> setSelectedDrawerItem(R.id.nav_drawer_extensions)
SHORTCUT_MANGA -> { SHORTCUT_MANGA -> {
val extras = intent.extras ?: return false val extras = intent.extras ?: return false
router.setRoot(RouterTransaction.with(MangaController(extras))) 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_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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -14,4 +14,11 @@
android:icon="@drawable/ic_filter_list_white_24dp" android:icon="@drawable/ic_filter_list_white_24dp"
app:showAsAction="always"/> app:showAsAction="always"/>
<item
android:id="@+id/action_auto_check"
android:title="@string/action_auto_check_extensions"
android:icon="@drawable/ic_filter_list_white_24dp"
android:checkable="true"
app:showAsAction="never"/>
</menu> </menu>

View File

@ -103,6 +103,7 @@
<string name="action_login">Log in</string> <string name="action_login">Log in</string>
<string name="action_webview_back">Back</string> <string name="action_webview_back">Back</string>
<string name="action_webview_forward">Forward</string> <string name="action_webview_forward">Forward</string>
<string name="action_auto_check_extensions">Auto-check for updates</string>
<!-- Operations --> <!-- Operations -->
<string name="deleting">Deleting…</string> <string name="deleting">Deleting…</string>
@ -515,6 +516,7 @@
<string name="update_check_notification_download_complete">Download complete</string> <string name="update_check_notification_download_complete">Download complete</string>
<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>
<string name="update_check_notification_ext_updates">%1$d extension updates available</string>
<!--Content Description--> <!--Content Description-->
<string name="description_backdrop">Backdrop image of manga</string> <string name="description_backdrop">Backdrop image of manga</string>
@ -541,6 +543,7 @@
<string name="channel_common">Common</string> <string name="channel_common">Common</string>
<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_ext_updates">Extension Updates</string>
<string name="channel_library_updates">Updating Library</string> <string name="channel_library_updates">Updating Library</string>
<string name="channel_new_chapters">New Chapters</string> <string name="channel_new_chapters">New Chapters</string>