Add update notifications for EH/EXH galleries

This commit is contained in:
NerdNumber9 2019-08-08 20:04:31 -04:00
parent 14879c4466
commit ac6dbbcd89
4 changed files with 96 additions and 68 deletions

View File

@ -0,0 +1,75 @@
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.os.Build
import android.support.v4.app.NotificationCompat
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.chop
import eu.kanade.tachiyomi.util.notification
import eu.kanade.tachiyomi.util.notificationManager
class LibraryUpdateNotifier(private val context: Context) {
/**
* Bitmap of the app for notifications.
*/
val notificationBitmap by lazy {
BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)
}
/**
* Shows the notification containing the result of the update done by the service.
*
* @param updates a list of manga with new updates.
*/
fun showResultNotification(updates: List<Manga>) {
val newUpdates = updates.map { it.title.chop(45) }.toMutableSet()
// Append new chapters from a previous, existing notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val previousNotification = context.notificationManager.activeNotifications
.find { it.id == Notifications.ID_LIBRARY_RESULT }
if (previousNotification != null) {
val oldUpdates = previousNotification.notification.extras
.getString(Notification.EXTRA_BIG_TEXT)
if (!oldUpdates.isNullOrEmpty()) {
newUpdates += oldUpdates.split("\n")
}
}
}
context.notificationManager.notify(Notifications.ID_LIBRARY_RESULT, context.notification(Notifications.CHANNEL_LIBRARY) {
setSmallIcon(R.drawable.ic_book_white_24dp)
setLargeIcon(notificationBitmap)
setContentTitle(context.getString(R.string.notification_new_chapters))
if (newUpdates.size > 1) {
setContentText(context.getString(R.string.notification_new_chapters_text, newUpdates.size))
setStyle(NotificationCompat.BigTextStyle().bigText(newUpdates.joinToString("\n")))
setNumber(newUpdates.size)
} else {
setContentText(newUpdates.first())
}
priority = NotificationCompat.PRIORITY_HIGH
setContentIntent(getNotificationIntent(context))
setAutoCancel(true)
})
}
/**
* Returns an intent to open the main activity.
*/
private fun getNotificationIntent(context: Context): PendingIntent {
val intent = Intent(context, 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(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
}

View File

@ -1,11 +1,8 @@
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.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
@ -28,7 +25,6 @@ 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.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.* import eu.kanade.tachiyomi.util.*
import exh.LIBRARY_UPDATE_EXCLUDED_SOURCES import exh.LIBRARY_UPDATE_EXCLUDED_SOURCES
import rx.Observable import rx.Observable
@ -73,12 +69,7 @@ class LibraryUpdateService(
NotificationReceiver.cancelLibraryUpdatePendingBroadcast(this) NotificationReceiver.cancelLibraryUpdatePendingBroadcast(this)
} }
/** private val updateNotifier by lazy { LibraryUpdateNotifier(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. * Cached progress notification to avoid creating a lot.
@ -86,7 +77,7 @@ class LibraryUpdateService(
private val progressNotification by lazy { NotificationCompat.Builder(this, Notifications.CHANNEL_LIBRARY) private val progressNotification by lazy { NotificationCompat.Builder(this, Notifications.CHANNEL_LIBRARY)
.setContentTitle(getString(R.string.app_name)) .setContentTitle(getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_refresh_white_24dp_img) .setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
.setLargeIcon(notificationBitmap) .setLargeIcon(updateNotifier.notificationBitmap)
.setOngoing(true) .setOngoing(true)
.setOnlyAlertOnce(true) .setOnlyAlertOnce(true)
.addAction(R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent) .addAction(R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent)
@ -319,7 +310,7 @@ class LibraryUpdateService(
// Notify result of the overall update. // Notify result of the overall update.
.doOnCompleted { .doOnCompleted {
if (newUpdates.isNotEmpty()) { if (newUpdates.isNotEmpty()) {
showResultNotification(newUpdates) updateNotifier.showResultNotification(newUpdates)
if (downloadNew && hasDownloads) { if (downloadNew && hasDownloads) {
DownloadService.start(this) DownloadService.start(this)
} }
@ -439,61 +430,10 @@ class LibraryUpdateService(
.build()) .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 showResultNotification(updates: List<Manga>) {
val newUpdates = updates.map { it.title.chop(45) }.toMutableSet()
// Append new chapters from a previous, existing notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val previousNotification = notificationManager.activeNotifications
.find { it.id == Notifications.ID_LIBRARY_RESULT }
if (previousNotification != null) {
val oldUpdates = previousNotification.notification.extras
.getString(Notification.EXTRA_BIG_TEXT)
if (!oldUpdates.isNullOrEmpty()) {
newUpdates += oldUpdates.split("\n")
}
}
}
notificationManager.notify(Notifications.ID_LIBRARY_RESULT, notification(Notifications.CHANNEL_LIBRARY) {
setSmallIcon(R.drawable.ic_book_white_24dp)
setLargeIcon(notificationBitmap)
setContentTitle(getString(R.string.notification_new_chapters))
if (newUpdates.size > 1) {
setContentText(getString(R.string.notification_new_chapters_text, newUpdates.size))
setStyle(NotificationCompat.BigTextStyle().bigText(newUpdates.joinToString("\n")))
setNumber(newUpdates.size)
} else {
setContentText(newUpdates.first())
}
priority = NotificationCompat.PRIORITY_HIGH
setContentIntent(getNotificationIntent())
setAutoCancel(true)
})
}
/** /**
* Cancels the progress notification. * Cancels the progress notification.
*/ */
private fun cancelProgressNotification() { private fun cancelProgressNotification() {
notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS) 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)
}
} }

