|
|
@ -31,21 +31,20 @@ 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.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.SChapter
|
|
|
|
|
|
|
|
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.ui.main.MainActivity
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.lang.chop
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.system.notification
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
|
|
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
|
|
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
|
|
|
import kotlinx.coroutines.CoroutineStart
|
|
|
|
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.GlobalScope
|
|
|
|
import kotlinx.coroutines.GlobalScope
|
|
|
|
import kotlinx.coroutines.Job
|
|
|
|
import kotlinx.coroutines.Job
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
import eu.kanade.tachiyomi.util.lang.chop
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.system.notification
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
|
|
|
|
|
|
|
import rx.Observable
|
|
|
|
import rx.Observable
|
|
|
|
import rx.Subscription
|
|
|
|
import rx.Subscription
|
|
|
|
import rx.schedulers.Schedulers
|
|
|
|
import rx.schedulers.Schedulers
|
|
|
@ -66,11 +65,11 @@ import java.util.concurrent.atomic.AtomicInteger
|
|
|
|
* destroyed.
|
|
|
|
* destroyed.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
class LibraryUpdateService(
|
|
|
|
class LibraryUpdateService(
|
|
|
|
val db: DatabaseHelper = Injekt.get(),
|
|
|
|
val db: DatabaseHelper = Injekt.get(),
|
|
|
|
val sourceManager: SourceManager = Injekt.get(),
|
|
|
|
val sourceManager: SourceManager = Injekt.get(),
|
|
|
|
val preferences: PreferencesHelper = Injekt.get(),
|
|
|
|
val preferences: PreferencesHelper = Injekt.get(),
|
|
|
|
val downloadManager: DownloadManager = Injekt.get(),
|
|
|
|
val downloadManager: DownloadManager = Injekt.get(),
|
|
|
|
val trackManager: TrackManager = Injekt.get()
|
|
|
|
val trackManager: TrackManager = Injekt.get()
|
|
|
|
) : Service() {
|
|
|
|
) : Service() {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -83,7 +82,6 @@ class LibraryUpdateService(
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private var subscription: Subscription? = null
|
|
|
|
private var subscription: Subscription? = null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Pending intent of action that cancels the library update
|
|
|
|
* Pending intent of action that cancels the library update
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -98,19 +96,31 @@ class LibraryUpdateService(
|
|
|
|
BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
|
|
|
|
BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private var job:Job? = null
|
|
|
|
private var job: Job? = null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val mangaToUpdate = mutableListOf<LibraryManga>()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val categoryIds = mutableSetOf<Int>()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// List containing new updates
|
|
|
|
|
|
|
|
private val newUpdates = mutableMapOf<LibraryManga, Array<Chapter>>()
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Cached progress notification to avoid creating a lot.
|
|
|
|
* Cached progress notification to avoid creating a lot.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
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(notificationBitmap)
|
|
|
|
.setOngoing(true)
|
|
|
|
.setOngoing(true)
|
|
|
|
.setOnlyAlertOnce(true)
|
|
|
|
.setOnlyAlertOnce(true)
|
|
|
|
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
|
|
|
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
|
|
|
.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
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -118,8 +128,8 @@ class LibraryUpdateService(
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
enum class Target {
|
|
|
|
enum class Target {
|
|
|
|
CHAPTERS, // Manga chapters
|
|
|
|
CHAPTERS, // Manga chapters
|
|
|
|
DETAILS, // Manga metadata
|
|
|
|
DETAILS, // Manga metadata
|
|
|
|
TRACKING // Tracking metadata
|
|
|
|
TRACKING // Tracking metadata
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
companion object {
|
|
|
@ -129,11 +139,8 @@ class LibraryUpdateService(
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
const val KEY_CATEGORY = "category"
|
|
|
|
const val KEY_CATEGORY = "category"
|
|
|
|
|
|
|
|
|
|
|
|
private val mangaToUpdate = mutableListOf<LibraryManga>()
|
|
|
|
fun categoryInQueue(id: Int?) = instance?.categoryIds?.contains(id) ?: false
|
|
|
|
|
|
|
|
private var instance: LibraryUpdateService? = null
|
|
|
|
private val categoryIds = mutableSetOf<Int>()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun categoryInQueue(id: Int?) = categoryIds.contains(id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Key that defines what should be updated.
|
|
|
|
* Key that defines what should be updated.
|
|
|
@ -143,11 +150,10 @@ class LibraryUpdateService(
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Returns the status of the service.
|
|
|
|
* Returns the status of the service.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param context the application context.
|
|
|
|
|
|
|
|
* @return true if the service is running, false otherwise.
|
|
|
|
* @return true if the service is running, false otherwise.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
fun isRunning(context: Context): Boolean {
|
|
|
|
fun isRunning(): Boolean {
|
|
|
|
return context.isServiceRunning(LibraryUpdateService::class.java)
|
|
|
|
return instance != null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -159,12 +165,11 @@ class LibraryUpdateService(
|
|
|
|
* @param target defines what should be updated.
|
|
|
|
* @param target defines what should be updated.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
fun start(context: Context, category: Category? = null, target: Target = Target.CHAPTERS) {
|
|
|
|
fun start(context: Context, category: Category? = null, target: Target = Target.CHAPTERS) {
|
|
|
|
if (!isRunning(context)) {
|
|
|
|
if (!isRunning()) {
|
|
|
|
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
|
|
|
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
|
|
|
putExtra(KEY_TARGET, target)
|
|
|
|
putExtra(KEY_TARGET, target)
|
|
|
|
category?.id?.let { id ->
|
|
|
|
category?.id?.let { id ->
|
|
|
|
putExtra(KEY_CATEGORY, id)
|
|
|
|
putExtra(KEY_CATEGORY, id)
|
|
|
|
categoryIds.add(id)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
|
@ -172,67 +177,83 @@ class LibraryUpdateService(
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
context.startForegroundService(intent)
|
|
|
|
context.startForegroundService(intent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
if (target == Target.CHAPTERS) category?.id?.let {
|
|
|
|
if (target == Target.CHAPTERS) category?.id?.let {
|
|
|
|
categoryIds.add(it)
|
|
|
|
instance?.addCategory(it)
|
|
|
|
val preferences: PreferencesHelper = Injekt.get()
|
|
|
|
|
|
|
|
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
|
|
|
|
|
|
|
addManga(getMangaToUpdate(it, target).sortedWith(
|
|
|
|
|
|
|
|
rankingScheme[selectedScheme]
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun addManga(mangaToAdd: List<LibraryManga>) {
|
|
|
|
|
|
|
|
for (manga in mangaToAdd) {
|
|
|
|
|
|
|
|
if (mangaToUpdate.none { it.id == manga.id }) mangaToUpdate.add(manga)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Stops the service.
|
|
|
|
* Stops the service.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param context the application context.
|
|
|
|
* @param context the application context.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
fun stop(context: Context) {
|
|
|
|
fun stop(context: Context) {
|
|
|
|
|
|
|
|
instance?.job?.cancel()
|
|
|
|
context.stopService(Intent(context, LibraryUpdateService::class.java))
|
|
|
|
context.stopService(Intent(context, LibraryUpdateService::class.java))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
private var listener: LibraryServiceListener? = null
|
|
|
|
* Returns the list of manga to be updated.
|
|
|
|
|
|
|
|
*
|
|
|
|
fun setListener(listener: LibraryServiceListener) {
|
|
|
|
* @param intent the update intent.
|
|
|
|
this.listener = listener
|
|
|
|
* @param target the target to update.
|
|
|
|
}
|
|
|
|
* @return a list of manga to update
|
|
|
|
|
|
|
|
*/
|
|
|
|
fun removeListener(listener: LibraryServiceListener) {
|
|
|
|
private fun getMangaToUpdate(categoryId: Int, target: Target): List<LibraryManga> {
|
|
|
|
if (this.listener == listener)
|
|
|
|
val preferences: PreferencesHelper = Injekt.get()
|
|
|
|
this.listener = null
|
|
|
|
val db: DatabaseHelper = Injekt.get()
|
|
|
|
}
|
|
|
|
var listToUpdate = if (categoryId != -1)
|
|
|
|
}
|
|
|
|
db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId }
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
val categoriesToUpdate = preferences.libraryUpdateCategories().getOrDefault().map(String::toInt)
|
|
|
|
|
|
|
|
categoryIds.addAll(categoriesToUpdate)
|
|
|
|
|
|
|
|
if (categoriesToUpdate.isNotEmpty())
|
|
|
|
|
|
|
|
db.getLibraryMangas().executeAsBlocking()
|
|
|
|
|
|
|
|
.filter { it.category in categoriesToUpdate }
|
|
|
|
|
|
|
|
.distinctBy { it.id }
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
db.getLibraryMangas().executeAsBlocking().distinctBy { it.id }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
|
|
|
|
|
|
|
|
listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return listToUpdate
|
|
|
|
private fun addManga(mangaToAdd: List<LibraryManga>) {
|
|
|
|
|
|
|
|
for (manga in mangaToAdd) {
|
|
|
|
|
|
|
|
if (mangaToUpdate.none { it.id == manga.id }) mangaToUpdate.add(manga)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun addCategory(categoryId: Int) {
|
|
|
|
|
|
|
|
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
|
|
|
|
|
|
|
val mangas =
|
|
|
|
|
|
|
|
getMangaToUpdate(categoryId, Target.CHAPTERS).sortedWith(
|
|
|
|
|
|
|
|
rankingScheme[selectedScheme]
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
categoryIds.add(categoryId)
|
|
|
|
|
|
|
|
addManga(mangas)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun getMangaToUpdate(intent: Intent, target: Target): List<LibraryManga> {
|
|
|
|
/**
|
|
|
|
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
|
|
|
|
* Returns the list of manga to be updated.
|
|
|
|
return getMangaToUpdate(categoryId, target)
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param intent the update intent.
|
|
|
|
|
|
|
|
* @param target the target to update.
|
|
|
|
|
|
|
|
* @return a list of manga to update
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private fun getMangaToUpdate(categoryId: Int, target: Target): List<LibraryManga> {
|
|
|
|
|
|
|
|
var listToUpdate = if (categoryId != -1) {
|
|
|
|
|
|
|
|
categoryIds.add(categoryId)
|
|
|
|
|
|
|
|
db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId }
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
val categoriesToUpdate =
|
|
|
|
|
|
|
|
preferences.libraryUpdateCategories().getOrDefault().map(String::toInt)
|
|
|
|
|
|
|
|
categoryIds.addAll(categoriesToUpdate)
|
|
|
|
|
|
|
|
if (categoriesToUpdate.isNotEmpty())
|
|
|
|
|
|
|
|
db.getLibraryMangas().executeAsBlocking()
|
|
|
|
|
|
|
|
.filter { it.category in categoriesToUpdate }
|
|
|
|
|
|
|
|
.distinctBy { it.id }
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
db.getLibraryMangas().executeAsBlocking().distinctBy { it.id }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
|
|
|
|
|
|
|
|
listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return listToUpdate
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun getMangaToUpdate(intent: Intent, target: Target): List<LibraryManga> {
|
|
|
|
|
|
|
|
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
|
|
|
|
|
|
|
|
return getMangaToUpdate(categoryId, target)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -243,26 +264,24 @@ class LibraryUpdateService(
|
|
|
|
super.onCreate()
|
|
|
|
super.onCreate()
|
|
|
|
startForeground(Notifications.ID_LIBRARY_PROGRESS, progressNotification.build())
|
|
|
|
startForeground(Notifications.ID_LIBRARY_PROGRESS, progressNotification.build())
|
|
|
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
|
|
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
|
|
|
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock")
|
|
|
|
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock"
|
|
|
|
|
|
|
|
)
|
|
|
|
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
|
|
|
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun stopService(name: Intent?): Boolean {
|
|
|
|
|
|
|
|
job?.cancel()
|
|
|
|
|
|
|
|
return super.stopService(name)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Method called when the service is destroyed. It destroys subscriptions and releases the wake
|
|
|
|
* Method called when the service is destroyed. It destroys subscriptions and releases the wake
|
|
|
|
* lock.
|
|
|
|
* lock.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
override fun onDestroy() {
|
|
|
|
override fun onDestroy() {
|
|
|
|
|
|
|
|
job?.cancel()
|
|
|
|
|
|
|
|
if (instance == this)
|
|
|
|
|
|
|
|
instance = null
|
|
|
|
subscription?.unsubscribe()
|
|
|
|
subscription?.unsubscribe()
|
|
|
|
mangaToUpdate.clear()
|
|
|
|
|
|
|
|
categoryIds.clear()
|
|
|
|
|
|
|
|
if (wakeLock.isHeld) {
|
|
|
|
if (wakeLock.isHeld) {
|
|
|
|
wakeLock.release()
|
|
|
|
wakeLock.release()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
listener?.onUpdateManga(LibraryManga())
|
|
|
|
super.onDestroy()
|
|
|
|
super.onDestroy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -283,93 +302,78 @@ class LibraryUpdateService(
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
|
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
|
|
if (intent == null) return START_NOT_STICKY
|
|
|
|
if (intent == null) return START_NOT_STICKY
|
|
|
|
val target = intent.getSerializableExtra(KEY_TARGET) as? Target ?: return START_NOT_STICKY
|
|
|
|
val target = intent.getSerializableExtra(KEY_TARGET) as? Target
|
|
|
|
|
|
|
|
?: return START_NOT_STICKY
|
|
|
|
|
|
|
|
|
|
|
|
// Unsubscribe from any previous subscription if needed.
|
|
|
|
// Unsubscribe from any previous subscription if needed.
|
|
|
|
subscription?.unsubscribe()
|
|
|
|
subscription?.unsubscribe()
|
|
|
|
|
|
|
|
instance = this
|
|
|
|
|
|
|
|
|
|
|
|
val handler = CoroutineExceptionHandler { _, exception ->
|
|
|
|
|
|
|
|
Timber.e(exception)
|
|
|
|
|
|
|
|
stopSelf(startId)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
|
|
|
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
|
|
|
if (target == Target.CHAPTERS) {
|
|
|
|
val mangaList =
|
|
|
|
updateChapters(
|
|
|
|
getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme])
|
|
|
|
getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme]), startId
|
|
|
|
// Update favorite manga. Destroy service when completed or in case of an error.
|
|
|
|
)
|
|
|
|
if (target == Target.DETAILS) {
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Update either chapter list or manga details.
|
|
|
|
// Update either chapter list or manga details.
|
|
|
|
// Update favorite manga. Destroy service when completed or in case of an error.
|
|
|
|
|
|
|
|
val mangaList =
|
|
|
|
|
|
|
|
getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme])
|
|
|
|
|
|
|
|
subscription = Observable.defer {
|
|
|
|
subscription = Observable.defer {
|
|
|
|
when (target) {
|
|
|
|
updateDetails(mangaList)
|
|
|
|
Target.DETAILS -> updateDetails(mangaList)
|
|
|
|
|
|
|
|
else -> updateTrackings(mangaList)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}.subscribeOn(Schedulers.io()).subscribe({}, {
|
|
|
|
}.subscribeOn(Schedulers.io()).subscribe({}, {
|
|
|
|
Timber.e(it)
|
|
|
|
Timber.e(it)
|
|
|
|
stopSelf(startId)
|
|
|
|
stopSelf(startId)
|
|
|
|
}, {
|
|
|
|
}, {
|
|
|
|
stopSelf(startId)
|
|
|
|
stopSelf(startId)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
launchTarget(target, mangaList, startId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return START_REDELIVER_INTENT
|
|
|
|
return START_REDELIVER_INTENT
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun updateChapters(mangaToAdd: List<LibraryManga>, startId: Int) {
|
|
|
|
private fun launchTarget(target: Target, mangaToAdd: List<LibraryManga>, startId: Int) {
|
|
|
|
addManga(mangaToAdd)
|
|
|
|
val handler = CoroutineExceptionHandler { _, exception ->
|
|
|
|
|
|
|
|
Timber.e(exception)
|
|
|
|
if (job == null) {
|
|
|
|
stopSelf(startId)
|
|
|
|
job = GlobalScope.launch(Dispatchers.IO, CoroutineStart.DEFAULT) {
|
|
|
|
}
|
|
|
|
updateChaptersJob()
|
|
|
|
if (target == Target.CHAPTERS) {
|
|
|
|
mangaToUpdate.clear()
|
|
|
|
job = GlobalScope.launch(handler) {
|
|
|
|
categoryIds.clear()
|
|
|
|
updateChaptersJob(mangaToAdd)
|
|
|
|
stopSelf(startId)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
job = GlobalScope.launch(handler) {
|
|
|
|
|
|
|
|
updateTrackings(mangaToAdd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
job?.invokeOnCompletion { stopSelf(startId) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun updateChaptersJob() {
|
|
|
|
private suspend fun updateChaptersJob(mangaToAdd: List<LibraryManga>) {
|
|
|
|
// Initialize the variables holding the progress of the updates.
|
|
|
|
|
|
|
|
var count = 0
|
|
|
|
|
|
|
|
// List containing new updates
|
|
|
|
|
|
|
|
val newUpdates = ArrayList<Pair<LibraryManga, Array<Chapter>>>()
|
|
|
|
|
|
|
|
// list containing failed updates
|
|
|
|
|
|
|
|
val failedUpdates = ArrayList<Manga>()
|
|
|
|
|
|
|
|
// List containing categories that get included in downloads.
|
|
|
|
// List containing categories that get included in downloads.
|
|
|
|
val categoriesToDownload = preferences.downloadNewCategories().getOrDefault().map(String::toInt)
|
|
|
|
val categoriesToDownload =
|
|
|
|
|
|
|
|
preferences.downloadNewCategories().getOrDefault().map(String::toInt)
|
|
|
|
// Boolean to determine if user wants to automatically download new chapters.
|
|
|
|
// Boolean to determine if user wants to automatically download new chapters.
|
|
|
|
val downloadNew = preferences.downloadNew().getOrDefault()
|
|
|
|
val downloadNew = preferences.downloadNew().getOrDefault()
|
|
|
|
// Boolean to determine if DownloadManager has downloads
|
|
|
|
// Boolean to determine if DownloadManager has downloads
|
|
|
|
var hasDownloads = false
|
|
|
|
var hasDownloads = false
|
|
|
|
|
|
|
|
// Initialize the variables holding the progress of the updates.
|
|
|
|
|
|
|
|
var count = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mangaToUpdate.addAll(mangaToAdd)
|
|
|
|
while (count < mangaToUpdate.size) {
|
|
|
|
while (count < mangaToUpdate.size) {
|
|
|
|
if (job?.isCancelled == true || job == null) break
|
|
|
|
val shouldDownload = (downloadNew && (categoriesToDownload.isEmpty() ||
|
|
|
|
val manga = mangaToUpdate[count]
|
|
|
|
mangaToUpdate[count].category in categoriesToDownload ||
|
|
|
|
showProgressNotification(manga, count++, mangaToUpdate.size)
|
|
|
|
db.getCategoriesForManga(mangaToUpdate[count]).executeOnIO()
|
|
|
|
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
|
|
|
|
.any { (it.id ?: -1) in categoriesToDownload }))
|
|
|
|
val fetchedChapters = try { source.fetchChapterList(manga).toBlocking().single() }
|
|
|
|
if (updateMangaChapters(mangaToUpdate[count], count, shouldDownload)) {
|
|
|
|
catch(e: java.lang.Exception) {
|
|
|
|
hasDownloads = true
|
|
|
|
failedUpdates.add(manga)
|
|
|
|
|
|
|
|
emptyList<SChapter>() }
|
|
|
|
|
|
|
|
if (fetchedChapters.isNotEmpty()) {
|
|
|
|
|
|
|
|
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source).first
|
|
|
|
|
|
|
|
if (newChapters.isNotEmpty()) {
|
|
|
|
|
|
|
|
if (downloadNew && (categoriesToDownload.isEmpty() || manga.category in categoriesToDownload)) {
|
|
|
|
|
|
|
|
downloadChapters(manga, newChapters.sortedBy { it.chapter_number })
|
|
|
|
|
|
|
|
hasDownloads = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
newUpdates.add(manga to newChapters.sortedBy { it.chapter_number }.toTypedArray())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (newUpdates.isNotEmpty()) {
|
|
|
|
if (newUpdates.isNotEmpty()) {
|
|
|
|
showResultNotification(newUpdates)
|
|
|
|
showResultNotification(newUpdates)
|
|
|
|
|
|
|
|
|
|
|
|
if (preferences.refreshCoversToo().getOrDefault()) {
|
|
|
|
if (preferences.refreshCoversToo().getOrDefault() && job?.isCancelled == false) {
|
|
|
|
updateDetails(newUpdates.map { it.first }).observeOn(Schedulers.io())
|
|
|
|
updateDetails(newUpdates.map { it.key }).observeOn(Schedulers.io())
|
|
|
|
.doOnCompleted {
|
|
|
|
.doOnCompleted {
|
|
|
|
cancelProgressNotification()
|
|
|
|
cancelProgressNotification()
|
|
|
|
if (downloadNew && hasDownloads) {
|
|
|
|
if (downloadNew && hasDownloads) {
|
|
|
@ -377,19 +381,51 @@ class LibraryUpdateService(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.subscribeOn(Schedulers.io()).subscribe {}
|
|
|
|
.subscribeOn(Schedulers.io()).subscribe {}
|
|
|
|
}
|
|
|
|
} else if (downloadNew && hasDownloads) {
|
|
|
|
else if (downloadNew && hasDownloads) {
|
|
|
|
|
|
|
|
DownloadService.start(this)
|
|
|
|
DownloadService.start(this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (failedUpdates.isNotEmpty()) {
|
|
|
|
|
|
|
|
Timber.e("Failed updating: ${failedUpdates.map { it.title }}")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cancelProgressNotification()
|
|
|
|
cancelProgressNotification()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun updateMangaChapters(
|
|
|
|
|
|
|
|
manga: LibraryManga,
|
|
|
|
|
|
|
|
progess: Int,
|
|
|
|
|
|
|
|
shouldDownload: Boolean
|
|
|
|
|
|
|
|
):
|
|
|
|
|
|
|
|
Boolean {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
var hasDownloads = false
|
|
|
|
|
|
|
|
if (job?.isCancelled == true) {
|
|
|
|
|
|
|
|
throw java.lang.Exception("Job was cancelled")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
showProgressNotification(manga, progess, mangaToUpdate.size)
|
|
|
|
|
|
|
|
val source = sourceManager.get(manga.source) as? HttpSource ?: return false
|
|
|
|
|
|
|
|
val fetchedChapters = withContext(Dispatchers.IO) {
|
|
|
|
|
|
|
|
source.fetchChapterList(manga).toBlocking().single()
|
|
|
|
|
|
|
|
} ?: emptyList()
|
|
|
|
|
|
|
|
if (fetchedChapters.isNotEmpty()) {
|
|
|
|
|
|
|
|
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source)
|
|
|
|
|
|
|
|
if (newChapters.first.isNotEmpty()) {
|
|
|
|
|
|
|
|
if (shouldDownload) {
|
|
|
|
|
|
|
|
downloadChapters(manga, newChapters.first.sortedBy { it.chapter_number })
|
|
|
|
|
|
|
|
hasDownloads = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
newUpdates[manga] =
|
|
|
|
|
|
|
|
newChapters.first.sortedBy { it.chapter_number }.toTypedArray()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newChapters.first.size + newChapters.second.size > 0) listener?.onUpdateManga(
|
|
|
|
|
|
|
|
manga
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasDownloads
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
|
|
|
Timber.e("Failed updating: ${manga.title}: $e")
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
|
|
|
fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
|
|
|
// we need to get the chapters from the db so we have chapter ids
|
|
|
|
// we need to get the chapters from the db so we have chapter ids
|
|
|
|
val mangaChapters = db.getChapters(manga).executeAsBlocking()
|
|
|
|
val mangaChapters = db.getChapters(manga).executeAsBlocking()
|
|
|
@ -410,7 +446,7 @@ class LibraryUpdateService(
|
|
|
|
fun updateManga(manga: Manga): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
|
|
|
fun updateManga(manga: Manga): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
|
|
|
val source = sourceManager.get(manga.source) as? HttpSource ?: return Observable.empty()
|
|
|
|
val source = sourceManager.get(manga.source) as? HttpSource ?: return Observable.empty()
|
|
|
|
return source.fetchChapterList(manga)
|
|
|
|
return source.fetchChapterList(manga)
|
|
|
|
.map { syncChaptersWithSource(db, it, manga, source) }
|
|
|
|
.map { syncChaptersWithSource(db, it, manga, source) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -426,61 +462,57 @@ 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.andIncrement, mangaToUpdate.size) }
|
|
|
|
.doOnNext { showProgressNotification(it, count.andIncrement, mangaToUpdate.size) }
|
|
|
|
// Update the details of the manga.
|
|
|
|
// Update the details of the manga.
|
|
|
|
.concatMap { manga ->
|
|
|
|
.concatMap { manga ->
|
|
|
|
val source = sourceManager.get(manga.source) as? HttpSource
|
|
|
|
val source = sourceManager.get(manga.source) as? HttpSource
|
|
|
|
?: return@concatMap Observable.empty<LibraryManga>()
|
|
|
|
?: return@concatMap Observable.empty<LibraryManga>()
|
|
|
|
|
|
|
|
source.fetchMangaDetails(manga)
|
|
|
|
source.fetchMangaDetails(manga)
|
|
|
|
.map { networkManga ->
|
|
|
|
.map { networkManga ->
|
|
|
|
val thumbnailUrl = manga.thumbnail_url
|
|
|
|
manga.copyFrom(networkManga)
|
|
|
|
manga.copyFrom(networkManga)
|
|
|
|
db.insertManga(manga).executeAsBlocking()
|
|
|
|
db.insertManga(manga).executeAsBlocking()
|
|
|
|
MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
|
|
|
|
if (thumbnailUrl != networkManga.thumbnail_url)
|
|
|
|
manga
|
|
|
|
MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
|
|
|
|
}
|
|
|
|
manga
|
|
|
|
.onErrorReturn { manga }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.onErrorReturn { manga }
|
|
|
|
.doOnCompleted {
|
|
|
|
}
|
|
|
|
cancelProgressNotification()
|
|
|
|
.doOnCompleted {
|
|
|
|
}
|
|
|
|
cancelProgressNotification()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Method that updates the metadata of the connected tracking services. It's called in a
|
|
|
|
* Method that updates the metadata of the connected tracking services. It's called in a
|
|
|
|
* background thread, so it's safe to do heavy operations or network calls here.
|
|
|
|
* background thread, so it's safe to do heavy operations or network calls here.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private fun updateTrackings(mangaToUpdate: List<LibraryManga>): Observable<LibraryManga> {
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun updateTrackings(mangaToUpdate: List<LibraryManga>) {
|
|
|
|
// Initialize the variables holding the progress of the updates.
|
|
|
|
// Initialize the variables holding the progress of the updates.
|
|
|
|
var count = 0
|
|
|
|
var count = 0
|
|
|
|
|
|
|
|
|
|
|
|
val loggedServices = trackManager.services.filter { it.isLogged }
|
|
|
|
val loggedServices = trackManager.services.filter { it.isLogged }
|
|
|
|
|
|
|
|
|
|
|
|
// Emit each manga and update it sequentially.
|
|
|
|
mangaToUpdate.forEach { manga ->
|
|
|
|
return Observable.from(mangaToUpdate)
|
|
|
|
showProgressNotification(manga, count++, mangaToUpdate.size)
|
|
|
|
// Notify manga that will update.
|
|
|
|
|
|
|
|
.doOnNext { showProgressNotification(it, count++, mangaToUpdate.size) }
|
|
|
|
val tracks = db.getTracks(manga).executeAsBlocking()
|
|
|
|
// Update the tracking details.
|
|
|
|
|
|
|
|
.concatMap { manga ->
|
|
|
|
tracks.forEach { track ->
|
|
|
|
val tracks = db.getTracks(manga).executeAsBlocking()
|
|
|
|
val service = trackManager.getService(track.sync_id)
|
|
|
|
|
|
|
|
if (service != null && service in loggedServices) {
|
|
|
|
Observable.from(tracks)
|
|
|
|
try {
|
|
|
|
.concatMap { track ->
|
|
|
|
service.refresh(track)
|
|
|
|
val service = trackManager.getService(track.sync_id)
|
|
|
|
db.insertTrack(track).executeAsBlocking()
|
|
|
|
if (service != null && service in loggedServices) {
|
|
|
|
} catch (e: Exception) {
|
|
|
|
service.refresh(track)
|
|
|
|
Timber.e(e)
|
|
|
|
.doOnNext { db.insertTrack(it).executeAsBlocking() }
|
|
|
|
}
|
|
|
|
.onErrorReturn { track }
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Observable.empty()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.map { manga }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.doOnCompleted {
|
|
|
|
|
|
|
|
cancelProgressNotification()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelProgressNotification()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -491,10 +523,12 @@ class LibraryUpdateService(
|
|
|
|
* @param total the total progress.
|
|
|
|
* @param total the total progress.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
|
|
|
|
private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
|
|
|
|
notificationManager.notify(Notifications.ID_LIBRARY_PROGRESS, progressNotification
|
|
|
|
notificationManager.notify(
|
|
|
|
.setContentTitle(manga.currentTitle())
|
|
|
|
Notifications.ID_LIBRARY_PROGRESS, progressNotification
|
|
|
|
|
|
|
|
.setContentTitle(manga.title)
|
|
|
|
.setProgress(total, current, false)
|
|
|
|
.setProgress(total, current, false)
|
|
|
|
.build())
|
|
|
|
.build()
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -502,11 +536,11 @@ class LibraryUpdateService(
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param updates a list of manga with new updates.
|
|
|
|
* @param updates a list of manga with new updates.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private fun showResultNotification(updates: List<Pair<Manga, Array<Chapter>>>) {
|
|
|
|
private fun showResultNotification(updates: Map<LibraryManga, Array<Chapter>>) {
|
|
|
|
val notifications = ArrayList<Pair<Notification, Int>>()
|
|
|
|
val notifications = ArrayList<Pair<Notification, Int>>()
|
|
|
|
updates.forEach {
|
|
|
|
updates.forEach {
|
|
|
|
val manga = it.first
|
|
|
|
val manga = it.key
|
|
|
|
val chapters = it.second
|
|
|
|
val chapters = it.value
|
|
|
|
val chapterNames = chapters.map { chapter -> chapter.name }
|
|
|
|
val chapterNames = chapters.map { chapter -> chapter.name }
|
|
|
|
notifications.add(Pair(notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
|
|
|
notifications.add(Pair(notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
|
|
|
setSmallIcon(R.drawable.ic_tachi)
|
|
|
|
setSmallIcon(R.drawable.ic_tachi)
|
|
|
@ -515,15 +549,17 @@ class LibraryUpdateService(
|
|
|
|
.asBitmap().load(manga).dontTransform().centerCrop().circleCrop()
|
|
|
|
.asBitmap().load(manga).dontTransform().centerCrop().circleCrop()
|
|
|
|
.override(256, 256).submit().get()
|
|
|
|
.override(256, 256).submit().get()
|
|
|
|
setLargeIcon(icon)
|
|
|
|
setLargeIcon(icon)
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e: Exception) { }
|
|
|
|
|
|
|
|
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
|
|
|
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
|
|
|
setContentTitle(manga.currentTitle())
|
|
|
|
setContentTitle(manga.title)
|
|
|
|
color = ContextCompat.getColor(this@LibraryUpdateService, R.color.colorAccent)
|
|
|
|
color = ContextCompat.getColor(this@LibraryUpdateService, R.color.colorAccent)
|
|
|
|
val chaptersNames = if (chapterNames.size > 5) {
|
|
|
|
val chaptersNames = if (chapterNames.size > 5) {
|
|
|
|
"${chapterNames.take(4).joinToString(", ")}, " +
|
|
|
|
"${chapterNames.take(4).joinToString(", ")}, " +
|
|
|
|
resources.getQuantityString(R.plurals.notification_and_n_more,
|
|
|
|
resources.getQuantityString(
|
|
|
|
(chapterNames.size - 4), (chapterNames.size - 4))
|
|
|
|
R.plurals.notification_and_n_more,
|
|
|
|
|
|
|
|
(chapterNames.size - 4), (chapterNames.size - 4)
|
|
|
|
|
|
|
|
)
|
|
|
|
} else chapterNames.joinToString(", ")
|
|
|
|
} else chapterNames.joinToString(", ")
|
|
|
|
setContentText(chaptersNames)
|
|
|
|
setContentText(chaptersNames)
|
|
|
|
setStyle(NotificationCompat.BigTextStyle().bigText(chaptersNames))
|
|
|
|
setStyle(NotificationCompat.BigTextStyle().bigText(chaptersNames))
|
|
|
@ -534,41 +570,57 @@ class LibraryUpdateService(
|
|
|
|
this@LibraryUpdateService, manga, chapters.first()
|
|
|
|
this@LibraryUpdateService, manga, chapters.first()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
addAction(R.drawable.ic_glasses_black_24dp, getString(R.string.action_mark_as_read),
|
|
|
|
addAction(
|
|
|
|
NotificationReceiver.markAsReadPendingBroadcast(this@LibraryUpdateService,
|
|
|
|
R.drawable.ic_glasses_black_24dp, getString(R.string.mark_as_read),
|
|
|
|
manga, chapters, Notifications.ID_NEW_CHAPTERS))
|
|
|
|
NotificationReceiver.markAsReadPendingBroadcast(
|
|
|
|
addAction(R.drawable.ic_book_white_24dp, getString(R.string.action_view_chapters),
|
|
|
|
this@LibraryUpdateService,
|
|
|
|
NotificationReceiver.openChapterPendingActivity(this@LibraryUpdateService,
|
|
|
|
manga, chapters, Notifications.ID_NEW_CHAPTERS
|
|
|
|
manga, 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)
|
|
|
|
setAutoCancel(true)
|
|
|
|
}, manga.id.hashCode()))
|
|
|
|
}, manga.id.hashCode()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NotificationManagerCompat.from(this).apply {
|
|
|
|
NotificationManagerCompat.from(this).apply {
|
|
|
|
|
|
|
|
|
|
|
|
notify(Notifications.ID_NEW_CHAPTERS, notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
|
|
|
notify(
|
|
|
|
setSmallIcon(R.drawable.ic_tachi)
|
|
|
|
Notifications.ID_NEW_CHAPTERS,
|
|
|
|
setLargeIcon(notificationBitmap)
|
|
|
|
notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
|
|
|
setContentTitle(getString(R.string.notification_new_chapters))
|
|
|
|
setSmallIcon(R.drawable.ic_tachi)
|
|
|
|
color = ContextCompat.getColor(applicationContext, R.color.colorAccent)
|
|
|
|
setLargeIcon(notificationBitmap)
|
|
|
|
if (updates.size > 1) {
|
|
|
|
setContentTitle(getString(R.string.new_chapters_found))
|
|
|
|
setContentText(resources.getQuantityString(R.plurals
|
|
|
|
color = ContextCompat.getColor(applicationContext, R.color.colorAccent)
|
|
|
|
.notification_new_chapters_text,
|
|
|
|
if (updates.size > 1) {
|
|
|
|
updates.size, updates.size))
|
|
|
|
setContentText(
|
|
|
|
setStyle(NotificationCompat.BigTextStyle().bigText(updates.joinToString("\n") {
|
|
|
|
resources.getQuantityString(
|
|
|
|
it.first.currentTitle().chop(45)
|
|
|
|
R.plurals
|
|
|
|
}))
|
|
|
|
.for_n_titles,
|
|
|
|
}
|
|
|
|
updates.size, updates.size
|
|
|
|
else {
|
|
|
|
)
|
|
|
|
setContentText(updates.first().first.currentTitle().chop(45))
|
|
|
|
)
|
|
|
|
}
|
|
|
|
setStyle(
|
|
|
|
priority = NotificationCompat.PRIORITY_HIGH
|
|
|
|
NotificationCompat.BigTextStyle()
|
|
|
|
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
|
|
|
.bigText(updates.keys.joinToString("\n") {
|
|
|
|
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
|
|
|
it.title.chop(45)
|
|
|
|
setGroupSummary(true)
|
|
|
|
})
|
|
|
|
setContentIntent(getNotificationIntent())
|
|
|
|
)
|
|
|
|
setAutoCancel(true)
|
|
|
|
} else {
|
|
|
|
})
|
|
|
|
setContentText(updates.keys.first().title.chop(45))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
priority = NotificationCompat.PRIORITY_HIGH
|
|
|
|
|
|
|
|
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
|
|
|
|
|
|
|
setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
|
|
|
|
|
|
|
|
setGroupSummary(true)
|
|
|
|
|
|
|
|
setContentIntent(getNotificationIntent())
|
|
|
|
|
|
|
|
setAutoCancel(true)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
notifications.forEach {
|
|
|
|
notifications.forEach {
|
|
|
|
notify(it.second, it.first)
|
|
|
|
notify(it.second, it.first)
|
|
|
@ -592,5 +644,8 @@ class LibraryUpdateService(
|
|
|
|
intent.action = MainActivity.SHORTCUT_RECENTLY_UPDATED
|
|
|
|
intent.action = MainActivity.SHORTCUT_RECENTLY_UPDATED
|
|
|
|
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface LibraryServiceListener {
|
|
|
|
|
|
|
|
fun onUpdateManga(manga: LibraryManga)
|
|
|
|
}
|
|
|
|
}
|
|
|
|