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 0162865846..a24212f299 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 @@ -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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index bfb3381fa0..edafa9af4c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -161,7 +161,7 @@ class ExtensionManager( private fun updatedInstalledExtensionsStatuses(availableExtensions: List) { 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 }) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt index bb4cd759d0..045ec44f48 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateJob.kt @@ -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 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt index d8eb982122..ec8201d6eb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt @@ -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 { + 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() + + val installedExtensions = ExtensionLoader.loadExtensions(context) + .filterIsInstance() + .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 { val text = response.body?.use { it.string() } ?: return emptyList() diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt index f6feabcb81..b2e2086a22 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt @@ -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) + } } /** 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 56969fc2dd..55170c0b1f 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 @@ -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) diff --git a/app/src/main/res/drawable/round_textview_background.xml b/app/src/main/res/drawable/round_textview_background.xml new file mode 100644 index 0000000000..867628b7f3 --- /dev/null +++ b/app/src/main/res/drawable/round_textview_background.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/menu_counter.xml b/app/src/main/res/layout/menu_counter.xml new file mode 100644 index 0000000000..6895098d9e --- /dev/null +++ b/app/src/main/res/layout/menu_counter.xml @@ -0,0 +1,14 @@ + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_navigation.xml b/app/src/main/res/menu/menu_navigation.xml index 1ea7a65415..1cfcd6ce15 100644 --- a/app/src/main/res/menu/menu_navigation.xml +++ b/app/src/main/res/menu/menu_navigation.xml @@ -1,5 +1,6 @@ - + For %d titles and %1$d more chapters. + and %1$d more extensions. Failed to update cover Please add the manga to your library before doing this Sync canceled