From 77c080cc474faa5bafad0e611a2f9de3f44b1495 Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Tue, 27 Apr 2021 23:03:17 -0400 Subject: [PATCH] Adding a button in new release notification to open GitHub release page Shows also while downloading/error/installed Should help those who can't complete their downloads for some reason There's also refactoring from upstream in this, including tapping on the notification for an intent. However the intent here opens the app with the release notes dialog --- .../data/notification/NotificationReceiver.kt | 24 ++++++ .../kanade/tachiyomi/data/updater/Release.kt | 2 + .../tachiyomi/data/updater/UpdaterJob.kt | 42 +++------- .../tachiyomi/data/updater/UpdaterNotifier.kt | 83 ++++++++++++++++--- .../data/updater/github/GithubRelease.kt | 1 + .../kanade/tachiyomi/ui/main/MainActivity.kt | 10 +++ .../tachiyomi/ui/setting/AboutController.kt | 4 +- app/src/main/res/values/strings.xml | 1 + 8 files changed, 124 insertions(+), 43 deletions(-) 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 6f71500a11..0a38b8f8b4 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 @@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.ui.setting.AboutController import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.notificationManager @@ -278,6 +279,8 @@ class NotificationReceiver : BroadcastReceiver() { // Called to cancel library update. private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE" + private const val ACTION_CANCEL_UPDATE_DOWNLOAD = "$ID.$NAME.CANCEL_UPDATE_DOWNLOAD" + // Called to mark as read private const val ACTION_MARK_AS_READ = "$ID.$NAME.MARK_AS_READ" @@ -471,6 +474,27 @@ class NotificationReceiver : BroadcastReceiver() { ) } + /** + * Returns [PendingIntent] that opens the manga details controller. + * + * @param context context of application + * @param manga manga of chapter + */ + internal fun openUpdatePendingActivity(context: Context, notes: String, downloadLink: String): + PendingIntent { + val newIntent = + Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_UPDATE_NOTES) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + .putExtra(AboutController.NewUpdateDialogController.BODY_KEY, notes) + .putExtra(AboutController.NewUpdateDialogController.URL_KEY, downloadLink) + return PendingIntent.getActivity( + context, + downloadLink.hashCode(), + newIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + } + /** * Returns [PendingIntent] that opens the manga details controller. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt index 61f2bd7870..74166bf5d8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/Release.kt @@ -9,4 +9,6 @@ interface Release { * @return download link of latest release. */ val downloadLink: String + + val releaseLink: String } 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 868c1fb752..afb69a693c 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 @@ -1,8 +1,6 @@ package eu.kanade.tachiyomi.data.updater -import android.app.PendingIntent import android.content.Context -import android.content.Intent import androidx.core.app.NotificationCompat import androidx.work.Constraints import androidx.work.CoroutineWorker @@ -11,9 +9,7 @@ import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters -import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.Notifications -import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.notificationManager import kotlinx.coroutines.coroutineScope import java.util.concurrent.TimeUnit @@ -22,37 +18,19 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { override suspend fun doWork(): Result = coroutineScope { - val result = try { - UpdateChecker.getUpdateChecker().checkForUpdate() + try { + val result = UpdateChecker.getUpdateChecker().checkForUpdate() + if (result is UpdateResult.NewUpdate<*>) { + UpdaterNotifier(context).promptUpdate( + result.release.info, + result.release.downloadLink, + result.release.releaseLink + ) + } + Result.success() } catch (e: Exception) { Result.failure() } - if (result is UpdateResult.NewUpdate<*>) { - val url = result.release.downloadLink - - val intent = Intent(context, UpdaterService::class.java).apply { - putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url) - } - - NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update { - setContentTitle(context.getString(R.string.app_name)) - setContentText(context.getString(R.string.update_available)) - setSmallIcon(android.R.drawable.stat_sys_download_done) - color = context.getResourceColor(R.attr.colorAccent) - // Download action - addAction( - android.R.drawable.stat_sys_download_done, - context.getString(R.string.download), - PendingIntent.getService( - context, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT - ) - ) - } - } - Result.success() } fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt index f13b5aab70..a5b2ce03a5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt @@ -1,13 +1,17 @@ package eu.kanade.tachiyomi.data.updater +import android.app.PendingIntent import android.content.Context +import android.content.Intent import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat +import androidx.core.net.toUri import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications +import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.notificationManager /** @@ -20,10 +24,14 @@ internal class UpdaterNotifier(private val context: Context) { /** * Builder to manage notifications. */ - private val notification by lazy { + private val notificationBuilder by lazy { NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON) } + companion object { + var releasePageUrl: String? = null + } + /** * Call to show notification. * @@ -33,20 +41,68 @@ internal class UpdaterNotifier(private val context: Context) { context.notificationManager.notify(id, build()) } + fun promptUpdate(body: String, url: String, releaseUrl: String) { + val intent = Intent(context, UpdaterService::class.java).apply { + putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url) + } + + val pendingIntent = NotificationReceiver.openUpdatePendingActivity(context, body, url) + releasePageUrl = releaseUrl + with(notificationBuilder) { + setContentTitle(context.getString(R.string.app_name)) + setContentText(context.getString(R.string.new_version_available)) + setContentIntent(pendingIntent) + setAutoCancel(true) + setSmallIcon(android.R.drawable.stat_sys_download_done) + color = context.getResourceColor(R.attr.colorAccent) + clearActions() + // Download action + addAction( + android.R.drawable.stat_sys_download_done, + context.getString(R.string.download), + PendingIntent.getService( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + ) + addReleasePageAction() + } + notificationBuilder.show() + } + + private fun NotificationCompat.Builder.addReleasePageAction() { + releasePageUrl?.let { releaseUrl -> + val releaseIntent = Intent(Intent.ACTION_VIEW, releaseUrl.toUri()).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + } + addAction( + R.drawable.ic_new_releases_24dp, + context.getString(R.string.release_page), + PendingIntent.getActivity(context, releaseUrl.hashCode(), releaseIntent, PendingIntent.FLAG_UPDATE_CURRENT) + ) + } + } + /** * Call when apk download starts. * * @param title tile of notification. */ fun onDownloadStarted(title: String): NotificationCompat.Builder { - with(notification) { + with(notificationBuilder) { setContentTitle(title) setContentText(context.getString(R.string.downloading)) setSmallIcon(android.R.drawable.stat_sys_download) + setAutoCancel(false) setOngoing(true) + clearActions() + + addReleasePageAction() } - notification.show() - return notification + notificationBuilder.show() + return notificationBuilder } /** @@ -55,11 +111,11 @@ internal class UpdaterNotifier(private val context: Context) { * @param progress progress of download (xx%/100). */ fun onProgressChange(progress: Int) { - with(notification) { + with(notificationBuilder) { setProgress(100, progress, false) setOnlyAlertOnce(true) } - notification.show() + notificationBuilder.show() } /** @@ -68,13 +124,15 @@ internal class UpdaterNotifier(private val context: Context) { * @param uri path location of apk. */ fun onDownloadFinished(uri: Uri) { - with(notification) { + with(notificationBuilder) { setContentText(context.getString(R.string.download_complete)) setSmallIcon(android.R.drawable.stat_sys_download_done) + setAutoCancel(false) setOnlyAlertOnce(false) setProgress(0, 0, false) // Install action setContentIntent(NotificationHandler.installApkPendingActivity(context, uri)) + clearActions() addAction( R.drawable.ic_system_update_24dp, context.getString(R.string.install), @@ -86,8 +144,9 @@ internal class UpdaterNotifier(private val context: Context) { context.getString(R.string.cancel), NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER) ) + addReleasePageAction() } - notification.show() + notificationBuilder.show() } /** @@ -96,12 +155,14 @@ internal class UpdaterNotifier(private val context: Context) { * @param url web location of apk to download. */ fun onDownloadError(url: String) { - with(notification) { + with(notificationBuilder) { setContentText(context.getString(R.string.download_error)) setSmallIcon(android.R.drawable.stat_sys_warning) setOnlyAlertOnce(false) + setAutoCancel(false) setProgress(0, 0, false) color = ContextCompat.getColor(context, R.color.colorAccent) + clearActions() // Retry action addAction( R.drawable.ic_refresh_24dp, @@ -114,7 +175,9 @@ internal class UpdaterNotifier(private val context: Context) { context.getString(R.string.cancel), NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER) ) + addReleasePageAction() } - notification.show(Notifications.ID_UPDATER) + notificationBuilder.show(Notifications.ID_UPDATER) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt index 09f1b37d01..69cafcd522 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/github/GithubRelease.kt @@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.data.updater.Release class GithubRelease( @SerializedName("tag_name") val version: String, @SerializedName("body") override val info: String, + @SerializedName("html_url") override val releaseLink: String, @SerializedName("assets") private val assets: List ) : Release { 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 0763626aea..72e23c35ac 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 @@ -49,6 +49,7 @@ import eu.kanade.tachiyomi.data.preference.asImmediateFlowIn import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.updater.UpdateChecker import eu.kanade.tachiyomi.data.updater.UpdateResult +import eu.kanade.tachiyomi.data.updater.UpdaterNotifier import eu.kanade.tachiyomi.databinding.MainActivityBinding import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi import eu.kanade.tachiyomi.ui.base.MaterialMenuSheet @@ -497,6 +498,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi // Create confirmation window withContext(Dispatchers.Main) { + UpdaterNotifier.releasePageUrl = result.release.releaseLink AboutController.NewUpdateDialogController(body, url).showDialog(router) } } @@ -570,6 +572,13 @@ open class MainActivity : BaseActivity(), DownloadServiceLi if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library router.pushController(MangaDetailsController(extras).withFadeTransaction()) } + SHORTCUT_UPDATE_NOTES -> { + val extras = intent.extras ?: return false + if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library + if (router.backstack.lastOrNull()?.controller() !is AboutController.NewUpdateDialogController) { + AboutController.NewUpdateDialogController(extras).showDialog(router) + } + } SHORTCUT_SOURCE -> { val extras = intent.extras ?: return false if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library @@ -882,6 +891,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi const val SHORTCUT_BROWSE = "eu.kanade.tachiyomi.SHOW_BROWSE" const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS" const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA" + const val SHORTCUT_UPDATE_NOTES = "eu.kanade.tachiyomi.SHOW_UPDATE_NOTES" const val SHORTCUT_SOURCE = "eu.kanade.tachiyomi.SHOW_SOURCE" const val SHORTCUT_READER_SETTINGS = "eu.kanade.tachiyomi.READER_SETTINGS" const val SHORTCUT_EXTENSIONS = "eu.kanade.tachiyomi.EXTENSIONS" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt index b1374cdd64..1938da45fe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AboutController.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.updater.UpdateChecker import eu.kanade.tachiyomi.data.updater.UpdateResult +import eu.kanade.tachiyomi.data.updater.UpdaterNotifier import eu.kanade.tachiyomi.data.updater.UpdaterService import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.util.lang.toTimestampString @@ -166,6 +167,7 @@ class AboutController : SettingsController() { // Create confirmation window withContext(Dispatchers.Main) { + UpdaterNotifier.releasePageUrl = result.release.releaseLink NewUpdateDialogController(body, url).showDialog(router) } } @@ -202,7 +204,7 @@ class AboutController : SettingsController() { .negativeButton(R.string.ignore) } - private companion object { + companion object { const val BODY_KEY = "NewUpdateDialogController.body" const val URL_KEY = "NewUpdateDialogController.key" } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 057edbee94..4a3ce206e7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -108,6 +108,7 @@ New version available! No new updates available Searching for updates… + Release page