Final Final fix for extension notifation + badge
Using coroutine for notifcation job Now setting a badge beside extension in drawer to signify updates, this is updated every hour or when the api is hit
This commit is contained in:
parent
8332a45028
commit
09bb216cda
@ -201,6 +201,10 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun refreshCoversToo() = rxPrefs.getBoolean(Keys.refreshCoversToo, true)
|
fun refreshCoversToo() = rxPrefs.getBoolean(Keys.refreshCoversToo, true)
|
||||||
|
|
||||||
|
fun extensionUpdatesCount() = rxPrefs.getInteger("ext_updates_count", 0)
|
||||||
|
|
||||||
|
fun lastExtCheck() = rxPrefs.getLong("last_ext_check", 0)
|
||||||
|
|
||||||
fun upgradeFilters() {
|
fun upgradeFilters() {
|
||||||
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
|
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
|
||||||
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()
|
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()
|
||||||
|
@ -161,7 +161,7 @@ class ExtensionManager(
|
|||||||
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
|
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
|
||||||
val mutInstalledExtensions = installedExtensions.toMutableList()
|
val mutInstalledExtensions = installedExtensions.toMutableList()
|
||||||
var changed = false
|
var changed = false
|
||||||
|
var hasUpdateCount = 0
|
||||||
for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
|
for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
|
||||||
val pkgName = installedExt.pkgName
|
val pkgName = installedExt.pkgName
|
||||||
val availableExt = availableExtensions.find { it.pkgName == pkgName }
|
val availableExt = availableExtensions.find { it.pkgName == pkgName }
|
||||||
@ -175,6 +175,7 @@ class ExtensionManager(
|
|||||||
val hasUpdate = availableExt.versionCode > installedExt.versionCode
|
val hasUpdate = availableExt.versionCode > installedExt.versionCode
|
||||||
if (installedExt.hasUpdate != hasUpdate) {
|
if (installedExt.hasUpdate != hasUpdate) {
|
||||||
mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate)
|
mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate)
|
||||||
|
hasUpdateCount++
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,6 +183,7 @@ class ExtensionManager(
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
installedExtensions = mutInstalledExtensions
|
installedExtensions = mutInstalledExtensions
|
||||||
}
|
}
|
||||||
|
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -312,10 +314,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) {
|
||||||
@ -324,6 +328,7 @@ class ExtensionManager(
|
|||||||
|
|
||||||
override fun onPackageUninstalled(pkgName: String) {
|
override fun onPackageUninstalled(pkgName: String) {
|
||||||
unregisterExtension(pkgName)
|
unregisterExtension(pkgName)
|
||||||
|
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.extension
|
package eu.kanade.tachiyomi.extension
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
@ -10,25 +11,27 @@ import com.evernote.android.job.JobRequest
|
|||||||
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.notification.Notifications
|
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.notification
|
import eu.kanade.tachiyomi.util.notification
|
||||||
import rx.Observable
|
import kotlinx.coroutines.Dispatchers
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import kotlinx.coroutines.GlobalScope
|
||||||
import rx.schedulers.Schedulers
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import uy.kohesive.injekt.injectLazy
|
||||||
import uy.kohesive.injekt.Injekt
|
import java.lang.Exception
|
||||||
import uy.kohesive.injekt.api.get
|
import java.util.Date
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ExtensionUpdateJob : Job() {
|
class ExtensionUpdateJob : Job() {
|
||||||
|
|
||||||
override fun onRunJob(params: Params): Result {
|
override fun onRunJob(params: Params): Result {
|
||||||
val extensionManager: ExtensionManager = Injekt.get()
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
extensionManager.findAvailableExtensions()
|
val pendingUpdates = ExtensionGithubApi().checkforUpdates(context)
|
||||||
|
|
||||||
extensionManager.getInstalledExtensionsObservable().map { list ->
|
|
||||||
val pendingUpdates = list.filter { it.hasUpdate }
|
|
||||||
if (pendingUpdates.isNotEmpty()) {
|
if (pendingUpdates.isNotEmpty()) {
|
||||||
val names = pendingUpdates.map { it.name }
|
val names = pendingUpdates.map { it.name }
|
||||||
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
|
preferences.extensionUpdatesCount().set(pendingUpdates.size)
|
||||||
NotificationManagerCompat.from(context).apply {
|
NotificationManagerCompat.from(context).apply {
|
||||||
notify(Notifications.ID_UPDATES_TO_EXTS,
|
notify(Notifications.ID_UPDATES_TO_EXTS,
|
||||||
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
||||||
@ -56,15 +59,7 @@ class ExtensionUpdateJob : Job() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Result.SUCCESS
|
}
|
||||||
}.take(1)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.subscribe({
|
|
||||||
}, {
|
|
||||||
Timber.e(it)
|
|
||||||
}, {
|
|
||||||
})
|
|
||||||
return Result.SUCCESS
|
return Result.SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
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
|
||||||
import com.github.salomonbrys.kotson.string
|
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.ExtensionManager
|
||||||
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.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
internal class ExtensionGithubApi {
|
internal class ExtensionGithubApi {
|
||||||
|
|
||||||
@ -31,6 +39,35 @@ internal class ExtensionGithubApi {
|
|||||||
.map(::parseResponse)
|
.map(::parseResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun checkforUpdates(context: Context): List<Extension.Installed> {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
val call = GET("$repoUrl/index.json")
|
||||||
|
val response = client.newCall(call).execute()
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val extensions = parseResponse(response)
|
||||||
|
val extensionsWithUpdate = mutableListOf<Extension.Installed>()
|
||||||
|
|
||||||
|
val installedExtensions = ExtensionLoader.loadExtensions(context)
|
||||||
|
.filterIsInstance<LoadResult.Success>()
|
||||||
|
.map { it.extension }
|
||||||
|
val mutInstalledExtensions = installedExtensions.toMutableList()
|
||||||
|
for (installedExt in mutInstalledExtensions) {
|
||||||
|
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()
|
||||||
|
|
||||||
|
@ -82,7 +82,9 @@ internal class ExtensionInstaller(private val context: Context) {
|
|||||||
// Always notify on main thread
|
// Always notify on main thread
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
// Always remove the download when unsubscribed
|
// Always remove the download when unsubscribed
|
||||||
.doOnUnsubscribe { deleteDownload(pkgName) }
|
.doOnUnsubscribe {
|
||||||
|
deleteDownload(pkgName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@ import android.content.res.Configuration
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
|
import android.graphics.Typeface
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
@ -15,6 +16,7 @@ import android.view.ViewGroup
|
|||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
||||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||||
@ -34,6 +36,7 @@ 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.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
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
|
||||||
@ -51,6 +54,7 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
|||||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||||
import eu.kanade.tachiyomi.util.doOnApplyWindowInsets
|
import eu.kanade.tachiyomi.util.doOnApplyWindowInsets
|
||||||
import eu.kanade.tachiyomi.util.getResourceColor
|
import eu.kanade.tachiyomi.util.getResourceColor
|
||||||
|
import eu.kanade.tachiyomi.util.gone
|
||||||
import eu.kanade.tachiyomi.util.launchUI
|
import eu.kanade.tachiyomi.util.launchUI
|
||||||
import eu.kanade.tachiyomi.util.marginBottom
|
import eu.kanade.tachiyomi.util.marginBottom
|
||||||
import eu.kanade.tachiyomi.util.marginTop
|
import eu.kanade.tachiyomi.util.marginTop
|
||||||
@ -58,11 +62,16 @@ import eu.kanade.tachiyomi.util.openInBrowser
|
|||||||
import eu.kanade.tachiyomi.util.updateLayoutParams
|
import eu.kanade.tachiyomi.util.updateLayoutParams
|
||||||
import eu.kanade.tachiyomi.util.updatePadding
|
import eu.kanade.tachiyomi.util.updatePadding
|
||||||
import eu.kanade.tachiyomi.util.updatePaddingRelative
|
import eu.kanade.tachiyomi.util.updatePaddingRelative
|
||||||
|
import eu.kanade.tachiyomi.util.visible
|
||||||
import kotlinx.android.synthetic.main.main_activity.*
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class MainActivity : BaseActivity() {
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
@ -276,10 +285,32 @@ class MainActivity : BaseActivity() {
|
|||||||
ChangelogDialogController().showDialog(router)
|
ChangelogDialogController().showDialog(router)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
preferences.extensionUpdatesCount().asObservable().subscribe {
|
||||||
|
setExtensionsBadge()
|
||||||
|
}
|
||||||
|
setExtensionsBadge()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setExtensionsBadge() {
|
||||||
|
|
||||||
|
val extUpdateText: TextView = nav_view.menu.findItem(
|
||||||
|
R.id.nav_drawer_extensions
|
||||||
|
)?.actionView as? TextView ?: return
|
||||||
|
|
||||||
|
val updates = preferences.extensionUpdatesCount().getOrDefault()
|
||||||
|
if (updates > 0) {
|
||||||
|
extUpdateText.text = updates.toString()
|
||||||
|
extUpdateText.visible()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
extUpdateText.text = null
|
||||||
|
extUpdateText.gone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
getExtensionUpdates()
|
||||||
val useBiometrics = preferences.useBiometrics().getOrDefault()
|
val useBiometrics = preferences.useBiometrics().getOrDefault()
|
||||||
if (useBiometrics && BiometricManager.from(this)
|
if (useBiometrics && BiometricManager.from(this)
|
||||||
.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||||
@ -294,6 +325,21 @@ class MainActivity : BaseActivity() {
|
|||||||
preferences.useBiometrics().set(false)
|
preferences.useBiometrics().set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getExtensionUpdates() {
|
||||||
|
if (Date().time >= preferences.lastExtCheck().getOrDefault() +
|
||||||
|
TimeUnit.HOURS.toMillis(1)) {
|
||||||
|
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) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
if (!handleIntentAction(intent)) {
|
if (!handleIntentAction(intent)) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
|
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/drawerPrimary"/>
|
||||||
|
</shape>
|
14
app/src/main/res/layout/menu_counter.xml
Normal file
14
app/src/main/res/layout/menu_counter.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||||
|
android:background="@drawable/round_textview_background"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingEnd="4dp"/>
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<group android:id="@+id/group_feature"
|
<group android:id="@+id/group_feature"
|
||||||
android:checkableBehavior="single">
|
android:checkableBehavior="single">
|
||||||
<item
|
<item
|
||||||
@ -22,6 +23,7 @@
|
|||||||
<item
|
<item
|
||||||
android:id="@+id/nav_drawer_extensions"
|
android:id="@+id/nav_drawer_extensions"
|
||||||
android:icon="@drawable/ic_extension_black_24dp"
|
android:icon="@drawable/ic_extension_black_24dp"
|
||||||
|
app:actionLayout="@layout/menu_counter"
|
||||||
android:title="@string/label_extensions"/>
|
android:title="@string/label_extensions"/>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_drawer_downloads"
|
android:id="@+id/nav_drawer_downloads"
|
||||||
|
@ -566,6 +566,7 @@
|
|||||||
<item quantity="other">For %d titles</item>
|
<item quantity="other">For %d titles</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="notification_and_n_more">and %1$d more chapters.</string>
|
<string name="notification_and_n_more">and %1$d more chapters.</string>
|
||||||
|
<string name="notification_and_n_more_ext">and %1$d more extensions.</string>
|
||||||
<string name="notification_cover_update_failed">Failed to update cover</string>
|
<string name="notification_cover_update_failed">Failed to update cover</string>
|
||||||
<string name="notification_first_add_to_library">Please add the manga to your library before doing this</string>
|
<string name="notification_first_add_to_library">Please add the manga to your library before doing this</string>
|
||||||
<string name="notification_not_connected_to_ac_title">Sync canceled</string>
|
<string name="notification_not_connected_to_ac_title">Sync canceled</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user