mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-07 11:17:25 +01:00
Move notification logic out of LibraryUpdateService
This commit is contained in:
parent
dd1b5c7ea7
commit
530daeaa3a
@ -70,11 +70,11 @@ class BackupCreateService : Service() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
notifier = BackupNotifier(this)
|
notifier = BackupNotifier(this)
|
||||||
|
wakeLock = acquireWakeLock(javaClass.name)
|
||||||
|
|
||||||
startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
|
startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
|
||||||
|
|
||||||
wakeLock = acquireWakeLock(javaClass.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopService(name: Intent?): Boolean {
|
override fun stopService(name: Intent?): Boolean {
|
||||||
|
@ -124,11 +124,11 @@ class BackupRestoreService : Service() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
notifier = BackupNotifier(this)
|
notifier = BackupNotifier(this)
|
||||||
|
wakeLock = acquireWakeLock(javaClass.name)
|
||||||
|
|
||||||
startForeground(Notifications.ID_RESTORE_PROGRESS, notifier.showRestoreProgress().build())
|
startForeground(Notifications.ID_RESTORE_PROGRESS, notifier.showRestoreProgress().build())
|
||||||
|
|
||||||
wakeLock = acquireWakeLock(javaClass.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopService(name: Intent?): Boolean {
|
override fun stopService(name: Intent?): Boolean {
|
||||||
|
@ -0,0 +1,268 @@
|
|||||||
|
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.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
|
||||||
|
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 java.text.DecimalFormat
|
||||||
|
import java.text.DecimalFormatSymbols
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 = if (preferences.hideNotificationContent()) {
|
||||||
|
context.getString(R.string.notification_check_updates)
|
||||||
|
} else {
|
||||||
|
manga.title
|
||||||
|
}
|
||||||
|
|
||||||
|
context.notificationManager.notify(
|
||||||
|
Notifications.ID_LIBRARY_PROGRESS,
|
||||||
|
progressNotificationBuilder
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setProgress(total, current, false)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the notification containing the result of the update done by the service.
|
||||||
|
*
|
||||||
|
* @param updates a list of manga with new updates.
|
||||||
|
*/
|
||||||
|
fun showUpdateNotifications(updates: List<Pair<Manga, Array<Chapter>>>) {
|
||||||
|
if (updates.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationManagerCompat.from(context).apply {
|
||||||
|
// Parent group notification
|
||||||
|
notify(
|
||||||
|
Notifications.ID_NEW_CHAPTERS,
|
||||||
|
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||||
|
setContentTitle(context.getString(R.string.notification_new_chapters))
|
||||||
|
if (updates.size == 1 && !preferences.hideNotificationContent()) {
|
||||||
|
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
||||||
|
} else {
|
||||||
|
setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
|
||||||
|
|
||||||
|
if (!preferences.hideNotificationContent()) {
|
||||||
|
setStyle(
|
||||||
|
NotificationCompat.BigTextStyle().bigText(
|
||||||
|
updates.joinToString("\n") {
|
||||||
|
it.first.title.chop(NOTIF_TITLE_MAX_LEN)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
|
setLargeIcon(notificationBitmap)
|
||||||
|
|
||||||
|
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||||
|
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
|
setGroupSummary(true)
|
||||||
|
priority = NotificationCompat.PRIORITY_HIGH
|
||||||
|
|
||||||
|
setContentIntent(getNotificationIntent())
|
||||||
|
setAutoCancel(true)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Per-manga notification
|
||||||
|
if (!preferences.hideNotificationContent()) {
|
||||||
|
updates.forEach {
|
||||||
|
val (manga, chapters) = it
|
||||||
|
notify(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNewChaptersNotification(manga: Manga, chapters: Array<Chapter>): Notification {
|
||||||
|
return context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||||
|
setContentTitle(manga.title)
|
||||||
|
|
||||||
|
val description = getNewChaptersDescription(chapters)
|
||||||
|
setContentText(description)
|
||||||
|
setStyle(NotificationCompat.BigTextStyle().bigText(description))
|
||||||
|
|
||||||
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
|
|
||||||
|
val icon = getMangaIcon(manga)
|
||||||
|
if (icon != null) {
|
||||||
|
setLargeIcon(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||||
|
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
|
priority = NotificationCompat.PRIORITY_HIGH
|
||||||
|
|
||||||
|
// Open first chapter on tap
|
||||||
|
setContentIntent(NotificationReceiver.openChapterPendingActivity(context, manga, chapters.first()))
|
||||||
|
setAutoCancel(true)
|
||||||
|
|
||||||
|
// Mark chapters as read action
|
||||||
|
addAction(
|
||||||
|
R.drawable.ic_glasses_black_24dp, context.getString(R.string.action_mark_as_read),
|
||||||
|
NotificationReceiver.markAsReadPendingBroadcast(
|
||||||
|
context,
|
||||||
|
manga, chapters, Notifications.ID_NEW_CHAPTERS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// View chapters action
|
||||||
|
addAction(
|
||||||
|
R.drawable.ic_book_24dp, context.getString(R.string.action_view_chapters),
|
||||||
|
NotificationReceiver.openChapterPendingActivity(
|
||||||
|
context,
|
||||||
|
manga, Notifications.ID_NEW_CHAPTERS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the progress notification.
|
||||||
|
*/
|
||||||
|
fun cancelProgressNotification() {
|
||||||
|
context.notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMangaIcon(manga: Manga): Bitmap? {
|
||||||
|
return try {
|
||||||
|
Glide.with(context)
|
||||||
|
.asBitmap()
|
||||||
|
.load(manga.toMangaThumbnail())
|
||||||
|
.dontTransform()
|
||||||
|
.centerCrop()
|
||||||
|
.circleCrop()
|
||||||
|
.override(
|
||||||
|
NOTIF_ICON_SIZE,
|
||||||
|
NOTIF_ICON_SIZE
|
||||||
|
)
|
||||||
|
.submit()
|
||||||
|
.get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNewChaptersDescription(chapters: Array<Chapter>): String {
|
||||||
|
val formatter = DecimalFormat(
|
||||||
|
"#.###",
|
||||||
|
DecimalFormatSymbols()
|
||||||
|
.apply { decimalSeparator = '.' }
|
||||||
|
)
|
||||||
|
|
||||||
|
val displayableChapterNumbers = chapters
|
||||||
|
.filter { it.isRecognizedNumber }
|
||||||
|
.sortedBy { it.chapter_number }
|
||||||
|
.map { formatter.format(it.chapter_number) }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
return when (displayableChapterNumbers.size) {
|
||||||
|
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
||||||
|
0 -> {
|
||||||
|
// "1 new chapter" or "5 new chapters"
|
||||||
|
context.resources.getQuantityString(R.plurals.notification_chapters_generic, chapters.size, chapters.size)
|
||||||
|
}
|
||||||
|
// Only 1 chapter has a parsed chapter number
|
||||||
|
1 -> {
|
||||||
|
val remaining = chapters.size - displayableChapterNumbers.size
|
||||||
|
if (remaining == 0) {
|
||||||
|
// "Chapter 2.5"
|
||||||
|
context.resources.getString(R.string.notification_chapters_single, displayableChapterNumbers.first())
|
||||||
|
} else {
|
||||||
|
// "Chapter 2.5 and 10 more"
|
||||||
|
context.resources.getString(R.string.notification_chapters_single_and_more, displayableChapterNumbers.first(), remaining)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Everything else (i.e. multiple parsed chapter numbers)
|
||||||
|
else -> {
|
||||||
|
val shouldTruncate = displayableChapterNumbers.size > NOTIF_MAX_CHAPTERS
|
||||||
|
if (shouldTruncate) {
|
||||||
|
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
||||||
|
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
||||||
|
val joinedChapterNumbers = displayableChapterNumbers.take(NOTIF_MAX_CHAPTERS).joinToString(", ")
|
||||||
|
context.resources.getQuantityString(R.plurals.notification_chapters_multiple_and_more, remaining, joinedChapterNumbers, remaining)
|
||||||
|
} else {
|
||||||
|
// "Chapters 1, 2.5, 3"
|
||||||
|
context.resources.getString(R.string.notification_chapters_multiple, displayableChapterNumbers.joinToString(", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 NOTIF_MAX_CHAPTERS = 5
|
||||||
|
private const val NOTIF_TITLE_MAX_LEN = 45
|
||||||
|
private const val NOTIF_ICON_SIZE = 192
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,11 @@
|
|||||||
package eu.kanade.tachiyomi.data.library
|
package eu.kanade.tachiyomi.data.library
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationCompat.GROUP_ALERT_SUMMARY
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
import eu.kanade.tachiyomi.data.database.models.Category
|
||||||
@ -23,26 +14,17 @@ import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||||
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateRanker.rankingScheme
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateRanker.rankingScheme
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
|
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.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.lang.chop
|
|
||||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||||
import eu.kanade.tachiyomi.util.system.notification
|
|
||||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
|
||||||
import java.text.DecimalFormat
|
|
||||||
import java.text.DecimalFormatSymbols
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -74,39 +56,13 @@ class LibraryUpdateService(
|
|||||||
*/
|
*/
|
||||||
private lateinit var wakeLock: PowerManager.WakeLock
|
private lateinit var wakeLock: PowerManager.WakeLock
|
||||||
|
|
||||||
|
private lateinit var notifier: LibraryUpdateNotifier
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription where the update is done.
|
* Subscription where the update is done.
|
||||||
*/
|
*/
|
||||||
private var subscription: Subscription? = null
|
private var subscription: Subscription? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached progress notification to avoid creating a lot.
|
|
||||||
*/
|
|
||||||
private val progressNotificationBuilder by lazy {
|
|
||||||
notificationBuilder(Notifications.CHANNEL_LIBRARY) {
|
|
||||||
setContentTitle(getString(R.string.app_name))
|
|
||||||
setSmallIcon(R.drawable.ic_refresh_24dp)
|
|
||||||
setLargeIcon(notificationBitmap)
|
|
||||||
setOngoing(true)
|
|
||||||
setOnlyAlertOnce(true)
|
|
||||||
addAction(R.drawable.ic_close_24dp, getString(android.R.string.cancel), cancelIntent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines what should be updated within a service execution.
|
* Defines what should be updated within a service execution.
|
||||||
*/
|
*/
|
||||||
@ -128,10 +84,6 @@ class LibraryUpdateService(
|
|||||||
*/
|
*/
|
||||||
const val KEY_TARGET = "target"
|
const val KEY_TARGET = "target"
|
||||||
|
|
||||||
private const val NOTIF_MAX_CHAPTERS = 5
|
|
||||||
private const val NOTIF_TITLE_MAX_LEN = 45
|
|
||||||
private const val NOTIF_ICON_SIZE = 192
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of the service.
|
* Returns the status of the service.
|
||||||
*
|
*
|
||||||
@ -186,9 +138,10 @@ class LibraryUpdateService(
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
startForeground(Notifications.ID_LIBRARY_PROGRESS, progressNotificationBuilder.build())
|
notifier = LibraryUpdateNotifier(this)
|
||||||
|
|
||||||
wakeLock = acquireWakeLock(javaClass.name)
|
wakeLock = acquireWakeLock(javaClass.name)
|
||||||
|
|
||||||
|
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -316,7 +269,7 @@ class LibraryUpdateService(
|
|||||||
{ bySource ->
|
{ bySource ->
|
||||||
bySource
|
bySource
|
||||||
// Notify manga that will update.
|
// Notify manga that will update.
|
||||||
.doOnNext { showProgressNotification(it, count.andIncrement, mangaToUpdate.size) }
|
.doOnNext { notifier.showProgressNotification(it, count.andIncrement, mangaToUpdate.size) }
|
||||||
.concatMap { manga ->
|
.concatMap { manga ->
|
||||||
updateManga(manga)
|
updateManga(manga)
|
||||||
// If there's any error, return empty update and continue.
|
// If there's any error, return empty update and continue.
|
||||||
@ -358,7 +311,7 @@ class LibraryUpdateService(
|
|||||||
// Notify result of the overall update.
|
// Notify result of the overall update.
|
||||||
.doOnCompleted {
|
.doOnCompleted {
|
||||||
if (newUpdates.isNotEmpty()) {
|
if (newUpdates.isNotEmpty()) {
|
||||||
showUpdateNotifications(newUpdates)
|
notifier.showUpdateNotifications(newUpdates)
|
||||||
if (downloadNew && hasDownloads) {
|
if (downloadNew && hasDownloads) {
|
||||||
DownloadService.start(this)
|
DownloadService.start(this)
|
||||||
}
|
}
|
||||||
@ -368,7 +321,7 @@ class LibraryUpdateService(
|
|||||||
Timber.e("Failed updating: ${failedUpdates.map { it.title }}")
|
Timber.e("Failed updating: ${failedUpdates.map { it.title }}")
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelProgressNotification()
|
notifier.cancelProgressNotification()
|
||||||
}
|
}
|
||||||
.map { manga -> manga.first }
|
.map { manga -> manga.first }
|
||||||
}
|
}
|
||||||
@ -415,14 +368,14 @@ class LibraryUpdateService(
|
|||||||
var count = 0
|
var count = 0
|
||||||
|
|
||||||
return Observable.from(mangaToUpdate)
|
return Observable.from(mangaToUpdate)
|
||||||
.doOnNext { showProgressNotification(it, count++, mangaToUpdate.size) }
|
.doOnNext { notifier.showProgressNotification(it, count++, mangaToUpdate.size) }
|
||||||
.map { manga ->
|
.map { manga ->
|
||||||
manga.prepUpdateCover(coverCache)
|
manga.prepUpdateCover(coverCache)
|
||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).executeAsBlocking()
|
||||||
manga
|
manga
|
||||||
}
|
}
|
||||||
.doOnCompleted {
|
.doOnCompleted {
|
||||||
cancelProgressNotification()
|
notifier.cancelProgressNotification()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +392,7 @@ class LibraryUpdateService(
|
|||||||
// Emit each manga and update it sequentially.
|
// Emit each manga and update it sequentially.
|
||||||
return Observable.from(mangaToUpdate)
|
return Observable.from(mangaToUpdate)
|
||||||
// Notify manga that will update.
|
// Notify manga that will update.
|
||||||
.doOnNext { showProgressNotification(it, count++, mangaToUpdate.size) }
|
.doOnNext { notifier.showProgressNotification(it, count++, mangaToUpdate.size) }
|
||||||
// Update the tracking details.
|
// Update the tracking details.
|
||||||
.concatMap { manga ->
|
.concatMap { manga ->
|
||||||
val tracks = db.getTracks(manga).executeAsBlocking()
|
val tracks = db.getTracks(manga).executeAsBlocking()
|
||||||
@ -458,207 +411,7 @@ class LibraryUpdateService(
|
|||||||
.map { manga }
|
.map { manga }
|
||||||
}
|
}
|
||||||
.doOnCompleted {
|
.doOnCompleted {
|
||||||
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.
|
|
||||||
*/
|
|
||||||
private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
|
|
||||||
val title = if (preferences.hideNotificationContent()) {
|
|
||||||
getString(R.string.notification_check_updates)
|
|
||||||
} else {
|
|
||||||
manga.title
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationManager.notify(
|
|
||||||
Notifications.ID_LIBRARY_PROGRESS,
|
|
||||||
progressNotificationBuilder
|
|
||||||
.setContentTitle(title)
|
|
||||||
.setProgress(total, current, false)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the notification containing the result of the update done by the service.
|
|
||||||
*
|
|
||||||
* @param updates a list of manga with new updates.
|
|
||||||
*/
|
|
||||||
private fun showUpdateNotifications(updates: List<Pair<Manga, Array<Chapter>>>) {
|
|
||||||
if (updates.isEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationManagerCompat.from(this).apply {
|
|
||||||
// Parent group notification
|
|
||||||
notify(
|
|
||||||
Notifications.ID_NEW_CHAPTERS,
|
|
||||||
notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
|
||||||
setContentTitle(getString(R.string.notification_new_chapters))
|
|
||||||
if (updates.size == 1 && !preferences.hideNotificationContent()) {
|
|
||||||
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
|
||||||
} else {
|
|
||||||
setContentText(resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
|
|
||||||
|
|
||||||
if (!preferences.hideNotificationContent()) {
|
|
||||||
setStyle(
|
|
||||||
NotificationCompat.BigTextStyle().bigText(
|
|
||||||
updates.joinToString("\n") {
|
|
||||||
it.first.title.chop(NOTIF_TITLE_MAX_LEN)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
|
||||||
setLargeIcon(notificationBitmap)
|
|
||||||
|
|
||||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
|
||||||
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
|
||||||
setGroupSummary(true)
|
|
||||||
priority = NotificationCompat.PRIORITY_HIGH
|
|
||||||
|
|
||||||
setContentIntent(getNotificationIntent())
|
|
||||||
setAutoCancel(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Per-manga notification
|
|
||||||
if (!preferences.hideNotificationContent()) {
|
|
||||||
updates.forEach {
|
|
||||||
val (manga, chapters) = it
|
|
||||||
notify(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNewChaptersNotification(manga: Manga, chapters: Array<Chapter>): Notification {
|
|
||||||
return notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
|
||||||
setContentTitle(manga.title)
|
|
||||||
|
|
||||||
val description = getNewChaptersDescription(chapters)
|
|
||||||
setContentText(description)
|
|
||||||
setStyle(NotificationCompat.BigTextStyle().bigText(description))
|
|
||||||
|
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
|
||||||
|
|
||||||
val icon = getMangaIcon(manga)
|
|
||||||
if (icon != null) {
|
|
||||||
setLargeIcon(icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
|
||||||
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
|
||||||
priority = NotificationCompat.PRIORITY_HIGH
|
|
||||||
|
|
||||||
// Open first chapter on tap
|
|
||||||
setContentIntent(NotificationReceiver.openChapterPendingActivity(this@LibraryUpdateService, manga, chapters.first()))
|
|
||||||
setAutoCancel(true)
|
|
||||||
|
|
||||||
// Mark chapters as read action
|
|
||||||
addAction(
|
|
||||||
R.drawable.ic_glasses_black_24dp, getString(R.string.action_mark_as_read),
|
|
||||||
NotificationReceiver.markAsReadPendingBroadcast(
|
|
||||||
this@LibraryUpdateService,
|
|
||||||
manga, chapters, Notifications.ID_NEW_CHAPTERS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// View chapters action
|
|
||||||
addAction(
|
|
||||||
R.drawable.ic_book_24dp, getString(R.string.action_view_chapters),
|
|
||||||
NotificationReceiver.openChapterPendingActivity(
|
|
||||||
this@LibraryUpdateService,
|
|
||||||
manga, Notifications.ID_NEW_CHAPTERS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the progress notification.
|
|
||||||
*/
|
|
||||||
private fun cancelProgressNotification() {
|
|
||||||
notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getMangaIcon(manga: Manga): Bitmap? {
|
|
||||||
return try {
|
|
||||||
Glide.with(this)
|
|
||||||
.asBitmap()
|
|
||||||
.load(manga.toMangaThumbnail())
|
|
||||||
.dontTransform()
|
|
||||||
.centerCrop()
|
|
||||||
.circleCrop()
|
|
||||||
.override(NOTIF_ICON_SIZE, NOTIF_ICON_SIZE)
|
|
||||||
.submit()
|
|
||||||
.get()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNewChaptersDescription(chapters: Array<Chapter>): String {
|
|
||||||
val formatter = DecimalFormat(
|
|
||||||
"#.###",
|
|
||||||
DecimalFormatSymbols()
|
|
||||||
.apply { decimalSeparator = '.' }
|
|
||||||
)
|
|
||||||
|
|
||||||
val displayableChapterNumbers = chapters
|
|
||||||
.filter { it.isRecognizedNumber }
|
|
||||||
.sortedBy { it.chapter_number }
|
|
||||||
.map { formatter.format(it.chapter_number) }
|
|
||||||
.toSet()
|
|
||||||
|
|
||||||
return when (displayableChapterNumbers.size) {
|
|
||||||
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
|
||||||
0 -> {
|
|
||||||
// "1 new chapter" or "5 new chapters"
|
|
||||||
resources.getQuantityString(R.plurals.notification_chapters_generic, chapters.size, chapters.size)
|
|
||||||
}
|
|
||||||
// Only 1 chapter has a parsed chapter number
|
|
||||||
1 -> {
|
|
||||||
val remaining = chapters.size - displayableChapterNumbers.size
|
|
||||||
if (remaining == 0) {
|
|
||||||
// "Chapter 2.5"
|
|
||||||
resources.getString(R.string.notification_chapters_single, displayableChapterNumbers.first())
|
|
||||||
} else {
|
|
||||||
// "Chapter 2.5 and 10 more"
|
|
||||||
resources.getString(R.string.notification_chapters_single_and_more, displayableChapterNumbers.first(), remaining)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Everything else (i.e. multiple parsed chapter numbers)
|
|
||||||
else -> {
|
|
||||||
val shouldTruncate = displayableChapterNumbers.size > NOTIF_MAX_CHAPTERS
|
|
||||||
if (shouldTruncate) {
|
|
||||||
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
|
||||||
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
|
||||||
val joinedChapterNumbers = displayableChapterNumbers.take(NOTIF_MAX_CHAPTERS).joinToString(", ")
|
|
||||||
resources.getQuantityString(R.plurals.notification_chapters_multiple_and_more, remaining, joinedChapterNumbers, remaining)
|
|
||||||
} else {
|
|
||||||
// "Chapters 1, 2.5, 3"
|
|
||||||
resources.getString(R.string.notification_chapters_multiple, displayableChapterNumbers.joinToString(", "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an intent to open the main activity.
|
|
||||||
*/
|
|
||||||
private fun getNotificationIntent(): PendingIntent {
|
|
||||||
val intent = Intent(this, MainActivity::class.java).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
action = MainActivity.SHORTCUT_RECENTLY_UPDATED
|
|
||||||
}
|
|
||||||
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,11 @@ class UpdaterService : Service() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
notifier = UpdaterNotifier(this)
|
notifier = UpdaterNotifier(this)
|
||||||
|
wakeLock = acquireWakeLock(javaClass.name)
|
||||||
|
|
||||||
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted().build())
|
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted().build())
|
||||||
|
|
||||||
wakeLock = acquireWakeLock(javaClass.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user