Restore download after network error

pull/26/head
Koitharu 6 years ago
parent 79058440a1
commit a2f09d8763

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

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

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

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

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

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

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

@ -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)
}
}
}

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

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