Reorder Library Update Service

Co-Authored-By: Carlos <carlosesco@users.noreply.github.com>
This commit is contained in:
Jay 2020-05-23 19:19:10 -04:00
parent c67a5a2c0e
commit 8f410ca864

View File

@ -117,97 +117,119 @@ class LibraryUpdateService(
TRACKING // Tracking metadata TRACKING // Tracking metadata
} }
companion object {
/** /**
* Key for category to update. * Method called when the service receives an intent.
*/
const val KEY_CATEGORY = "category"
fun categoryInQueue(id: Int?) = instance?.categoryIds?.contains(id) ?: false
private var instance: LibraryUpdateService? = null
/**
* Key that defines what should be updated.
*/
const val KEY_TARGET = "target"
/**
* Key for list of manga to be updated. (For dynamic categories)
*/
const val KEY_MANGAS = "mangas"
/**
* Returns the status of the service.
* *
* @return true if the service is running, false otherwise. * @param intent the start intent from.
* @param flags the flags of the command.
* @param startId the start id of this command.
* @return the start value of the command.
*/ */
fun isRunning(): Boolean { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return instance != null if (intent == null) return START_NOT_STICKY
val target = intent.getSerializableExtra(KEY_TARGET) as? Target ?: return START_NOT_STICKY
instance = this
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
val savedMangasList = intent.getLongArrayExtra(KEY_MANGAS)?.asList()
val mangaList = (if (savedMangasList != null) {
val mangas = db.getLibraryMangas().executeAsBlocking().filter {
it.id in savedMangasList
}.distinctBy { it.id }
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
if (categoryId > -1) categoryIds.add(categoryId)
mangas
} else {
getMangaToUpdate(intent, target)
}).sortedWith(rankingScheme[selectedScheme])
// Update favorite manga. Destroy service when completed or in case of an error.
launchTarget(target, mangaList, startId)
return START_REDELIVER_INTENT
} }
/** /**
* Starts the service. It will be started only if there isn't another instance already * Method called when the service is created. It injects dagger dependencies and acquire
* running. * the wake lock.
*
* @param context the application context.
* @param category a specific category to update, or null for global update.
* @param target defines what should be updated.
*/ */
fun start( override fun onCreate() {
context: Context, super.onCreate()
category: Category? = null, notifier = LibraryUpdateNotifier(this)
target: Target = Target.CHAPTERS, wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
mangaToUse: List<LibraryManga>? = null PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock"
) {
if (!isRunning()) {
val intent = Intent(context, LibraryUpdateService::class.java).apply {
putExtra(KEY_TARGET, target)
category?.id?.let { id ->
putExtra(KEY_CATEGORY, id)
if (mangaToUse != null) putExtra(
KEY_MANGAS,
mangaToUse.mapNotNull { it.id }.toLongArray()
) )
} wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
} startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent)
} else {
context.startForegroundService(intent)
}
} else {
if (target == Target.CHAPTERS) category?.id?.let {
if (mangaToUse != null) instance?.addMangaToQueue(it, mangaToUse)
else instance?.addCategory(it)
}
}
} }
/** /**
* Stops the service. * Method called when the service is destroyed. It cancels jobs and releases the wake lock.
*
* @param context the application context.
*/ */
fun stop(context: Context) { override fun onDestroy() {
instance?.job?.cancel() job?.cancel()
GlobalScope.launch { if (instance == this)
instance?.jobCount?.set(0) instance = null
instance?.finishUpdates() if (wakeLock.isHeld) {
wakeLock.release()
} }
context.stopService(Intent(context, LibraryUpdateService::class.java)) listener?.onUpdateManga(LibraryManga())
super.onDestroy()
} }
private var listener: LibraryServiceListener? = null private fun getMangaToUpdate(intent: Intent, target: Target): List<LibraryManga> {
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
fun setListener(listener: LibraryServiceListener) { return getMangaToUpdate(categoryId, target)
this.listener = listener
} }
fun removeListener(listener: LibraryServiceListener) { /**
if (this.listener == listener) this.listener = null * Returns the list of manga to be updated.
*
* @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)
if (categoriesToUpdate.isNotEmpty()) {
categoryIds.addAll(categoriesToUpdate)
db.getLibraryMangas().executeAsBlocking()
.filter { it.category in categoriesToUpdate }.distinctBy { it.id }
} else {
categoryIds.addAll(db.getCategories().executeAsBlocking().mapNotNull { it.id } + 0)
db.getLibraryMangas().executeAsBlocking().distinctBy { it.id }
} }
} }
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED }
}
return listToUpdate
}
private fun launchTarget(target: Target, mangaToAdd: List<LibraryManga>, startId: Int) {
val handler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception)
stopSelf(startId)
}
if (target == Target.CHAPTERS) {
listener?.onUpdateManga(LibraryManga())
}
job = GlobalScope.launch(handler) {
when (target) {
Target.CHAPTERS -> updateChaptersJob(mangaToAdd)
Target.DETAILS -> updateDetails(mangaToAdd)
else -> updateTrackings(mangaToAdd)
}
}
job?.invokeOnCompletion { stopSelf(startId) }
}
private fun addManga(mangaToAdd: List<LibraryManga>) { private fun addManga(mangaToAdd: List<LibraryManga>) {
val distinctManga = mangaToAdd.filter { it !in mangaToUpdate } val distinctManga = mangaToAdd.filter { it !in mangaToUpdate }
@ -259,69 +281,6 @@ class LibraryUpdateService(
addManga(mangas) addManga(mangas)
} }
/**
* Returns the list of manga to be updated.
*
* @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)
if (categoriesToUpdate.isNotEmpty()) {
categoryIds.addAll(categoriesToUpdate)
db.getLibraryMangas().executeAsBlocking()
.filter { it.category in categoriesToUpdate }.distinctBy { it.id }
} else {
categoryIds.addAll(db.getCategories().executeAsBlocking().mapNotNull { it.id } + 0)
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)
}
/**
* Method called when the service is created. It injects dagger dependencies and acquire
* the wake lock.
*/
override fun onCreate() {
super.onCreate()
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())
}
/**
* Method called when the service is destroyed. It cancels jobs and releases the wake lock.
*/
override fun onDestroy() {
job?.cancel()
if (instance == this)
instance = null
if (wakeLock.isHeld) {
wakeLock.release()
}
listener?.onUpdateManga(LibraryManga())
super.onDestroy()
}
/** /**
* This method needs to be implemented, but it's not used/needed. * This method needs to be implemented, but it's not used/needed.
*/ */
@ -329,57 +288,6 @@ class LibraryUpdateService(
return null return null
} }
/**
* Method called when the service receives an intent.
*
* @param intent the start intent from.
* @param flags the flags of the command.
* @param startId the start id of this command.
* @return the start value of the command.
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) return START_NOT_STICKY
val target = intent.getSerializableExtra(KEY_TARGET) as? Target ?: return START_NOT_STICKY
instance = this
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
val savedMangasList = intent.getLongArrayExtra(KEY_MANGAS)?.asList()
val mangaList = (if (savedMangasList != null) {
val mangas = db.getLibraryMangas().executeAsBlocking().filter {
it.id in savedMangasList
}.distinctBy { it.id }
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
if (categoryId > -1) categoryIds.add(categoryId)
mangas
} else {
getMangaToUpdate(intent, target)
}).sortedWith(rankingScheme[selectedScheme])
// Update favorite manga. Destroy service when completed or in case of an error.
launchTarget(target, mangaList, startId)
return START_REDELIVER_INTENT
}
private fun launchTarget(target: Target, mangaToAdd: List<LibraryManga>, startId: Int) {
val handler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception)
stopSelf(startId)
}
if (target == Target.CHAPTERS) {
listener?.onUpdateManga(LibraryManga())
}
job = GlobalScope.launch(handler) {
when (target) {
Target.CHAPTERS -> updateChaptersJob(mangaToAdd)
Target.DETAILS -> updateDetails(mangaToAdd)
else -> updateTrackings(mangaToAdd)
}
}
job?.invokeOnCompletion { stopSelf(startId) }
}
private suspend fun updateChaptersJob(mangaToAdd: List<LibraryManga>) { private suspend fun updateChaptersJob(mangaToAdd: List<LibraryManga>) {
// Initialize the variables holding the progress of the updates. // Initialize the variables holding the progress of the updates.
@ -614,6 +522,98 @@ class LibraryUpdateService(
} }
return File("") return File("")
} }
companion object {
/**
* Key for category to update.
*/
const val KEY_CATEGORY = "category"
fun categoryInQueue(id: Int?) = instance?.categoryIds?.contains(id) ?: false
private var instance: LibraryUpdateService? = null
/**
* Key that defines what should be updated.
*/
const val KEY_TARGET = "target"
/**
* Key for list of manga to be updated. (For dynamic categories)
*/
const val KEY_MANGAS = "mangas"
/**
* Returns the status of the service.
*
* @return true if the service is running, false otherwise.
*/
fun isRunning(): Boolean {
return instance != null
}
/**
* Starts the service. It will be started only if there isn't another instance already
* running.
*
* @param context the application context.
* @param category a specific category to update, or null for global update.
* @param target defines what should be updated.
*/
fun start(
context: Context,
category: Category? = null,
target: Target = Target.CHAPTERS,
mangaToUse: List<LibraryManga>? = null
) {
if (!isRunning()) {
val intent = Intent(context, LibraryUpdateService::class.java).apply {
putExtra(KEY_TARGET, target)
category?.id?.let { id ->
putExtra(KEY_CATEGORY, id)
if (mangaToUse != null) putExtra(
KEY_MANGAS,
mangaToUse.mapNotNull { it.id }.toLongArray()
)
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent)
} else {
context.startForegroundService(intent)
}
} else {
if (target == Target.CHAPTERS) category?.id?.let {
if (mangaToUse != null) instance?.addMangaToQueue(it, mangaToUse)
else instance?.addCategory(it)
}
}
}
/**
* Stops the service.
*
* @param context the application context.
*/
fun stop(context: Context) {
instance?.job?.cancel()
GlobalScope.launch {
instance?.jobCount?.set(0)
instance?.finishUpdates()
}
context.stopService(Intent(context, LibraryUpdateService::class.java))
}
private var listener: LibraryServiceListener? = null
fun setListener(listener: LibraryServiceListener) {
this.listener = listener
}
fun removeListener(listener: LibraryServiceListener) {
if (this.listener == listener) this.listener = null
}
}
} }
interface LibraryServiceListener { interface LibraryServiceListener {