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:
Jay 2020-01-13 04:16:05 -08:00
parent 8332a45028
commit 09bb216cda
10 changed files with 164 additions and 49 deletions

View File

@ -201,6 +201,10 @@ class PreferencesHelper(val context: Context) {
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() {
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()

View File

@ -161,7 +161,7 @@ class ExtensionManager(
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
val mutInstalledExtensions = installedExtensions.toMutableList()
var changed = false
var hasUpdateCount = 0
for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
val pkgName = installedExt.pkgName
val availableExt = availableExtensions.find { it.pkgName == pkgName }
@ -175,6 +175,7 @@ class ExtensionManager(
val hasUpdate = availableExt.versionCode > installedExt.versionCode
if (installedExt.hasUpdate != hasUpdate) {
mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate)
hasUpdateCount++
changed = true
}
}
@ -182,6 +183,7 @@ class ExtensionManager(
if (changed) {
installedExtensions = mutInstalledExtensions
}
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
}
/**
@ -312,10 +314,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) {
@ -324,6 +328,7 @@ class ExtensionManager(
override fun onPackageUninstalled(pkgName: String) {
unregisterExtension(pkgName)
preferences.extensionUpdatesCount().set(installedExtensions.count { it.hasUpdate })
}
}

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
@ -10,61 +11,55 @@ 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.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.util.notification
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import uy.kohesive.injekt.injectLazy
import java.lang.Exception
import java.util.Date
import java.util.concurrent.TimeUnit
class ExtensionUpdateJob : Job() {
override fun onRunJob(params: Params): Result {
val extensionManager: ExtensionManager = Injekt.get()
extensionManager.findAvailableExtensions()
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.resources.getQuantityString(
R.plurals.update_check_notification_ext_updates, names
.size, names.size
)
GlobalScope.launch(Dispatchers.IO) {
val pendingUpdates = ExtensionGithubApi().checkforUpdates(context)
if (pendingUpdates.isNotEmpty()) {
val names = pendingUpdates.map { it.name }
val preferences: PreferencesHelper by injectLazy()
preferences.extensionUpdatesCount().set(pendingUpdates.size)
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 = if (names.size > 5) {
"${names.take(4).joinToString(", ")}, " + context.getString(
R.string.notification_and_n_more, (names.size - 4)
)
} else names.joinToString(", ")
setContentText(extNames)
setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
setSmallIcon(R.drawable.ic_extension_update)
color = ContextCompat.getColor(context, R.color.colorAccentLight)
setContentIntent(
NotificationReceiver.openExtensionsPendingActivity(
context
)
)
val extNames = if (names.size > 5) {
"${names.take(4).joinToString(", ")}, " + context.getString(
R.string.notification_and_n_more, (names.size - 4)
)
setAutoCancel(true)
})
}
} else names.joinToString(", ")
setContentText(extNames)
setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
setSmallIcon(R.drawable.ic_extension_update)
color = ContextCompat.getColor(context, R.color.colorAccentLight)
setContentIntent(
NotificationReceiver.openExtensionsPendingActivity(
context
)
)
setAutoCancel(true)
})
}
Result.SUCCESS
}.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({
}, {
Timber.e(it)
}, {
})
}
}
return Result.SUCCESS
}

View File

@ -1,18 +1,26 @@
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
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonArray
import eu.kanade.tachiyomi.extension.ExtensionManager
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
import eu.kanade.tachiyomi.network.asObservableSuccess
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.withContext
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.lang.Exception
internal class ExtensionGithubApi {
@ -31,6 +39,35 @@ internal class ExtensionGithubApi {
.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> {
val text = response.body?.use { it.string() } ?: return emptyList()

View File

@ -82,7 +82,9 @@ internal class ExtensionInstaller(private val context: Context) {
// Always notify on main thread
.observeOn(AndroidSchedulers.mainThread())
// Always remove the download when unsubscribed
.doOnUnsubscribe { deleteDownload(pkgName) }
.doOnUnsubscribe {
deleteDownload(pkgName)
}
}
/**

View File

@ -7,6 +7,7 @@ import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Rect
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
import android.view.MotionEvent
@ -15,6 +16,7 @@ import android.view.ViewGroup
import android.webkit.WebView
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
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
@ -34,6 +36,7 @@ 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
@ -51,6 +54,7 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
import eu.kanade.tachiyomi.util.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.gone
import eu.kanade.tachiyomi.util.launchUI
import eu.kanade.tachiyomi.util.marginBottom
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.updatePadding
import eu.kanade.tachiyomi.util.updatePaddingRelative
import eu.kanade.tachiyomi.util.visible
import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.util.Date
import java.util.concurrent.TimeUnit
class MainActivity : BaseActivity() {
@ -276,10 +285,32 @@ class MainActivity : BaseActivity() {
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() {
super.onResume()
getExtensionUpdates()
val useBiometrics = preferences.useBiometrics().getOrDefault()
if (useBiometrics && BiometricManager.from(this)
.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
@ -294,6 +325,21 @@ class MainActivity : BaseActivity() {
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) {
if (!handleIntentAction(intent)) {
super.onNewIntent(intent)

View 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>

View 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"/>

View File

@ -1,5 +1,6 @@
<?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"
android:checkableBehavior="single">
<item
@ -22,6 +23,7 @@
<item
android:id="@+id/nav_drawer_extensions"
android:icon="@drawable/ic_extension_black_24dp"
app:actionLayout="@layout/menu_counter"
android:title="@string/label_extensions"/>
<item
android:id="@+id/nav_drawer_downloads"

View File

@ -566,6 +566,7 @@
<item quantity="other">For %d titles</item>
</plurals>
<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_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>