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
This commit is contained in:
Jays2Kings 2021-04-27 23:03:17 -04:00
parent 0b4f83d83e
commit 77c080cc47
8 changed files with 124 additions and 43 deletions

View File

@ -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.
*

View File

@ -9,4 +9,6 @@ interface Release {
* @return download link of latest release.
*/
val downloadLink: String
val releaseLink: String
}

View File

@ -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) {

View File

@ -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()
}
notificationBuilder.show(Notifications.ID_UPDATER)
}
notification.show(Notifications.ID_UPDATER)
}
}

View File

@ -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<Assets>
) : Release {

View File

@ -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<MainActivityBinding>(), 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<MainActivityBinding>(), 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<MainActivityBinding>(), 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"

View File

@ -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"
}

View File

@ -108,6 +108,7 @@
<string name="new_version_available">New version available!</string>
<string name="no_new_updates_available">No new updates available</string>
<string name="searching_for_updates">Searching for updates…</string>
<string name="release_page">Release page</string>
<!-- Main Screens -->