View File

@ -26,9 +26,9 @@ class EHentaiUpdateHelper(context: Context) {
/** /**
* @param chapters Cannot be an empty list! * @param chapters Cannot be an empty list!
* *
* @return Pair<Accepted, Discarded> * @return Triple<Accepted, Discarded, HasNew>
*/ */
fun findAcceptedRootAndDiscardOthers(sourceId: Long, chapters: List<Chapter>): Single<Pair<ChapterChain, List<ChapterChain>>> { fun findAcceptedRootAndDiscardOthers(sourceId: Long, chapters: List<Chapter>): Single<Triple<ChapterChain, List<ChapterChain>, Boolean>> {
// Find other chains // Find other chains
val chainsObservable = Observable.merge(chapters.map { chapter -> val chainsObservable = Observable.merge(chapters.map { chapter ->
db.getChapters(chapter.url).asRxSingle().toObservable() db.getChapters(chapter.url).asRxSingle().toObservable()
@ -62,6 +62,8 @@ class EHentaiUpdateHelper(context: Context) {
val chainsAsChapters = chains.flatMap { it.chapters } val chainsAsChapters = chains.flatMap { it.chapters }
if(toDiscard.isNotEmpty()) { if(toDiscard.isNotEmpty()) {
var new = false
// Copy chain chapters to curChapters // Copy chain chapters to curChapters
val newChapters = toDiscard val newChapters = toDiscard
.flatMap { chain -> .flatMap { chain ->
@ -98,6 +100,7 @@ class EHentaiUpdateHelper(context: Context) {
existing.bookmark = existing.bookmark || chapter.bookmark existing.bookmark = existing.bookmark || chapter.bookmark
curChapters curChapters
} else if (chapter.date_upload > 0) { // Ignore chapters using the old system } else if (chapter.date_upload > 0) { // Ignore chapters using the old system
new = true
curChapters + ChapterImpl().apply { curChapters + ChapterImpl().apply {
manga_id = accepted.manga.id manga_id = accepted.manga.id
url = chapter.url url = chapter.url
@ -145,8 +148,8 @@ class EHentaiUpdateHelper(context: Context) {
db.setMangaCategories(newCategories, rootsToMutate.map { it.manga }) db.setMangaCategories(newCategories, rootsToMutate.map { it.manga })
} }
newAccepted to toDiscard Triple(newAccepted, toDiscard, new)
} else accepted to emptyList() } else Triple(accepted, emptyList(), false)
}.toSingle() }.toSingle()
} }
} }

View File

@ -15,6 +15,7 @@ import com.kizitonwose.time.hours
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
@ -49,6 +50,8 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
private val updateHelper: EHentaiUpdateHelper by injectLazy() private val updateHelper: EHentaiUpdateHelper by injectLazy()
private val logger = XLog.tag("EHUpdater") private val logger = XLog.tag("EHUpdater")
private val updateNotifier by lazy { LibraryUpdateNotifier(this) }
/** /**
* This method is called if the system has determined that you must stop execution of your job * This method is called if the system has determined that you must stop execution of your job
* even before you've had a chance to call [.jobFinished]. * even before you've had a chance to call [.jobFinished].
@ -152,6 +155,7 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
var failuresThisIteration = 0 var failuresThisIteration = 0
var updatedThisIteration = 0 var updatedThisIteration = 0
val updatedManga = mutableListOf<Manga>()
val modifiedThisIteration = mutableSetOf<Long>() val modifiedThisIteration = mutableSetOf<Long>()
try { try {
@ -205,9 +209,13 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
} }
// Find accepted root and discard others // Find accepted root and discard others
val (acceptedRoot, discardedRoots) = val (acceptedRoot, discardedRoots, hasNew) =
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters).await() updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters).await()
if(hasNew && updatedManga.none { it.id == acceptedRoot.manga.id }) {
updatedManga += acceptedRoot.manga
}
modifiedThisIteration += acceptedRoot.manga.id!! modifiedThisIteration += acceptedRoot.manga.id!!
modifiedThisIteration += discardedRoots.map { it.manga.id!! } modifiedThisIteration += discardedRoots.map { it.manga.id!! }
updatedThisIteration++ updatedThisIteration++
@ -222,6 +230,8 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
) )
) )
) )
updateNotifier.showResultNotification(updatedManga)
} }
} }