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