Refactor downloading-related classes
This commit is contained in:
@@ -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') {
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.koitharu.kotatsu.download.ui.worker
|
package org.koitharu.kotatsu.utils
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
|
|
||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user