mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 19:31:49 +01:00
Show notification with error log on update failures + Move notification logic out of LibraryUpdateService
This commit is contained in:
parent
e76805160c
commit
12b2da9058
@ -0,0 +1,242 @@
|
||||
package eu.kanade.tachiyomi.data.library
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.net.Uri
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import coil.Coil
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.GetRequest
|
||||
import coil.transform.CircleCropTransformation
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
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.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.lang.chop
|
||||
import eu.kanade.tachiyomi.util.system.notification
|
||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.ArrayList
|
||||
|
||||
class LibraryUpdateNotifier(private val context: Context) {
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
/**
|
||||
* Pending intent of action that cancels the library update
|
||||
*/
|
||||
private val cancelIntent by lazy {
|
||||
NotificationReceiver.cancelLibraryUpdatePendingBroadcast(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitmap of the app for notifications.
|
||||
*/
|
||||
private val notificationBitmap by lazy {
|
||||
BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached progress notification to avoid creating a lot.
|
||||
*/
|
||||
val progressNotificationBuilder by lazy {
|
||||
context.notificationBuilder(Notifications.CHANNEL_LIBRARY) {
|
||||
setContentTitle(context.getString(R.string.app_name))
|
||||
setSmallIcon(R.drawable.ic_refresh_24dp)
|
||||
setLargeIcon(notificationBitmap)
|
||||
setOngoing(true)
|
||||
setOnlyAlertOnce(true)
|
||||
color = ContextCompat.getColor(context, R.color.colorAccent)
|
||||
addAction(R.drawable.ic_close_24dp, context.getString(android.R.string.cancel), cancelIntent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the notification containing the currently updating manga and the progress.
|
||||
*
|
||||
* @param manga the manga that's being updated.
|
||||
* @param current the current progress.
|
||||
* @param total the total progress.
|
||||
*/
|
||||
fun showProgressNotification(manga: Manga, current: Int, total: Int) {
|
||||
val title = manga.title
|
||||
|
||||
context.notificationManager.notify(
|
||||
Notifications.ID_LIBRARY_PROGRESS,
|
||||
progressNotificationBuilder
|
||||
.setContentTitle(title)
|
||||
.setProgress(total, current, false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows notification containing update entries that failed with action to open full log.
|
||||
*
|
||||
* @param errors List of entry titles that failed to update.
|
||||
* @param uri Uri for error log file containing all titles that failed.
|
||||
*/
|
||||
fun showUpdateErrorNotification(errors: List<String>, uri: Uri) {
|
||||
if (errors.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
context.notificationManager.notify(
|
||||
Notifications.ID_LIBRARY_ERROR,
|
||||
context.notificationBuilder(Notifications.CHANNEL_LIBRARY) {
|
||||
setContentTitle(context.resources.getQuantityString(R.plurals.notification_update_failed, errors.size, errors.size))
|
||||
setStyle(
|
||||
NotificationCompat.BigTextStyle().bigText(
|
||||
errors.joinToString("\n") {
|
||||
it.chop(TITLE_MAX_LEN)
|
||||
}
|
||||
)
|
||||
)
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
addAction(
|
||||
R.drawable.nnf_ic_file_folder,
|
||||
context.getString(R.string.view_all_errors),
|
||||
NotificationReceiver.openErrorLogPendingActivity(context, uri)
|
||||
)
|
||||
}
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the notification containing the result of the update done by the service.
|
||||
*
|
||||
* @param updates a list of manga with new updates.
|
||||
*/
|
||||
suspend fun showResultNotification(updates: Map<LibraryManga, Array<Chapter>>) {
|
||||
val notifications = ArrayList<Pair<Notification, Int>>()
|
||||
updates.forEach {
|
||||
val manga = it.key
|
||||
val chapters = it.value
|
||||
val chapterNames = chapters.map { chapter -> chapter.name }
|
||||
notifications.add(Pair(context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
try {
|
||||
val request = GetRequest.Builder(context).data(manga)
|
||||
.networkCachePolicy(CachePolicy.DISABLED)
|
||||
.transformations(CircleCropTransformation()).size(width = ICON_SIZE, height = ICON_SIZE)
|
||||
.build()
|
||||
|
||||
Coil.imageLoader(context)
|
||||
.execute(request).drawable?.let { drawable ->
|
||||
setLargeIcon((drawable as BitmapDrawable).bitmap)
|
||||
}
|
||||
} catch (e: Exception) { }
|
||||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
setContentTitle(manga.title)
|
||||
color = ContextCompat.getColor(context, R.color.colorAccent)
|
||||
val chaptersNames = if (chapterNames.size > MAX_CHAPTERS) {
|
||||
"${chapterNames.take(MAX_CHAPTERS - 1)
|
||||
.joinToString(", ")}, " + context.resources.getQuantityString(
|
||||
R.plurals.notification_and_n_more,
|
||||
(chapterNames.size - (MAX_CHAPTERS - 1)),
|
||||
(chapterNames.size - (MAX_CHAPTERS - 1))
|
||||
)
|
||||
} else chapterNames.joinToString(", ")
|
||||
setContentText(chaptersNames)
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(chaptersNames))
|
||||
priority = NotificationCompat.PRIORITY_HIGH
|
||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||
setContentIntent(
|
||||
NotificationReceiver.openChapterPendingActivity(
|
||||
context, manga, chapters.first()
|
||||
)
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_glasses_black_24dp, context.getString(R.string.mark_as_read),
|
||||
NotificationReceiver.markAsReadPendingBroadcast(
|
||||
context,
|
||||
manga, chapters, Notifications.ID_NEW_CHAPTERS
|
||||
)
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_book_white_24dp, context.getString(R.string.view_chapters),
|
||||
NotificationReceiver.openChapterPendingActivity(
|
||||
context,
|
||||
manga, Notifications.ID_NEW_CHAPTERS
|
||||
)
|
||||
)
|
||||
setAutoCancel(true)
|
||||
}, manga.id.hashCode()))
|
||||
}
|
||||
|
||||
NotificationManagerCompat.from(context).apply {
|
||||
|
||||
notify(
|
||||
Notifications.ID_NEW_CHAPTERS,
|
||||
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
setLargeIcon(notificationBitmap)
|
||||
setContentTitle(context.getString(R.string.new_chapters_found))
|
||||
color = ContextCompat.getColor(context, R.color.colorAccent)
|
||||
if (updates.size > 1) {
|
||||
setContentText(
|
||||
context.resources.getQuantityString(
|
||||
R.plurals
|
||||
.for_n_titles,
|
||||
updates.size, updates.size
|
||||
)
|
||||
)
|
||||
setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.bigText(updates.keys.joinToString("\n") {
|
||||
it.title.chop(45)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
setContentText(updates.keys.first().title.chop(45))
|
||||
}
|
||||
priority = NotificationCompat.PRIORITY_HIGH
|
||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
setGroupSummary(true)
|
||||
setContentIntent(getNotificationIntent())
|
||||
setAutoCancel(true)
|
||||
})
|
||||
|
||||
notifications.forEach {
|
||||
notify(it.second, it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the progress notification.
|
||||
*/
|
||||
fun cancelProgressNotification() {
|
||||
context.notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an intent to open the main activity.
|
||||
*/
|
||||
private fun getNotificationIntent(): PendingIntent {
|
||||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
action = MainActivity.SHORTCUT_RECENTLY_UPDATED
|
||||
}
|
||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_CHAPTERS = 5
|
||||
private const val TITLE_MAX_LEN = 45
|
||||
private const val ICON_SIZE = 192
|
||||
}
|
||||
}
|
@ -1,25 +1,14 @@
|
||||
package eu.kanade.tachiyomi.data.library
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.GROUP_ALERT_SUMMARY
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import coil.Coil
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.GetRequest
|
||||
import coil.request.LoadRequest
|
||||
import coil.transform.CircleCropTransformation
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
@ -30,7 +19,6 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateRanker.rankingScheme
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
|
||||
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
|
||||
@ -39,12 +27,9 @@ import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.fetchMangaDetailsAsync
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.lang.chop
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||
import eu.kanade.tachiyomi.util.system.notification
|
||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -60,7 +45,7 @@ import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.ArrayList
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
@ -86,19 +71,7 @@ class LibraryUpdateService(
|
||||
*/
|
||||
private lateinit var wakeLock: PowerManager.WakeLock
|
||||
|
||||
/**
|
||||
* Pending intent of action that cancels the library update
|
||||
*/
|
||||
private val cancelIntent by lazy {
|
||||
NotificationReceiver.cancelLibraryUpdatePendingBroadcast(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitmap of the app for notifications.
|
||||
*/
|
||||
private val notificationBitmap by lazy {
|
||||
BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
|
||||
}
|
||||
private lateinit var notifier: LibraryUpdateNotifier
|
||||
|
||||
private var job: Job? = null
|
||||
|
||||
@ -111,6 +84,9 @@ class LibraryUpdateService(
|
||||
// List containing new updates
|
||||
private val newUpdates = mutableMapOf<LibraryManga, Array<Chapter>>()
|
||||
|
||||
// List containing failed updates
|
||||
private val failedUpdates = mutableMapOf<Manga, String?>()
|
||||
|
||||
val count = AtomicInteger(0)
|
||||
val jobCount = AtomicInteger(0)
|
||||
|
||||
@ -131,19 +107,6 @@ class LibraryUpdateService(
|
||||
preferences.deleteRemovedChapters().get() != 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached progress notification to avoid creating a lot.
|
||||
*/
|
||||
private val progressNotification by lazy {
|
||||
NotificationCompat.Builder(this, Notifications.CHANNEL_LIBRARY)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setSmallIcon(R.drawable.ic_refresh_white_24dp_img).setLargeIcon(notificationBitmap)
|
||||
.setOngoing(true).setOnlyAlertOnce(true)
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorAccent)).addAction(
|
||||
R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines what should be updated within a service execution.
|
||||
*/
|
||||
@ -315,11 +278,12 @@ class LibraryUpdateService(
|
||||
*/
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
startForeground(Notifications.ID_LIBRARY_PROGRESS, progressNotification.build())
|
||||
notifier = LibraryUpdateNotifier(this)
|
||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock"
|
||||
)
|
||||
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
||||
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -407,11 +371,11 @@ class LibraryUpdateService(
|
||||
private suspend fun finishUpdates() {
|
||||
if (jobCount.get() != 0) return
|
||||
if (newUpdates.isNotEmpty()) {
|
||||
showResultNotification(newUpdates)
|
||||
notifier.showResultNotification(newUpdates)
|
||||
|
||||
if (preferences.refreshCoversToo().getOrDefault() && job?.isCancelled == false) {
|
||||
updateDetails(newUpdates.keys.toList())
|
||||
cancelProgressNotification()
|
||||
notifier.cancelProgressNotification()
|
||||
if (downloadNew && hasDownloads) {
|
||||
DownloadService.start(this)
|
||||
}
|
||||
@ -420,7 +384,15 @@ class LibraryUpdateService(
|
||||
}
|
||||
newUpdates.clear()
|
||||
}
|
||||
cancelProgressNotification()
|
||||
if (preferences.showLibraryUpdateErrors() && failedUpdates.isNotEmpty()) {
|
||||
val errorFile = writeErrorFile(failedUpdates)
|
||||
notifier.showUpdateErrorNotification(
|
||||
failedUpdates.map { it.key.title },
|
||||
errorFile.getUriCompat(this)
|
||||
)
|
||||
}
|
||||
failedUpdates.clear()
|
||||
notifier.cancelProgressNotification()
|
||||
}
|
||||
|
||||
private suspend fun updateMangaInSource(
|
||||
@ -459,7 +431,7 @@ class LibraryUpdateService(
|
||||
if (job?.isCancelled == true) {
|
||||
return false
|
||||
}
|
||||
showProgressNotification(manga, progress, mangaToUpdate.size)
|
||||
notifier.showProgressNotification(manga, progress, mangaToUpdate.size)
|
||||
val source = sourceManager.get(manga.source) as? HttpSource ?: return false
|
||||
val fetchedChapters = withContext(Dispatchers.IO) {
|
||||
source.fetchChapterList(manga).toBlocking().single()
|
||||
@ -489,6 +461,7 @@ class LibraryUpdateService(
|
||||
return hasDownloads
|
||||
} catch (e: Exception) {
|
||||
if (e !is CancellationException) {
|
||||
failedUpdates[manga] = e.message
|
||||
Timber.e("Failed updating: ${manga.title}: $e")
|
||||
}
|
||||
return false
|
||||
@ -496,14 +469,9 @@ class LibraryUpdateService(
|
||||
}
|
||||
|
||||
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
// we need to get the chapters from the db so we have chapter ids
|
||||
val mangaChapters = db.getChapters(manga).executeAsBlocking()
|
||||
val dbChapters = chapters.map {
|
||||
mangaChapters.find { mangaChapter -> mangaChapter.url == it.url }!!
|
||||
}
|
||||
// We don't want to start downloading while the library is updating, because websites
|
||||
// may don't like it and they could ban the user.
|
||||
downloadManager.downloadChapters(manga, dbChapters, false)
|
||||
downloadManager.downloadChapters(manga, chapters, false)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -523,7 +491,11 @@ class LibraryUpdateService(
|
||||
return@async
|
||||
}
|
||||
val source = sourceManager.get(manga.source) as? HttpSource ?: return@async
|
||||
showProgressNotification(manga, count.andIncrement, mangaToUpdate.size)
|
||||
notifier.showProgressNotification(
|
||||
manga,
|
||||
count.andIncrement,
|
||||
mangaToUpdate.size
|
||||
)
|
||||
|
||||
val networkManga = try {
|
||||
source.fetchMangaDetailsAsync(manga)
|
||||
@ -550,7 +522,7 @@ class LibraryUpdateService(
|
||||
}
|
||||
}
|
||||
asyncList.awaitAll()
|
||||
cancelProgressNotification()
|
||||
notifier.cancelProgressNotification()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -565,7 +537,7 @@ class LibraryUpdateService(
|
||||
val loggedServices = trackManager.services.filter { it.isLogged }
|
||||
|
||||
mangaToUpdate.forEach { manga ->
|
||||
showProgressNotification(manga, count++, mangaToUpdate.size)
|
||||
notifier.showProgressNotification(manga, count++, mangaToUpdate.size)
|
||||
|
||||
val tracks = db.getTracks(manga).executeAsBlocking()
|
||||
|
||||
@ -581,143 +553,28 @@ class LibraryUpdateService(
|
||||
}
|
||||
}
|
||||
}
|
||||
cancelProgressNotification()
|
||||
notifier.cancelProgressNotification()
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the notification containing the currently updating manga and the progress.
|
||||
*
|
||||
* @param manga the manga that's being updated.
|
||||
* @param current the current progress.
|
||||
* @param total the total progress.
|
||||
* Writes basic file of update errors to cache dir.
|
||||
*/
|
||||
private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
|
||||
notificationManager.notify(
|
||||
Notifications.ID_LIBRARY_PROGRESS, progressNotification
|
||||
.setContentTitle(manga.title)
|
||||
.setProgress(total, current, false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
private fun writeErrorFile(errors: Map<Manga, String?>): File {
|
||||
try {
|
||||
if (errors.isNotEmpty()) {
|
||||
val destFile = File(externalCacheDir, "tachiyomi_update_errors.txt")
|
||||
|
||||
/**
|
||||
* Shows the notification containing the result of the update done by the service.
|
||||
*
|
||||
* @param updates a list of manga with new updates.
|
||||
*/
|
||||
private suspend fun showResultNotification(updates: Map<LibraryManga, Array<Chapter>>) {
|
||||
val notifications = ArrayList<Pair<Notification, Int>>()
|
||||
updates.forEach {
|
||||
val manga = it.key
|
||||
val chapters = it.value
|
||||
val chapterNames = chapters.map { chapter -> chapter.name }
|
||||
notifications.add(Pair(notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
try {
|
||||
|
||||
val request = GetRequest.Builder(this@LibraryUpdateService).data(manga)
|
||||
.networkCachePolicy(CachePolicy.DISABLED)
|
||||
.transformations(CircleCropTransformation()).size(width = 256, height = 256)
|
||||
.build()
|
||||
|
||||
Coil.imageLoader(this@LibraryUpdateService)
|
||||
.execute(request).drawable?.let { drawable ->
|
||||
setLargeIcon((drawable as BitmapDrawable).bitmap)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
||||
setContentTitle(manga.title)
|
||||
color = ContextCompat.getColor(this@LibraryUpdateService, R.color.colorAccent)
|
||||
val chaptersNames = if (chapterNames.size > 5) {
|
||||
"${chapterNames.take(4).joinToString(", ")}, " +
|
||||
resources.getQuantityString(
|
||||
R.plurals.notification_and_n_more,
|
||||
(chapterNames.size - 4), (chapterNames.size - 4)
|
||||
)
|
||||
} else chapterNames.joinToString(", ")
|
||||
setContentText(chaptersNames)
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(chaptersNames))
|
||||
priority = NotificationCompat.PRIORITY_HIGH
|
||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||
setContentIntent(
|
||||
NotificationReceiver.openChapterPendingActivity(
|
||||
this@LibraryUpdateService, manga, chapters.first()
|
||||
)
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_glasses_black_24dp, getString(R.string.mark_as_read),
|
||||
NotificationReceiver.markAsReadPendingBroadcast(
|
||||
this@LibraryUpdateService,
|
||||
manga, chapters, Notifications.ID_NEW_CHAPTERS
|
||||
)
|
||||
)
|
||||
addAction(
|
||||
R.drawable.ic_book_white_24dp, getString(R.string.view_chapters),
|
||||
NotificationReceiver.openChapterPendingActivity(
|
||||
this@LibraryUpdateService,
|
||||
manga, Notifications.ID_NEW_CHAPTERS
|
||||
)
|
||||
)
|
||||
setAutoCancel(true)
|
||||
}, manga.id.hashCode()))
|
||||
}
|
||||
|
||||
NotificationManagerCompat.from(this).apply {
|
||||
|
||||
notify(
|
||||
Notifications.ID_NEW_CHAPTERS,
|
||||
notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
setLargeIcon(notificationBitmap)
|
||||
setContentTitle(getString(R.string.new_chapters_found))
|
||||
color = ContextCompat.getColor(applicationContext, R.color.colorAccent)
|
||||
if (updates.size > 1) {
|
||||
setContentText(
|
||||
resources.getQuantityString(
|
||||
R.plurals
|
||||
.for_n_titles,
|
||||
updates.size, updates.size
|
||||
)
|
||||
)
|
||||
setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.bigText(updates.keys.joinToString("\n") {
|
||||
it.title.chop(45)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
setContentText(updates.keys.first().title.chop(45))
|
||||
destFile.bufferedWriter().use { out ->
|
||||
errors.forEach { (manga, error) ->
|
||||
out.write("${manga.title}: $error\n")
|
||||
}
|
||||
priority = NotificationCompat.PRIORITY_HIGH
|
||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
||||
setGroupSummary(true)
|
||||
setContentIntent(getNotificationIntent())
|
||||
setAutoCancel(true)
|
||||
})
|
||||
|
||||
notifications.forEach {
|
||||
notify(it.second, it.first)
|
||||
}
|
||||
return destFile
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the progress notification.
|
||||
*/
|
||||
private fun cancelProgressNotification() {
|
||||
notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an intent to open the main activity.
|
||||
*/
|
||||
private fun getNotificationIntent(): PendingIntent {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
intent.action = MainActivity.SHORTCUT_RECENTLY_UPDATED
|
||||
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
return File("")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,6 +435,22 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [PendingIntent] that opens the error log file in an external viewer
|
||||
*
|
||||
* @param context context of application
|
||||
* @param uri uri of error log file
|
||||
* @return [PendingIntent]
|
||||
*/
|
||||
internal fun openErrorLogPendingActivity(context: Context, uri: Uri): PendingIntent {
|
||||
val intent = Intent().apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
setDataAndType(uri, "text/plain")
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
}
|
||||
return PendingIntent.getActivity(context, 0, intent, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [PendingIntent] that opens the extensions controller,
|
||||
*
|
||||
|
@ -24,6 +24,7 @@ object Notifications {
|
||||
*/
|
||||
const val CHANNEL_LIBRARY = "library_channel"
|
||||
const val ID_LIBRARY_PROGRESS = -101
|
||||
const val ID_LIBRARY_ERROR = -102
|
||||
|
||||
/**
|
||||
* Notification channel and ids used by the downloader.
|
||||
|
@ -121,8 +121,6 @@ object PreferenceKeys {
|
||||
|
||||
const val uniformGrid = "uniform_grid"
|
||||
|
||||
const val libraryAsSingleList = "library_as_single_list"
|
||||
|
||||
const val lang = "app_language"
|
||||
|
||||
const val dateFormat = "app_date_format"
|
||||
@ -149,6 +147,8 @@ object PreferenceKeys {
|
||||
|
||||
const val updateOnRefresh = "update_on_refresh"
|
||||
|
||||
const val showLibraryUpdateErrors = "show_library_update_errors"
|
||||
|
||||
const val alwaysShowChapterTransition = "always_show_chapter_transition"
|
||||
|
||||
@Deprecated("Use the preferences of the source")
|
||||
|
@ -278,6 +278,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun onlySearchPinned() = flowPrefs.getBoolean(Keys.onlySearchPinned, true)
|
||||
|
||||
fun showLibraryUpdateErrors() = prefs.getBoolean(Keys.showLibraryUpdateErrors, false)
|
||||
|
||||
// Tutorial preferences
|
||||
fun shownFilterTutorial() = flowPrefs.getBoolean("shown_filter_tutorial", false)
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package eu.kanade.tachiyomi.network
|
||||
|
||||
import android.content.Context
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
@ -17,7 +16,7 @@ class NetworkHelper(context: Context) {
|
||||
val client = OkHttpClient.Builder()
|
||||
.cookieJar(cookieManager)
|
||||
.cache(Cache(cacheDir, cacheSize))
|
||||
.addInterceptor(ChuckerInterceptor(context))
|
||||
// .addInterceptor(ChuckerInterceptor(context))
|
||||
.build()
|
||||
|
||||
val cloudflareClient = client.newBuilder()
|
||||
|
@ -159,6 +159,12 @@ class SettingsLibraryController : SettingsController() {
|
||||
summaryRes = R.string.auto_refresh_covers_summary
|
||||
defaultValue = true
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
key = Keys.showLibraryUpdateErrors
|
||||
titleRes = R.string.show_notification_error
|
||||
defaultValue = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +170,10 @@
|
||||
<item quantity="one">and %1$d more chapter</item>
|
||||
<item quantity="other">and %1$d more chapters</item>
|
||||
</plurals>
|
||||
<plurals name="notification_update_failed">
|
||||
<item quantity="one">1 update failed</item>
|
||||
<item quantity="other">%1$d updates failed</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Library settings -->
|
||||
<string name="library_update_frequency">Library update frequency</string>
|
||||
@ -186,6 +190,7 @@
|
||||
<string name="auto_refresh_covers">Automatically refresh covers</string>
|
||||
<string name="auto_refresh_covers_summary">Refresh covers in library as well
|
||||
when updating library</string>
|
||||
<string name="show_notification_error">Show a notification for errors</string>
|
||||
|
||||
<!-- Recents -->
|
||||
<string name="recents">Recents</string>
|
||||
|
Loading…
Reference in New Issue
Block a user