Move tracker logic into own class
This commit is contained in:
@@ -67,19 +67,19 @@ android {
|
||||
afterEvaluate {
|
||||
compileDebugKotlin {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += ['-opt-in=kotlin.RequiresOptIn']
|
||||
freeCompilerArgs += ['-opt-in=org.koitharu.kotatsu.parsers.InternalParsersApi']
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
implementation('com.github.nv95:kotatsu-parsers:ab87a50e9b') {
|
||||
implementation('com.github.nv95:kotatsu-parsers:0ed35a4b21') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.2'
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.8.0-rc02'
|
||||
implementation 'androidx.core:core-ktx:1.8.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.5.0-rc01'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.0-rc01'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-rc01'
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.parsers.InternalParsersApi
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* This parser is just for parser development, it should not be used in releases
|
||||
*/
|
||||
@OptIn(InternalParsersApi::class)
|
||||
class DummyParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.DUMMY) {
|
||||
|
||||
override val configKeyDomain: ConfigKey.Domain
|
||||
|
||||
@@ -21,7 +21,7 @@ interface TrackLogsDao {
|
||||
suspend fun removeAll(mangaId: Long)
|
||||
|
||||
@Query("DELETE FROM track_logs WHERE manga_id NOT IN (SELECT manga_id FROM tracks)")
|
||||
suspend fun cleanup()
|
||||
suspend fun gc()
|
||||
|
||||
@Query("SELECT COUNT(*) FROM track_logs")
|
||||
suspend fun count(): Int
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.core.db.dao
|
||||
import androidx.room.*
|
||||
import org.koitharu.kotatsu.core.db.entity.TrackEntity
|
||||
|
||||
|
||||
@Dao
|
||||
abstract class TracksDao {
|
||||
|
||||
@@ -32,7 +31,7 @@ abstract class TracksDao {
|
||||
abstract suspend fun delete(mangaId: Long)
|
||||
|
||||
@Query("DELETE FROM tracks WHERE manga_id NOT IN (SELECT manga_id FROM history UNION SELECT manga_id FROM favourites)")
|
||||
abstract suspend fun cleanup()
|
||||
abstract suspend fun gc()
|
||||
|
||||
@Transaction
|
||||
open suspend fun upsert(entity: TrackEntity) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.exceptions
|
||||
|
||||
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
|
||||
|
||||
class CompositeException(val errors: Collection<Throwable>) : Exception(
|
||||
message = errors.mapNotNullToSet { it.message }.joinToString()
|
||||
)
|
||||
class CompositeException(val errors: Collection<Throwable>) : Exception() {
|
||||
|
||||
override val message: String = errors.mapNotNullToSet { it.message }.joinToString()
|
||||
}
|
||||
@@ -1,14 +1,41 @@
|
||||
package org.koitharu.kotatsu.core.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import java.util.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class MangaTracking(
|
||||
class MangaTracking(
|
||||
val manga: Manga,
|
||||
val knownChaptersCount: Int,
|
||||
val lastChapterId: Long,
|
||||
val lastNotifiedChapterId: Long,
|
||||
val lastCheck: Date?
|
||||
)
|
||||
val lastCheck: Date?,
|
||||
) {
|
||||
|
||||
fun isEmpty(): Boolean {
|
||||
return knownChaptersCount <= 0 || lastChapterId == 0L
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MangaTracking
|
||||
|
||||
if (manga != other.manga) return false
|
||||
if (knownChaptersCount != other.knownChaptersCount) return false
|
||||
if (lastChapterId != other.lastChapterId) return false
|
||||
if (lastNotifiedChapterId != other.lastNotifiedChapterId) return false
|
||||
if (lastCheck != other.lastCheck) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = manga.hashCode()
|
||||
result = 31 * result + knownChaptersCount
|
||||
result = 31 * result + lastChapterId.hashCode()
|
||||
result = 31 * result + lastNotifiedChapterId.hashCode()
|
||||
result = 31 * result + (lastCheck?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
interface MangaRepository {
|
||||
|
||||
@@ -13,7 +13,7 @@ interface MangaRepository {
|
||||
|
||||
val sortOrders: Set<SortOrder>
|
||||
|
||||
suspend fun getList(offset: Int, query: String?): List<Manga>
|
||||
suspend fun getList(offset: Int, query: String): List<Manga>
|
||||
|
||||
suspend fun getList(offset: Int, tags: Set<MangaTag>?, sortOrder: SortOrder?): List<Manga>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class RemoteMangaRepository(private val parser: MangaParser) : MangaRepository {
|
||||
getConfig().defaultSortOrder = value
|
||||
}
|
||||
|
||||
override suspend fun getList(offset: Int, query: String?): List<Manga> {
|
||||
override suspend fun getList(offset: Int, query: String): List<Manga> {
|
||||
return parser.getList(offset, query)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ import androidx.annotation.WorkerThread
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.*
|
||||
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
@@ -22,12 +28,6 @@ import org.koitharu.kotatsu.utils.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||
import org.koitharu.kotatsu.utils.ext.readText
|
||||
import org.koitharu.kotatsu.utils.ext.resolveName
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
private const val MAX_PARALLELISM = 4
|
||||
|
||||
@@ -37,12 +37,12 @@ class LocalMangaRepository(private val storageManager: LocalStorageManager) : Ma
|
||||
private val filenameFilter = CbzFilter()
|
||||
private val locks = CompositeMutex<Long>()
|
||||
|
||||
override suspend fun getList(offset: Int, query: String?): List<Manga> {
|
||||
override suspend fun getList(offset: Int, query: String): List<Manga> {
|
||||
if (offset > 0) {
|
||||
return emptyList()
|
||||
}
|
||||
val list = getRawList()
|
||||
if (!query.isNullOrEmpty()) {
|
||||
if (query.isNotEmpty()) {
|
||||
list.retainAll { x ->
|
||||
x.title.contains(query, ignoreCase = true) ||
|
||||
x.altTitle?.contains(query, ignoreCase = true) == true
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.tracker
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.tracker.domain.Tracker
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.ui.FeedViewModel
|
||||
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
|
||||
@@ -13,5 +14,7 @@ val trackerModule
|
||||
factory { TrackingRepository(get()) }
|
||||
factory { TrackerNotificationChannels(androidContext(), get()) }
|
||||
|
||||
factory { Tracker(get()) }
|
||||
|
||||
viewModel { FeedViewModel(get()) }
|
||||
}
|
||||
132
app/src/main/java/org/koitharu/kotatsu/tracker/domain/Tracker.kt
Normal file
132
app/src/main/java/org/koitharu/kotatsu/tracker/domain/Tracker.kt
Normal file
@@ -0,0 +1,132 @@
|
||||
package org.koitharu.kotatsu.tracker.domain
|
||||
|
||||
import org.koitharu.kotatsu.core.model.MangaTracking
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
|
||||
|
||||
class Tracker(
|
||||
private val repository: TrackingRepository,
|
||||
) {
|
||||
|
||||
suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates {
|
||||
val repo = MangaRepository(track.manga.source)
|
||||
val details = repo.getDetails(track.manga)
|
||||
val chapters = details.chapters.orEmpty()
|
||||
if (track.isEmpty()) {
|
||||
// first check or manga was empty on last check
|
||||
if (commit) {
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = chapters.size,
|
||||
lastChapterId = chapters.lastOrNull()?.id ?: 0L,
|
||||
previousTrackChapterId = 0L,
|
||||
newChapters = emptyList(),
|
||||
saveTrackLog = false,
|
||||
)
|
||||
}
|
||||
return MangaUpdates(
|
||||
manga = details,
|
||||
newChapters = emptyList(),
|
||||
)
|
||||
}
|
||||
val newChapters = details.getNewChapters(track.lastChapterId)
|
||||
if (newChapters.isEmpty()) {
|
||||
if (commit) {
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = chapters.size,
|
||||
lastChapterId = chapters.lastOrNull()?.id ?: 0L,
|
||||
previousTrackChapterId = 0L,
|
||||
newChapters = emptyList(),
|
||||
saveTrackLog = false,
|
||||
)
|
||||
}
|
||||
return MangaUpdates(
|
||||
manga = details,
|
||||
newChapters = emptyList(),
|
||||
)
|
||||
}
|
||||
return when {
|
||||
|
||||
// the same chapters count
|
||||
chapters.size == track.knownChaptersCount -> {
|
||||
if (chapters.lastOrNull()?.id == track.lastChapterId) {
|
||||
// manga was not updated. skip
|
||||
MangaUpdates(
|
||||
manga = details,
|
||||
newChapters = emptyList(),
|
||||
)
|
||||
} else {
|
||||
// number of chapters still the same, bu last chapter changed.
|
||||
// maybe some chapters are removed. we need to find last known chapter
|
||||
val knownChapter = chapters.indexOfLast { it.id == track.lastChapterId }
|
||||
if (knownChapter == -1) {
|
||||
// confuse. reset anything
|
||||
if (commit) {
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = chapters.size,
|
||||
lastChapterId = chapters.lastOrNull()?.id ?: 0L,
|
||||
previousTrackChapterId = 0L,
|
||||
newChapters = emptyList(),
|
||||
saveTrackLog = false,
|
||||
)
|
||||
}
|
||||
MangaUpdates(
|
||||
manga = details,
|
||||
newChapters = emptyList(),
|
||||
)
|
||||
} else {
|
||||
val newChapters = chapters.takeLast(chapters.size - knownChapter + 1)
|
||||
if (commit) {
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = knownChapter + 1,
|
||||
lastChapterId = track.lastChapterId,
|
||||
previousTrackChapterId = track.lastNotifiedChapterId,
|
||||
newChapters = newChapters,
|
||||
saveTrackLog = true,
|
||||
)
|
||||
}
|
||||
MangaUpdates(
|
||||
manga = details,
|
||||
newChapters = details.getNewChapters(track.lastNotifiedChapterId),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val newChapters = chapters.takeLast(chapters.size - track.knownChaptersCount)
|
||||
if (commit) {
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = track.knownChaptersCount,
|
||||
lastChapterId = track.lastChapterId,
|
||||
previousTrackChapterId = track.lastNotifiedChapterId,
|
||||
newChapters = newChapters,
|
||||
saveTrackLog = true,
|
||||
)
|
||||
}
|
||||
MangaUpdates(
|
||||
manga = details,
|
||||
newChapters = details.getNewChapters(track.lastNotifiedChapterId),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Manga.getNewChapters(lastChapterId: Long): List<MangaChapter> {
|
||||
val chapters = chapters ?: return emptyList()
|
||||
if (lastChapterId == 0L) {
|
||||
return emptyList()
|
||||
}
|
||||
val raw = chapters.takeLastWhile { x -> x.id != lastChapterId }
|
||||
return if (raw.isEmpty() || raw.size == chapters.size) {
|
||||
emptyList()
|
||||
} else {
|
||||
raw
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.tracker.domain
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.*
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
@@ -11,7 +12,6 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import java.util.*
|
||||
|
||||
class TrackingRepository(
|
||||
private val db: MangaDatabase,
|
||||
@@ -28,7 +28,8 @@ class TrackingRepository(
|
||||
suspend fun getFavouritesManga(): Map<FavouriteCategory, List<Manga>> {
|
||||
val categories = db.favouriteCategoriesDao.findAll()
|
||||
return categories.associateTo(LinkedHashMap(categories.size)) { categoryEntity ->
|
||||
categoryEntity.toFavouriteCategory() to db.favouritesDao.findAllManga(categoryEntity.categoryId).toMangaList()
|
||||
categoryEntity.toFavouriteCategory() to db.favouritesDao.findAllManga(categoryEntity.categoryId)
|
||||
.toMangaList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +69,10 @@ class TrackingRepository(
|
||||
|
||||
suspend fun clearLogs() = db.trackLogsDao.clear()
|
||||
|
||||
suspend fun cleanup() {
|
||||
suspend fun gc() {
|
||||
db.withTransaction {
|
||||
db.tracksDao.cleanup()
|
||||
db.trackLogsDao.cleanup()
|
||||
db.tracksDao.gc()
|
||||
db.trackLogsDao.gc()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.koitharu.kotatsu.tracker.domain.model
|
||||
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
|
||||
class MangaUpdates(
|
||||
val manga: Manga,
|
||||
val newChapters: List<MangaChapter>,
|
||||
)
|
||||
@@ -14,35 +14,38 @@ import androidx.lifecycle.map
|
||||
import androidx.work.*
|
||||
import coil.ImageLoader
|
||||
import coil.request.ImageRequest
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.tracker.domain.Tracker
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.PendingIntentCompat
|
||||
import org.koitharu.kotatsu.utils.ext.referer
|
||||
import org.koitharu.kotatsu.utils.ext.toBitmapOrNull
|
||||
import org.koitharu.kotatsu.utils.ext.trySetForeground
|
||||
import org.koitharu.kotatsu.utils.progress.Progress
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams), KoinComponent {
|
||||
CoroutineWorker(context, workerParams),
|
||||
KoinComponent {
|
||||
|
||||
private val notificationManager by lazy {
|
||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
}
|
||||
|
||||
private val coil by inject<ImageLoader>()
|
||||
|
||||
private val repository by inject<TrackingRepository>()
|
||||
private val settings by inject<AppSettings>()
|
||||
private val channels by inject<TrackerNotificationChannels>()
|
||||
private val tracker by inject<Tracker>()
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
if (!settings.isTrackerEnabled) {
|
||||
@@ -54,84 +57,25 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
val tracks = getAllTracks()
|
||||
|
||||
var success = 0
|
||||
val workData = Data.Builder()
|
||||
.putInt(DATA_TOTAL, tracks.size)
|
||||
val workData = Data.Builder().putInt(DATA_TOTAL, tracks.size)
|
||||
for ((index, item) in tracks.withIndex()) {
|
||||
val (track, channelId) = item
|
||||
val details = runCatching {
|
||||
MangaRepository(track.manga.source).getDetails(track.manga)
|
||||
val updates = runCatching {
|
||||
tracker.fetchUpdates(track, commit = true)
|
||||
}.onSuccess {
|
||||
success++
|
||||
}.getOrNull()
|
||||
workData.putInt(DATA_PROGRESS, index)
|
||||
setProgress(workData.build())
|
||||
val chapters = details?.chapters ?: continue
|
||||
when {
|
||||
// first check or manga was empty on last check
|
||||
track.knownChaptersCount <= 0 || track.lastChapterId == 0L -> {
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = chapters.size,
|
||||
lastChapterId = chapters.lastOrNull()?.id ?: 0L,
|
||||
previousTrackChapterId = 0L,
|
||||
newChapters = emptyList(),
|
||||
saveTrackLog = false,
|
||||
)
|
||||
}
|
||||
// the same chapters count
|
||||
chapters.size == track.knownChaptersCount -> {
|
||||
if (chapters.lastOrNull()?.id == track.lastChapterId) {
|
||||
// manga was not updated. skip
|
||||
} else {
|
||||
// number of chapters still the same, bu last chapter changed.
|
||||
// maybe some chapters are removed. we need to find last known chapter
|
||||
val knownChapter = chapters.indexOfLast { it.id == track.lastChapterId }
|
||||
if (knownChapter == -1) {
|
||||
// confuse. reset anything
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = chapters.size,
|
||||
lastChapterId = chapters.lastOrNull()?.id ?: 0L,
|
||||
previousTrackChapterId = 0L,
|
||||
newChapters = emptyList(),
|
||||
saveTrackLog = false,
|
||||
)
|
||||
} else {
|
||||
val newChapters = chapters.takeLast(chapters.size - knownChapter + 1)
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = knownChapter + 1,
|
||||
lastChapterId = track.lastChapterId,
|
||||
previousTrackChapterId = track.lastNotifiedChapterId,
|
||||
newChapters = newChapters,
|
||||
saveTrackLog = true,
|
||||
)
|
||||
showNotification(
|
||||
details,
|
||||
channelId,
|
||||
newChapters.takeLastWhile { x -> x.id != track.lastNotifiedChapterId },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val newChapters = chapters.takeLast(chapters.size - track.knownChaptersCount)
|
||||
repository.storeTrackResult(
|
||||
mangaId = track.manga.id,
|
||||
knownChaptersCount = track.knownChaptersCount,
|
||||
lastChapterId = track.lastChapterId,
|
||||
previousTrackChapterId = track.lastNotifiedChapterId,
|
||||
newChapters = newChapters,
|
||||
saveTrackLog = true,
|
||||
)
|
||||
showNotification(
|
||||
manga = track.manga,
|
||||
channelId = channelId,
|
||||
newChapters = newChapters.takeLastWhile { x -> x.id != track.lastNotifiedChapterId },
|
||||
)
|
||||
}
|
||||
if (updates != null && updates.newChapters.isNotEmpty()) {
|
||||
showNotification(
|
||||
manga = updates.manga,
|
||||
channelId = channelId,
|
||||
newChapters = updates.newChapters,
|
||||
)
|
||||
}
|
||||
success++
|
||||
}
|
||||
repository.cleanup()
|
||||
repository.gc()
|
||||
return if (success == 0) {
|
||||
Result.retry()
|
||||
} else {
|
||||
@@ -194,8 +138,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
val colorPrimary = ContextCompat.getColor(applicationContext, R.color.blue_primary)
|
||||
val builder = NotificationCompat.Builder(applicationContext, channelId)
|
||||
val summary = applicationContext.resources.getQuantityString(
|
||||
R.plurals.new_chapters,
|
||||
newChapters.size, newChapters.size
|
||||
R.plurals.new_chapters, newChapters.size, newChapters.size
|
||||
)
|
||||
with(builder) {
|
||||
setContentText(summary)
|
||||
@@ -203,10 +146,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
setNumber(newChapters.size)
|
||||
setLargeIcon(
|
||||
coil.execute(
|
||||
ImageRequest.Builder(applicationContext)
|
||||
.data(manga.coverUrl)
|
||||
.referer(manga.publicUrl)
|
||||
.build()
|
||||
ImageRequest.Builder(applicationContext).data(manga.coverUrl).referer(manga.publicUrl).build()
|
||||
).toBitmapOrNull()
|
||||
)
|
||||
setSmallIcon(R.drawable.ic_stat_book_plus)
|
||||
@@ -220,8 +160,10 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
val intent = DetailsActivity.newIntent(applicationContext, manga)
|
||||
setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
applicationContext, id,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
applicationContext,
|
||||
id,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
setAutoCancel(true)
|
||||
@@ -251,9 +193,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
val title = applicationContext.getString(R.string.check_for_new_chapters)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
WORKER_CHANNEL_ID,
|
||||
title,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
WORKER_CHANNEL_ID, title, NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
channel.setShowBadge(false)
|
||||
channel.enableVibration(false)
|
||||
@@ -262,17 +202,11 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
val notification = NotificationCompat.Builder(applicationContext, WORKER_CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||
.setDefaults(0)
|
||||
.setColor(ContextCompat.getColor(applicationContext, R.color.blue_primary_dark))
|
||||
.setSilent(true)
|
||||
.setProgress(0, 0, true)
|
||||
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_DEFERRED)
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
val notification = NotificationCompat.Builder(applicationContext, WORKER_CHANNEL_ID).setContentTitle(title)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN).setDefaults(0)
|
||||
.setColor(ContextCompat.getColor(applicationContext, R.color.blue_primary_dark)).setSilent(true)
|
||||
.setProgress(0, 0, true).setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_DEFERRED).setOngoing(true).build()
|
||||
|
||||
return ForegroundInfo(WORKER_NOTIFICATION_ID, notification)
|
||||
}
|
||||
@@ -287,44 +221,31 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
|
||||
private const val TAG_ONESHOT = "tracking_oneshot"
|
||||
|
||||
fun setup(context: Context) {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
val request = PeriodicWorkRequestBuilder<TrackWorker>(4, TimeUnit.HOURS)
|
||||
.setConstraints(constraints)
|
||||
.addTag(TAG)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES)
|
||||
.build()
|
||||
WorkManager.getInstance(context)
|
||||
.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, request)
|
||||
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
|
||||
val request =
|
||||
PeriodicWorkRequestBuilder<TrackWorker>(4, TimeUnit.HOURS).setConstraints(constraints).addTag(TAG)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES).build()
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, request)
|
||||
}
|
||||
|
||||
fun startNow(context: Context) {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
val request = OneTimeWorkRequestBuilder<TrackWorker>()
|
||||
.setConstraints(constraints)
|
||||
.addTag(TAG_ONESHOT)
|
||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||
.build()
|
||||
WorkManager.getInstance(context)
|
||||
.enqueue(request)
|
||||
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
|
||||
val request = OneTimeWorkRequestBuilder<TrackWorker>().setConstraints(constraints).addTag(TAG_ONESHOT)
|
||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
}
|
||||
|
||||
fun getProgressLiveData(context: Context): LiveData<Progress?> {
|
||||
return WorkManager.getInstance(context)
|
||||
.getWorkInfosByTagLiveData(TAG)
|
||||
.map { list ->
|
||||
list.find { work ->
|
||||
work.state == WorkInfo.State.RUNNING
|
||||
}?.let { workInfo ->
|
||||
Progress(
|
||||
value = workInfo.progress.getInt(DATA_PROGRESS, 0),
|
||||
total = workInfo.progress.getInt(DATA_TOTAL, -1)
|
||||
).takeUnless { it.isIndeterminate }
|
||||
}
|
||||
return WorkManager.getInstance(context).getWorkInfosByTagLiveData(TAG).map { list ->
|
||||
list.find { work ->
|
||||
work.state == WorkInfo.State.RUNNING
|
||||
}?.let { workInfo ->
|
||||
Progress(
|
||||
value = workInfo.progress.getInt(DATA_PROGRESS, 0),
|
||||
total = workInfo.progress.getInt(DATA_TOTAL, -1)
|
||||
).takeUnless { it.isIndeterminate }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user