Refactor downloading-related classes

This commit is contained in:
Koitharu
2023-05-08 16:49:58 +03:00
parent 42df607f52
commit ac9680b5c0
11 changed files with 75 additions and 40 deletions

View File

@@ -102,6 +102,12 @@ dependencies {
implementation 'com.google.android.material:material:1.9.0' implementation 'com.google.android.material:material:1.9.0'
//noinspection LifecycleAnnotationProcessorWithJava8 //noinspection LifecycleAnnotationProcessorWithJava8
kapt 'androidx.lifecycle:lifecycle-compiler:2.6.1' kapt 'androidx.lifecycle:lifecycle-compiler:2.6.1'
/**
* TODO: check
* https://issuetracker.google.com/issues/270245927
* https://issuetracker.google.com/issues/280504155
*/
implementation 'androidx.work:work-runtime-ktx:2.8.1' implementation 'androidx.work:work-runtime-ktx:2.8.1'
//noinspection GradleDependency //noinspection GradleDependency
implementation('com.google.guava:guava:31.1-android') { implementation('com.google.guava:guava:31.1-android') {

View File

@@ -6,7 +6,7 @@ import org.koitharu.kotatsu.local.data.LocalManga
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import java.util.Date import java.util.Date
data class DownloadState2( data class DownloadState(
val manga: Manga, val manga: Manga,
val isIndeterminate: Boolean, val isIndeterminate: Boolean,
val isPaused: Boolean = false, val isPaused: Boolean = false,
@@ -46,6 +46,50 @@ data class DownloadState2(
.putBoolean(DATA_PAUSED, isPaused) .putBoolean(DATA_PAUSED, isPaused)
.build() .build()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DownloadState
if (manga != other.manga) return false
if (isIndeterminate != other.isIndeterminate) return false
if (isPaused != other.isPaused) return false
if (isStopped != other.isStopped) return false
if (error != other.error) return false
if (totalChapters != other.totalChapters) return false
if (currentChapter != other.currentChapter) return false
if (totalPages != other.totalPages) return false
if (currentPage != other.currentPage) return false
if (eta != other.eta) return false
if (localManga != other.localManga) return false
if (!downloadedChapters.contentEquals(other.downloadedChapters)) return false
if (timestamp != other.timestamp) return false
if (max != other.max) return false
if (progress != other.progress) return false
return percent == other.percent
}
override fun hashCode(): Int {
var result = manga.hashCode()
result = 31 * result + isIndeterminate.hashCode()
result = 31 * result + isPaused.hashCode()
result = 31 * result + isStopped.hashCode()
result = 31 * result + (error?.hashCode() ?: 0)
result = 31 * result + totalChapters
result = 31 * result + currentChapter
result = 31 * result + totalPages
result = 31 * result + currentPage
result = 31 * result + eta.hashCode()
result = 31 * result + (localManga?.hashCode() ?: 0)
result = 31 * result + downloadedChapters.contentHashCode()
result = 31 * result + timestamp.hashCode()
result = 31 * result + max
result = 31 * result + progress
result = 31 * result + percent.hashCode()
return result
}
companion object { companion object {
private const val DATA_MANGA_ID = "manga_id" private const val DATA_MANGA_ID = "manga_id"

View File

@@ -19,7 +19,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.base.domain.MangaDataRepository
import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.ui.DateTimeAgo import org.koitharu.kotatsu.core.ui.DateTimeAgo
import org.koitharu.kotatsu.download.domain.DownloadState2 import org.koitharu.kotatsu.download.domain.DownloadState
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
@@ -173,21 +173,21 @@ class DownloadsViewModel @Inject constructor(
private suspend fun WorkInfo.toUiModel(): DownloadItemModel? { private suspend fun WorkInfo.toUiModel(): DownloadItemModel? {
val workData = if (outputData == Data.EMPTY) progress else outputData val workData = if (outputData == Data.EMPTY) progress else outputData
val mangaId = DownloadState2.getMangaId(workData) val mangaId = DownloadState.getMangaId(workData)
if (mangaId == 0L) return null if (mangaId == 0L) return null
val manga = getManga(mangaId) ?: return null val manga = getManga(mangaId) ?: return null
return DownloadItemModel( return DownloadItemModel(
id = id, id = id,
workState = state, workState = state,
manga = manga, manga = manga,
error = DownloadState2.getError(workData), error = DownloadState.getError(workData),
isIndeterminate = DownloadState2.isIndeterminate(workData), isIndeterminate = DownloadState.isIndeterminate(workData),
isPaused = DownloadState2.isPaused(workData), isPaused = DownloadState.isPaused(workData),
max = DownloadState2.getMax(workData), max = DownloadState.getMax(workData),
progress = DownloadState2.getProgress(workData), progress = DownloadState.getProgress(workData),
eta = DownloadState2.getEta(workData), eta = DownloadState.getEta(workData),
timestamp = DownloadState2.getTimestamp(workData), timestamp = DownloadState.getTimestamp(workData),
totalChapters = DownloadState2.getDownloadedChapters(workData).size, totalChapters = DownloadState.getDownloadedChapters(workData).size,
) )
} }

View File

@@ -25,7 +25,7 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.download.domain.DownloadState2 import org.koitharu.kotatsu.download.domain.DownloadState
import org.koitharu.kotatsu.download.ui.list.DownloadsActivity import org.koitharu.kotatsu.download.ui.list.DownloadsActivity
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -100,7 +100,7 @@ class DownloadNotificationFactory @AssistedInject constructor(
builder.priority = NotificationCompat.PRIORITY_DEFAULT builder.priority = NotificationCompat.PRIORITY_DEFAULT
} }
suspend fun create(state: DownloadState2?): Notification = mutex.withLock { suspend fun create(state: DownloadState?): Notification = mutex.withLock {
builder.setContentTitle(state?.manga?.title ?: context.getString(R.string.preparing_)) builder.setContentTitle(state?.manga?.title ?: context.getString(R.string.preparing_))
builder.setContentText(context.getString(R.string.manga_downloading_)) builder.setContentText(context.getString(R.string.manga_downloading_))
builder.setProgress(1, 0, true) builder.setProgress(1, 0, true)

View File

@@ -39,8 +39,7 @@ import org.koitharu.kotatsu.base.domain.MangaDataRepository
import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.download.domain.DownloadState2 import org.koitharu.kotatsu.download.domain.DownloadState
import org.koitharu.kotatsu.download.ui.service.PausingHandle
import org.koitharu.kotatsu.local.data.LocalManga import org.koitharu.kotatsu.local.data.LocalManga
import org.koitharu.kotatsu.local.data.LocalStorageChanges import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.local.data.PagesCache
@@ -52,6 +51,7 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.utils.Throttler
import org.koitharu.kotatsu.utils.WorkManagerHelper import org.koitharu.kotatsu.utils.WorkManagerHelper
import org.koitharu.kotatsu.utils.ext.deleteAwait import org.koitharu.kotatsu.utils.ext.deleteAwait
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
@@ -83,8 +83,8 @@ class DownloadWorker @AssistedInject constructor(
private val notificationManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private val notificationManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@Volatile @Volatile
private var lastPublishedState: DownloadState2? = null private var lastPublishedState: DownloadState? = null
private val currentState: DownloadState2 private val currentState: DownloadState
get() = checkNotNull(lastPublishedState) get() = checkNotNull(lastPublishedState)
private val pausingHandle = PausingHandle() private val pausingHandle = PausingHandle()
@@ -98,7 +98,7 @@ class DownloadWorker @AssistedInject constructor(
val manga = mangaDataRepository.findMangaById(mangaId) ?: return Result.failure() val manga = mangaDataRepository.findMangaById(mangaId) ?: return Result.failure()
val chaptersIds = inputData.getLongArray(CHAPTERS_IDS)?.takeUnless { it.isEmpty() } val chaptersIds = inputData.getLongArray(CHAPTERS_IDS)?.takeUnless { it.isEmpty() }
val downloadedIds = getDoneChapters() val downloadedIds = getDoneChapters()
lastPublishedState = DownloadState2(manga, isIndeterminate = true) lastPublishedState = DownloadState(manga, isIndeterminate = true)
return try { return try {
downloadMangaImpl(chaptersIds, downloadedIds) downloadMangaImpl(chaptersIds, downloadedIds)
Result.success(currentState.toWorkData()) Result.success(currentState.toWorkData())
@@ -291,7 +291,7 @@ class DownloadWorker @AssistedInject constructor(
return file return file
} }
private suspend fun publishState(state: DownloadState2) { private suspend fun publishState(state: DownloadState) {
val previousState = currentState val previousState = currentState
lastPublishedState = state lastPublishedState = state
if (previousState.isParticularProgress && state.isParticularProgress) { if (previousState.isParticularProgress && state.isParticularProgress) {
@@ -314,7 +314,7 @@ class DownloadWorker @AssistedInject constructor(
private suspend fun getDoneChapters(): LongArray { private suspend fun getDoneChapters(): LongArray {
val work = WorkManagerHelper(WorkManager.getInstance(applicationContext)).getWorkInfoById(id) val work = WorkManagerHelper(WorkManager.getInstance(applicationContext)).getWorkInfoById(id)
?: return LongArray(0) ?: return LongArray(0)
return DownloadState2.getDownloadedChapters(work.progress) return DownloadState.getDownloadedChapters(work.progress)
} }
private fun getChapters( private fun getChapters(

View File

@@ -1,4 +1,4 @@
package org.koitharu.kotatsu.download.ui.service package org.koitharu.kotatsu.download.ui.worker
import androidx.annotation.AnyThread import androidx.annotation.AnyThread
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow

View File

@@ -7,7 +7,6 @@ import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.PatternMatcher import android.os.PatternMatcher
import androidx.core.app.PendingIntentCompat import androidx.core.app.PendingIntentCompat
import org.koitharu.kotatsu.download.ui.service.PausingHandle
import org.koitharu.kotatsu.utils.ext.toUUIDOrNull import org.koitharu.kotatsu.utils.ext.toUUIDOrNull
import java.util.UUID import java.util.UUID

View File

@@ -26,7 +26,7 @@ import org.koitharu.kotatsu.parsers.util.json.mapJSONTo
import org.koitharu.kotatsu.sync.data.SyncAuthApi import org.koitharu.kotatsu.sync.data.SyncAuthApi
import org.koitharu.kotatsu.sync.data.SyncAuthenticator import org.koitharu.kotatsu.sync.data.SyncAuthenticator
import org.koitharu.kotatsu.sync.data.SyncInterceptor import org.koitharu.kotatsu.sync.data.SyncInterceptor
import org.koitharu.kotatsu.utils.GZipInterceptor import org.koitharu.kotatsu.core.network.GZipInterceptor
import org.koitharu.kotatsu.utils.ext.parseJsonOrNull import org.koitharu.kotatsu.utils.ext.parseJsonOrNull
import org.koitharu.kotatsu.utils.ext.toContentValues import org.koitharu.kotatsu.utils.ext.toContentValues
import org.koitharu.kotatsu.utils.ext.toJson import org.koitharu.kotatsu.utils.ext.toJson

View File

@@ -1,14 +0,0 @@
package org.koitharu.kotatsu.utils
import okhttp3.Interceptor
import okhttp3.Response
import org.koitharu.kotatsu.core.network.CommonHeaders.CONTENT_ENCODING
class GZipInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val newRequest = chain.request().newBuilder()
newRequest.addHeader(CONTENT_ENCODING, "gzip")
return chain.proceed(newRequest.build())
}
}

View File

@@ -1,4 +1,4 @@
package org.koitharu.kotatsu.download.ui.worker package org.koitharu.kotatsu.utils
import android.os.SystemClock import android.os.SystemClock

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.utils.progress
import androidx.annotation.AnyThread import androidx.annotation.AnyThread
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.download.ui.service.PausingHandle import org.koitharu.kotatsu.download.ui.worker.PausingHandle
class PausingProgressJob<P>( class PausingProgressJob<P>(
job: Job, job: Job,
@@ -23,4 +23,4 @@ class PausingProgressJob<P>(
@AnyThread @AnyThread
fun resume() = pausingHandle.resume() fun resume() = pausingHandle.resume()
} }