Restore download after network error

This commit is contained in:
Koitharu
2020-06-29 09:48:17 +03:00
parent 79058440a1
commit a2f09d8763
10 changed files with 93 additions and 42 deletions

View File

@@ -3,6 +3,7 @@
<words>
<w>chucker</w>
<w>desu</w>
<w>failsafe</w>
<w>koin</w>
<w>kotatsu</w>
<w>manga</w>

View File

@@ -62,7 +62,6 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6'
implementation 'androidx.core:core-ktx:1.5.0-alpha01'
implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
implementation 'androidx.activity:activity-ktx:1.2.0-alpha06'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha06'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha05'

View File

@@ -72,7 +72,6 @@ class LocalMangaRepository : MangaRepository, KoinComponent {
}
}
fun delete(manga: Manga): Boolean {
val file = Uri.parse(manga.url).toFile()
return file.delete()

View File

@@ -13,7 +13,8 @@ import java.util.*
object MangaProviderFactory : KoinComponent {
private val loaderContext by inject<MangaLoaderContext>()
private val cache = EnumMap<MangaSource, WeakReference<MangaRepository>>(MangaSource::class.java)
private val cache =
EnumMap<MangaSource, WeakReference<MangaRepository>>(MangaSource::class.java)
fun getSources(includeHidden: Boolean): List<MangaSource> {
val settings = get<AppSettings>()
@@ -33,24 +34,37 @@ object MangaProviderFactory : KoinComponent {
}
}
fun createLocal(): LocalMangaRepository =
(cache[MangaSource.LOCAL]?.get() as? LocalMangaRepository)
?: LocalMangaRepository().also {
cache[MangaSource.LOCAL] = WeakReference<MangaRepository>(it)
fun createLocal(): LocalMangaRepository {
var instance = cache[MangaSource.LOCAL]?.get()
if (instance == null) {
synchronized(cache) {
instance = cache[MangaSource.LOCAL]?.get()
if (instance == null) {
instance = LocalMangaRepository()
cache[MangaSource.LOCAL] = WeakReference<MangaRepository>(instance)
}
}
}
return instance as LocalMangaRepository
}
@Throws(Throwable::class)
fun create(source: MangaSource): MangaRepository {
cache[source]?.get()?.let {
return it
var instance = cache[source]?.get()
if (instance == null) {
synchronized(cache) {
instance = cache[source]?.get()
if (instance == null) {
instance = try {
source.cls.getDeclaredConstructor(MangaLoaderContext::class.java)
.newInstance(loaderContext)
} catch (e: NoSuchMethodException) {
source.cls.newInstance()
}
cache[source] = WeakReference(instance!!)
}
}
}
val instance = try {
source.cls.getDeclaredConstructor(MangaLoaderContext::class.java)
.newInstance(loaderContext)
} catch (e: NoSuchMethodException) {
source.cls.newInstance()
}
cache[source] = WeakReference<MangaRepository>(instance)
return instance
return instance!!
}
}

View File

@@ -92,6 +92,11 @@ class DownloadNotification(private val context: Context) {
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS)
}
fun setWaitingForNetwork() {
builder.setProgress(0, 0, false)
builder.setContentText(context.getString(R.string.waiting_for_network))
}
fun setPostProcessing() {
builder.setProgress(1, 0, true)
builder.setContentText(context.getString(R.string.processing_))

View File

@@ -13,6 +13,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.IOException
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
@@ -24,10 +25,7 @@ import org.koitharu.kotatsu.domain.local.MangaZip
import org.koitharu.kotatsu.ui.common.BaseService
import org.koitharu.kotatsu.ui.common.dialog.CheckBoxAlertDialog
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.utils.ext.await
import org.koitharu.kotatsu.utils.ext.retryUntilSuccess
import org.koitharu.kotatsu.utils.ext.safe
import org.koitharu.kotatsu.utils.ext.sub
import org.koitharu.kotatsu.utils.ext.*
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.collections.set
@@ -37,6 +35,7 @@ class DownloadService : BaseService() {
private lateinit var notification: DownloadNotification
private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var connectivityManager: ConnectivityManager
private val okHttp by inject<OkHttpClient>()
private val cache by inject<PagesCache>()
@@ -47,6 +46,7 @@ class DownloadService : BaseService() {
override fun onCreate() {
super.onCreate()
notification = DownloadNotification(this)
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager)
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading")
}
@@ -88,9 +88,11 @@ class DownloadService : BaseService() {
try {
val repo = MangaProviderFactory.create(manga.source)
val cover = safe {
Coil.execute(GetRequestBuilder(this@DownloadService)
.data(manga.coverUrl)
.build()).drawable
Coil.execute(
GetRequestBuilder(this@DownloadService)
.data(manga.coverUrl)
.build()
).drawable
}
withContext(Dispatchers.Main) {
notification.setLargeIcon(cover)
@@ -112,23 +114,30 @@ class DownloadService : BaseService() {
if (chaptersIds == null || chapter.id in chaptersIds) {
val pages = repo.getPages(chapter)
for ((pageIndex, page) in pages.withIndex()) {
val url = repo.getPageFullUrl(page)
val file = cache[url] ?: downloadPage(url, destination)
output.addPage(
chapter,
file,
pageIndex,
MimeTypeMap.getFileExtensionFromUrl(url)
failsafe@ do {
try {
val url = repo.getPageFullUrl(page)
val file = cache[url] ?: downloadPage(url, destination)
output.addPage(
chapter,
file,
pageIndex,
MimeTypeMap.getFileExtensionFromUrl(url)
)
} catch (e: IOException) {
notification.setWaitingForNetwork()
notification.update()
connectivityManager.waitForNetwork()
continue@failsafe
}
} while (false)
notification.setProgress(
chapters.size,
pages.size,
chapterIndex,
pageIndex
)
withContext(Dispatchers.Main) {
notification.setProgress(
chapters.size,
pages.size,
chapterIndex,
pageIndex
)
notification.update()
}
notification.update()
}
}
}

View File

@@ -13,7 +13,6 @@ import org.koin.core.get
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory
@@ -33,7 +32,8 @@ class LocalListPresenter : BasePresenter<MangaListView<File>>() {
private lateinit var repository: LocalMangaRepository
override fun onFirstViewAttach() {
repository = MangaProviderFactory.create(MangaSource.LOCAL) as LocalMangaRepository
repository = MangaProviderFactory.createLocal()
super.onFirstViewAttach()
}

View File

@@ -0,0 +1,22 @@
package org.koitharu.kotatsu.utils.ext
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkRequest
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
suspend fun ConnectivityManager.waitForNetwork(): Network {
val request = NetworkRequest.Builder().build()
return suspendCancellableCoroutine<Network> { cont ->
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
cont.resume(network)
}
}
registerNetworkCallback(request, callback)
cont.invokeOnCancellation {
unregisterNetworkCallback(callback)
}
}
}

View File

@@ -139,4 +139,5 @@
<string name="related">Похожие</string>
<string name="new_version_s">Новая версия: %s</string>
<string name="size_s">Размер: %s</string>
<string name="waiting_for_network">Ожидание подключения…</string>
</resources>

View File

@@ -140,4 +140,5 @@
<string name="related">Related</string>
<string name="new_version_s">New version: %s</string>
<string name="size_s">Size: %s</string>
<string name="waiting_for_network">Waiting for network…</string>
</resources>