diff --git a/app/build.gradle b/app/build.gradle
index 90302a0cb..fd4388643 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -67,26 +67,26 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
implementation 'androidx.core:core-ktx:1.5.0-alpha05'
- implementation 'androidx.activity:activity-ktx:1.2.0-beta02'
- implementation 'androidx.fragment:fragment-ktx:1.3.0-beta02'
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-beta01'
- implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01'
- implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-beta01'
- implementation 'androidx.lifecycle:lifecycle-service:2.3.0-beta01'
- implementation 'androidx.lifecycle:lifecycle-process:2.3.0-beta01'
+ implementation 'androidx.activity:activity-ktx:1.2.0-rc01'
+ implementation 'androidx.fragment:fragment-ktx:1.3.0-rc01'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-rc01'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-rc01'
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-rc01'
+ implementation 'androidx.lifecycle:lifecycle-service:2.3.0-rc01'
+ implementation 'androidx.lifecycle:lifecycle-process:2.3.0-rc01'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.2.0-beta01'
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.work:work-runtime-ktx:2.4.0'
- implementation 'com.google.android.material:material:1.3.0-alpha04'
+ implementation 'com.google.android.material:material:1.3.0-beta01'
//noinspection LifecycleAnnotationProcessorWithJava8
- kapt 'androidx.lifecycle:lifecycle-compiler:2.3.0-beta01'
+ kapt 'androidx.lifecycle:lifecycle-compiler:2.3.0-rc01'
- implementation 'androidx.room:room-runtime:2.2.5'
- implementation 'androidx.room:room-ktx:2.2.5'
- kapt 'androidx.room:room-compiler:2.2.5'
+ implementation 'androidx.room:room-runtime:2.2.6'
+ implementation 'androidx.room:room-ktx:2.2.6'
+ kapt 'androidx.room:room-compiler:2.2.6'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.squareup.okio:okio:2.9.0'
@@ -95,8 +95,8 @@ dependencies {
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.0'
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.0'
- implementation 'org.koin:koin-android:2.2.0'
- implementation 'org.koin:koin-androidx-viewmodel:2.2.0'
+ implementation 'org.koin:koin-android:2.2.2'
+ implementation 'org.koin:koin-androidx-viewmodel:2.2.2'
implementation 'io.coil-kt:coil-base:1.1.0'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.tomclaw.cache:cache:1.0'
@@ -105,5 +105,5 @@ dependencies {
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.json:json:20201115'
- testImplementation 'org.koin:koin-test:2.2.0-rc-2'
+ testImplementation 'org.koin:koin-test:2.2.2'
}
\ No newline at end of file
diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml
new file mode 100644
index 000000000..5a9c5bfd8
--- /dev/null
+++ b/app/src/debug/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Kotatsu Dev
+
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt
index e35156460..080aa9d91 100644
--- a/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt
@@ -40,5 +40,7 @@ abstract class AlertDialogFragment : DialogFragment() {
open fun onBuildDialog(builder: AlertDialog.Builder) = Unit
+ protected fun bindingOrNull(): B? = viewBinding
+
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt
index 23efa5f10..5bd4a51a3 100644
--- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt
@@ -16,6 +16,7 @@ import androidx.viewbinding.ViewBinding
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
abstract class BaseActivity : AppCompatActivity(), OnApplyWindowInsetsListener {
@@ -23,6 +24,11 @@ abstract class BaseActivity : AppCompatActivity(), OnApplyWindo
protected lateinit var binding: B
private set
+
+ protected val exceptionResolver by lazy(LazyThreadSafetyMode.NONE) {
+ ExceptionResolver(this, supportFragmentManager)
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
if (get().isAmoledTheme) {
setTheme(R.style.AppTheme_Amoled)
diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseFragment.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseFragment.kt
index 107bcd839..70a55214a 100644
--- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseFragment.kt
@@ -11,6 +11,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
abstract class BaseFragment : Fragment(), OnApplyWindowInsetsListener {
@@ -19,6 +20,10 @@ abstract class BaseFragment : Fragment(), OnApplyWindowInsetsLi
protected val binding: B
get() = checkNotNull(viewBinding)
+ protected val exceptionResolver by lazy(LazyThreadSafetyMode.NONE) {
+ ExceptionResolver(viewLifecycleOwner, childFragmentManager)
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt
index b955ec529..9b731229c 100644
--- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt
@@ -7,15 +7,13 @@ import android.webkit.WebViewClient
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl.Companion.toHttpUrl
-import org.koin.core.component.KoinComponent
-import org.koin.core.component.get
class CloudFlareClient(
+ private val cookieJar: CookieJar,
private val callback: CloudFlareCallback,
private val targetUrl: String
-) : WebViewClient(), KoinComponent {
+) : WebViewClient() {
- private val cookieJar = get()
private val cookieManager = CookieManager.getInstance()
init {
@@ -39,7 +37,8 @@ class CloudFlareClient(
private fun checkClearance() {
val httpUrl = targetUrl.toHttpUrl()
- val cookies = cookieManager.getCookie(targetUrl).split(';').mapNotNull {
+ val rawCookie = cookieManager.getCookie(targetUrl) ?: return
+ val cookies = rawCookie.split(';').mapNotNull {
Cookie.parse(httpUrl, it)
}
if (cookies.none { it.name == CF_CLEARANCE }) {
diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt
index a3464e778..641a17a6a 100644
--- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt
@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.browser.cloudflare
import android.annotation.SuppressLint
+import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -9,7 +10,8 @@ import android.webkit.CookieManager
import android.webkit.WebSettings
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isInvisible
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import androidx.fragment.app.setFragmentResult
+import org.koin.android.ext.android.get
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.network.UserAgentInterceptor
import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding
@@ -19,6 +21,7 @@ import org.koitharu.kotatsu.utils.ext.withArgs
class CloudFlareDialog : AlertDialogFragment(), CloudFlareCallback {
private val url by stringArgument(ARG_URL)
+ private val pendingResult = Bundle(1)
override fun onInflateView(
inflater: LayoutInflater,
@@ -35,7 +38,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud
databaseEnabled = true
userAgentString = UserAgentInterceptor.userAgent
}
- binding.webView.webViewClient = CloudFlareClient(this, url.orEmpty())
+ binding.webView.webViewClient = CloudFlareClient(get(), this, url.orEmpty())
CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webView, true)
if (url.isNullOrEmpty()) {
dismissAllowingStateLoss()
@@ -63,18 +66,24 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud
super.onPause()
}
+ override fun onDismiss(dialog: DialogInterface) {
+ setFragmentResult(TAG, pendingResult)
+ super.onDismiss(dialog)
+ }
+
override fun onPageLoaded() {
- binding.progressBar.isInvisible = true
+ bindingOrNull()?.progressBar?.isInvisible = true
}
override fun onCheckPassed() {
- ((parentFragment ?: activity) as? SwipeRefreshLayout.OnRefreshListener)?.onRefresh()
+ pendingResult.putBoolean(EXTRA_RESULT, true)
dismiss()
}
companion object {
const val TAG = "CloudFlareDialog"
+ const val EXTRA_RESULT = "result"
private const val ARG_URL = "url"
fun newInstance(url: String) = CloudFlareDialog().withArgs(1) {
diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt
index 6705ff77d..f435f9930 100644
--- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/CloudFlareProtectedException.kt
@@ -1,5 +1,12 @@
package org.koitharu.kotatsu.core.exceptions
import okio.IOException
+import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
-class CloudFlareProtectedException(val url: String) : IOException("Protected by CloudFlare")
\ No newline at end of file
+class CloudFlareProtectedException(
+ val url: String
+) : IOException("Protected by CloudFlare"), ResolvableException {
+
+ override val resolveTextId: Int = R.string.resolve
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt
new file mode 100644
index 000000000..906773ab8
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt
@@ -0,0 +1,38 @@
+package org.koitharu.kotatsu.core.exceptions.resolve
+
+import android.util.ArrayMap
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.LifecycleOwner
+import kotlinx.coroutines.suspendCancellableCoroutine
+import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
+import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
+
+class ExceptionResolver(
+ private val lifecycleOwner: LifecycleOwner,
+ private val fm: FragmentManager
+) {
+
+ private val continuations = ArrayMap>(1)
+
+ suspend fun resolve(e: ResolvableException): Boolean = when (e) {
+ is CloudFlareProtectedException -> resolveCF(e.url)
+ else -> false
+ }
+
+ private suspend fun resolveCF(url: String) = suspendCancellableCoroutine { cont ->
+ val dialog = CloudFlareDialog.newInstance(url)
+ fm.clearFragmentResult(CloudFlareDialog.TAG)
+ continuations[CloudFlareDialog.TAG] = cont
+ fm.setFragmentResultListener(CloudFlareDialog.TAG, lifecycleOwner) { key, result ->
+ continuations.remove(key)?.resume(result.getBoolean(CloudFlareDialog.EXTRA_RESULT))
+ }
+ dialog.show(fm, CloudFlareDialog.TAG)
+ cont.invokeOnCancellation {
+ continuations.remove(CloudFlareDialog.TAG, cont)
+ fm.clearFragmentResultListener(CloudFlareDialog.TAG)
+ dialog.dismiss()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ResolvableException.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ResolvableException.kt
new file mode 100644
index 000000000..6ea802b61
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ResolvableException.kt
@@ -0,0 +1,6 @@
+package org.koitharu.kotatsu.core.exceptions.resolve
+
+interface ResolvableException {
+
+ val resolveTextId: Int
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/core/ui/ChipsFactory.kt b/app/src/main/java/org/koitharu/kotatsu/core/ui/ChipsFactory.kt
index c7d28982b..d18524e4b 100644
--- a/app/src/main/java/org/koitharu/kotatsu/core/ui/ChipsFactory.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/core/ui/ChipsFactory.kt
@@ -27,6 +27,7 @@ class ChipsFactory(val context: Context) {
chip.setChipIconResource(iconRes)
}
chip.tag = tag
+ chip.setEnsureMinTouchTargetSize(false)
chip.setOnClickListener(onClickListener)
return chip
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt
index b4f3bec58..73ed41739 100644
--- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt
@@ -16,6 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
@@ -26,6 +27,7 @@ import org.koitharu.kotatsu.base.ui.list.decor.SectionItemDecoration
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
+import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.prefs.ListMode
@@ -38,6 +40,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.utils.ext.clearItemDecorations
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.toggleDrawer
+import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
abstract class MangaListFragment : BaseFragment(),
PaginationScrollListener.Callback, OnListItemClickListener, OnFilterChangedListener,
@@ -64,9 +67,7 @@ abstract class MangaListFragment : BaseFragment(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- listAdapter = MangaListAdapter(get(), viewLifecycleOwner, this) {
- viewModel.onRetry()
- }
+ listAdapter = MangaListAdapter(get(), viewLifecycleOwner, this, ::resolveException)
paginationListener = PaginationScrollListener(4, this)
with(binding.recyclerView) {
setHasFixedSize(true)
@@ -163,6 +164,18 @@ abstract class MangaListFragment : BaseFragment(),
}
}
+ private fun resolveException(e: Throwable) {
+ if (e is ResolvableException) {
+ viewLifecycleScope.launch {
+ if (exceptionResolver.resolve(e)) {
+ viewModel.onRetry()
+ }
+ }
+ } else {
+ viewModel.onRetry()
+ }
+ }
+
@CallSuper
protected open fun onLoadingStateChanged(isLoading: Boolean) {
binding.swipeRefreshLayout.isEnabled =
diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorFooterAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorFooterAD.kt
index c8ce8b96c..6e777f36b 100644
--- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorFooterAD.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorFooterAD.kt
@@ -7,13 +7,13 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
fun errorFooterAD(
- onRetryClick: () -> Unit
+ onRetryClick: (Throwable) -> Unit
) = adapterDelegateViewBinding(
{ inflater, parent -> ItemErrorFooterBinding.inflate(inflater, parent, false) }
) {
binding.root.setOnClickListener {
- onRetryClick()
+ onRetryClick(item.exception)
}
bind {
diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt
index c1ffe00a5..eb78e6700 100644
--- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/ErrorStateListAD.kt
@@ -8,13 +8,13 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
fun errorStateListAD(
- onRetryClick: () -> Unit
+ onRetryClick: (Throwable) -> Unit
) = adapterDelegateViewBinding(
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }
) {
binding.buttonRetry.setOnClickListener {
- onRetryClick()
+ onRetryClick(item.exception)
}
bind {
@@ -22,6 +22,9 @@ fun errorStateListAD(
text = item.exception.getDisplayMessage(context.resources)
setCompoundDrawablesWithIntrinsicBounds(0, item.icon, 0, 0)
}
- binding.buttonRetry.isVisible = item.canRetry
+ with(binding.buttonRetry) {
+ isVisible = item.canRetry
+ setText(item.buttonText)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt
index 562a6e6d3..e57f3e31f 100644
--- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt
@@ -16,7 +16,7 @@ class MangaListAdapter(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
clickListener: OnListItemClickListener,
- onRetryClick: () -> Unit
+ onRetryClick: (Throwable) -> Unit
) : AsyncListDifferDelegationAdapter(DiffCallback()) {
init {
diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ErrorState.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ErrorState.kt
index 868435096..43f566555 100644
--- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ErrorState.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ErrorState.kt
@@ -1,9 +1,11 @@
package org.koitharu.kotatsu.list.ui.model
import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
data class ErrorState(
val exception: Throwable,
@DrawableRes val icon: Int,
- val canRetry: Boolean
+ val canRetry: Boolean,
+ @StringRes val buttonText: Int
) : ListModel
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModelConversionExt.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModelConversionExt.kt
index 5832e798c..ab65b6f88 100644
--- a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModelConversionExt.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/ListModelConversionExt.kt
@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.list.ui.model
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.ListMode
import kotlin.math.roundToInt
@@ -46,7 +47,8 @@ fun > List.toUi(destination: C, mode: Li
fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(
exception = this,
icon = R.drawable.ic_error_large,
- canRetry = canRetry
+ canRetry = canRetry,
+ buttonText = (this as? ResolvableException)?.resolveTextId ?: R.string.try_again
)
fun Throwable.toErrorFooter() = ErrorFooter(
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt
index 9ff16dd08..4778febb5 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt
@@ -3,16 +3,18 @@ package org.koitharu.kotatsu.reader.ui.pager
import android.content.Context
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.reader.ui.PageLoader
abstract class BasePageHolder(
protected val binding: B,
loader: PageLoader,
- settings: AppSettings
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
) : RecyclerView.ViewHolder(binding.root), PageHolderDelegate.Callback {
- protected val delegate = PageHolderDelegate(loader, settings, this)
+ protected val delegate = PageHolderDelegate(loader, settings, this, exceptionResolver)
val context: Context
get() = itemView.context
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt
index a426718b7..f084b4ae6 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/BaseReaderAdapter.kt
@@ -4,6 +4,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.reader.ui.PageLoader
import org.koitharu.kotatsu.utils.ext.resetTransformations
@@ -12,7 +13,8 @@ import kotlin.coroutines.suspendCoroutine
abstract class BaseReaderAdapter>(
private val loader: PageLoader,
- private val settings: AppSettings
+ private val settings: AppSettings,
+ private val exceptionResolver: ExceptionResolver
) : RecyclerView.Adapter() {
private val differ = AsyncListDiffer(this, DiffCallback())
@@ -42,7 +44,7 @@ abstract class BaseReaderAdapter>(
final override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
- ): H = onCreateViewHolder(parent, loader, settings).also(this::onViewHolderCreated)
+ ): H = onCreateViewHolder(parent, loader, settings, exceptionResolver)
fun setItems(items: List, callback: Runnable) {
differ.submitList(items, callback)
@@ -54,12 +56,11 @@ abstract class BaseReaderAdapter>(
}
}
- protected open fun onViewHolderCreated(holder: H) = Unit
-
protected abstract fun onCreateViewHolder(
parent: ViewGroup,
loader: PageLoader,
- settings: AppSettings
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
): H
private class DiffCallback : DiffUtil.ItemCallback() {
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt
index 283318446..48a09b291 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt
@@ -4,6 +4,8 @@ import android.net.Uri
import androidx.core.net.toUri
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import kotlinx.coroutines.*
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
+import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.prefs.AppSettings
@@ -16,39 +18,52 @@ import java.io.IOException
class PageHolderDelegate(
private val loader: PageLoader,
private val settings: AppSettings,
- private val callback: Callback
+ private val callback: Callback,
+ private val exceptionResolver: ExceptionResolver
) : SubsamplingScaleImageView.DefaultOnImageEventListener(), CoroutineScope by loader {
private var state = State.EMPTY
private var job: Job? = null
private var file: File? = null
+ private var error: Throwable? = null
fun onBind(page: MangaPage) {
- doLoad(page, force = false)
+ job = launchInstead(job) {
+ doLoad(page, force = false)
+ }
}
fun retry(page: MangaPage) {
- doLoad(page, force = true)
+ job = launchInstead(job) {
+ (error as? ResolvableException)?.let {
+ exceptionResolver.resolve(it)
+ }
+ doLoad(page, force = true)
+ }
}
fun onRecycle() {
state = State.EMPTY
file = null
+ error = null
job?.cancel()
}
override fun onReady() {
state = State.SHOWING
+ error = null
callback.onImageShowing(settings.zoomMode)
}
override fun onImageLoaded() {
state = State.SHOWN
+ error = null
callback.onImageShown()
}
override fun onImageLoadError(e: Exception) {
val file = this.file
+ error = e
if (state == State.LOADED && e is IOException && file != null && file.exists()) {
job = launchAfter(job) {
state = State.CONVERTING
@@ -68,25 +83,25 @@ class PageHolderDelegate(
}
}
- private fun doLoad(data: MangaPage, force: Boolean) {
- job = launchInstead(job) {
- state = State.LOADING
- callback.onLoadingStarted()
- try {
- val file = withContext(Dispatchers.IO) {
- val pageUrl = data.source.repository.getPageFullUrl(data)
- check(pageUrl.isNotEmpty()) { "Cannot obtain full image url" }
- loader.loadFile(pageUrl, force)
- }
- this@PageHolderDelegate.file = file
- state = State.LOADED
- callback.onImageReady(file.toUri())
- } catch (e: CancellationException) {
- // do nothing
- } catch (e: Exception) {
- state = State.ERROR
- callback.onError(e)
+ private suspend fun doLoad(data: MangaPage, force: Boolean) {
+ state = State.LOADING
+ error = null
+ callback.onLoadingStarted()
+ try {
+ val file = withContext(Dispatchers.IO) {
+ val pageUrl = data.source.repository.getPageFullUrl(data)
+ check(pageUrl.isNotEmpty()) { "Cannot obtain full image url" }
+ loader.loadFile(pageUrl, force)
}
+ this@PageHolderDelegate.file = file
+ state = State.LOADED
+ callback.onImageReady(file.toUri())
+ } catch (e: CancellationException) {
+ // do nothing
+ } catch (e: Exception) {
+ state = State.ERROR
+ error = e
+ callback.onError(e)
}
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt
index 342df54b5..b98ab2239 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPageHolder.kt
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.reader.ui.pager.reversed
import android.graphics.PointF
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ItemPageBinding
@@ -11,8 +12,9 @@ import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
class ReversedPageHolder(
binding: ItemPageBinding,
loader: PageLoader,
- settings: AppSettings
-) : PageHolder(binding, loader, settings) {
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
+) : PageHolder(binding, loader, settings, exceptionResolver) {
override fun onImageShowing(zoom: ZoomMode) {
with(binding.ssiv) {
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt
index 2562b54e5..ecaea29bf 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedPagesAdapter.kt
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.reader.ui.pager.reversed
import android.view.LayoutInflater
import android.view.ViewGroup
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.ui.PageLoader
@@ -9,16 +10,19 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
class ReversedPagesAdapter(
loader: PageLoader,
- settings: AppSettings
-) : BaseReaderAdapter(loader, settings) {
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
+) : BaseReaderAdapter(loader, settings, exceptionResolver) {
override fun onCreateViewHolder(
parent: ViewGroup,
loader: PageLoader,
- settings: AppSettings
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
) = ReversedPageHolder(
binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false),
loader = loader,
- settings = settings
+ settings = settings,
+ exceptionResolver = exceptionResolver
)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt
index 26ee06174..2757d4ade 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt
@@ -25,10 +25,10 @@ class ReversedReaderFragment : BaseReader() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- pagerAdapter = ReversedPagesAdapter(loader, get())
+ pagerAdapter = ReversedPagesAdapter(loader, get(), exceptionResolver)
with(binding.pager) {
adapter = pagerAdapter
- offscreenPageLimit = 1
+ offscreenPageLimit = 2
doOnPageChanged(::notifyPageChanged)
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt
index e53fef51b..08683dbb4 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt
@@ -7,6 +7,8 @@ import androidx.core.view.isVisible
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
+import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ItemPageBinding
@@ -18,8 +20,9 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
open class PageHolder(
binding: ItemPageBinding,
loader: PageLoader,
- settings: AppSettings
-) : BasePageHolder(binding, loader, settings), View.OnClickListener {
+ settings: AppSettings, exceptionResolver: ExceptionResolver
+) : BasePageHolder(binding, loader, settings, exceptionResolver),
+ View.OnClickListener {
init {
binding.ssiv.setOnImageEventListener(delegate)
@@ -93,6 +96,9 @@ open class PageHolder(
override fun onError(e: Throwable) {
binding.textViewError.text = e.getDisplayMessage(context.resources)
+ binding.buttonRetry.setText(
+ (e as? ResolvableException)?.resolveTextId ?: R.string.try_again
+ )
binding.layoutError.isVisible = true
binding.progressBar.isVisible = false
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt
index 63e623180..d14620dce 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt
@@ -25,10 +25,10 @@ class PagerReaderFragment : BaseReader() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- pagesAdapter = PagesAdapter(loader, get())
+ pagesAdapter = PagesAdapter(loader, get(), exceptionResolver)
with(binding.pager) {
adapter = pagesAdapter
- offscreenPageLimit = 1
+ offscreenPageLimit = 2
doOnPageChanged(::notifyPageChanged)
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt
index b21f5226b..aa27be5b2 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PagesAdapter.kt
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.reader.ui.pager.standard
import android.view.LayoutInflater
import android.view.ViewGroup
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.ui.PageLoader
@@ -9,16 +10,19 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
class PagesAdapter(
loader: PageLoader,
- settings: AppSettings
-) : BaseReaderAdapter(loader, settings) {
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
+) : BaseReaderAdapter(loader, settings, exceptionResolver) {
override fun onCreateViewHolder(
parent: ViewGroup,
loader: PageLoader,
- settings: AppSettings
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
) = PageHolder(
binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false),
loader = loader,
- settings = settings
+ settings = settings,
+ exceptionResolver = exceptionResolver
)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonAdapter.kt
index 2d911e6a6..d05ce562e 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonAdapter.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonAdapter.kt
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.reader.ui.pager.wetoon
import android.view.LayoutInflater
import android.view.ViewGroup
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
import org.koitharu.kotatsu.reader.ui.PageLoader
@@ -9,13 +10,15 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
class WebtoonAdapter(
loader: PageLoader,
- settings: AppSettings
-) : BaseReaderAdapter(loader, settings) {
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
+) : BaseReaderAdapter(loader, settings, exceptionResolver) {
override fun onCreateViewHolder(
parent: ViewGroup,
loader: PageLoader,
- settings: AppSettings
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
) = WebtoonHolder(
binding = ItemPageWebtoonBinding.inflate(
LayoutInflater.from(parent.context),
@@ -23,6 +26,7 @@ class WebtoonAdapter(
false
),
loader = loader,
- settings = settings
+ settings = settings,
+ exceptionResolver = exceptionResolver
)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonHolder.kt
index 38521c312..b50f21ee8 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonHolder.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonHolder.kt
@@ -6,6 +6,8 @@ import androidx.core.view.isVisible
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
+import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
@@ -18,8 +20,10 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
class WebtoonHolder(
binding: ItemPageWebtoonBinding,
loader: PageLoader,
- settings: AppSettings
-) : BasePageHolder(binding, loader, settings), View.OnClickListener {
+ settings: AppSettings,
+ exceptionResolver: ExceptionResolver
+) : BasePageHolder(binding, loader, settings, exceptionResolver),
+ View.OnClickListener {
private var scrollToRestore = 0
@@ -74,6 +78,9 @@ class WebtoonHolder(
override fun onError(e: Throwable) {
binding.textViewError.text = e.getDisplayMessage(context.resources)
+ binding.buttonRetry.setText(
+ (e as? ResolvableException)?.resolveTextId ?: R.string.try_again
+ )
binding.layoutError.isVisible = true
binding.progressBar.isVisible = false
}
diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonReaderFragment.kt
index 663de33ee..76831d41d 100644
--- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonReaderFragment.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/wetoon/WebtoonReaderFragment.kt
@@ -26,7 +26,7 @@ class WebtoonReaderFragment : BaseReader() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- webtoonAdapter = WebtoonAdapter(loader, get())
+ webtoonAdapter = WebtoonAdapter(loader, get(), exceptionResolver)
with(binding.recyclerView) {
setHasFixedSize(true)
adapter = webtoonAdapter
diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt b/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt
index f1e4446fa..0b6b67e5e 100644
--- a/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/settings/AppUpdateChecker.kt
@@ -6,12 +6,14 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.activity.ComponentActivity
+import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-import org.koin.android.ext.android.inject
+import kotlinx.coroutines.withContext
+import org.koin.android.ext.android.get
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.github.AppVersion
@@ -32,8 +34,8 @@ import java.util.concurrent.TimeUnit
class AppUpdateChecker(private val activity: ComponentActivity) {
- private val settings by activity.inject()
- private val repo by activity.inject()
+ private val settings = activity.get()
+ private val repo = activity.get()
fun launchIfNeeded(): Job? {
return if (settings.appUpdateAuto && settings.appUpdate + PERIOD < System.currentTimeMillis()) {
@@ -52,21 +54,28 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
}
suspend fun checkNow() = runCatching {
- val version = repo.getLatestVersion()
- val newVersionId = VersionId.parse(version.name)
- val currentVersionId = VersionId.parse(BuildConfig.VERSION_NAME)
- val result = newVersionId > currentVersionId
- if (result) {
- showUpdateDialog(version)
+ withContext(Dispatchers.Default) {
+ val version = repo.getLatestVersion()
+ val newVersionId = VersionId.parse(version.name)
+ val currentVersionId = VersionId.parse(BuildConfig.VERSION_NAME)
+ val result = newVersionId > currentVersionId
+ if (result) {
+ withContext(Dispatchers.Main) {
+ showUpdateDialog(version)
+ }
+ }
+ settings.appUpdate = System.currentTimeMillis()
+ result
}
- settings.appUpdate = System.currentTimeMillis()
- result
+ }.onFailure {
+ it.printStackTrace()
}.getOrNull()
- private fun launchInternal() = activity.lifecycleScope.launch(Dispatchers.Main) {
+ private fun launchInternal() = activity.lifecycleScope.launch {
checkNow()
}
+ @MainThread
private fun showUpdateDialog(version: AppVersion) {
AlertDialog.Builder(activity)
.setTitle(R.string.app_update_available)
diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CommonExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CommonExt.kt
index 1e3688ebe..ea806d4a1 100644
--- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CommonExt.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CommonExt.kt
@@ -5,6 +5,7 @@ import android.util.Log
import kotlinx.coroutines.delay
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import org.koitharu.kotatsu.core.exceptions.WrongPasswordException
@@ -37,6 +38,7 @@ suspend inline fun T.retryUntilSuccess(maxAttempts: Int, action: T.() ->
}
fun Throwable.getDisplayMessage(resources: Resources) = when (this) {
+ is CloudFlareProtectedException -> resources.getString(R.string.captcha_required)
is UnsupportedOperationException -> resources.getString(R.string.operation_not_supported)
is UnsupportedFileException -> resources.getString(R.string.text_file_not_supported)
is FileNotFoundException -> resources.getString(R.string.file_not_found)
diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt
index 2ef2d1074..d9850010a 100644
--- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt
@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.utils.ext
+import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
@@ -16,27 +17,27 @@ import kotlin.coroutines.resumeWithException
suspend fun Call.await() = suspendCancellableCoroutine { cont ->
this.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
- if (!cont.isCancelled) {
+ if (cont.isActive) {
cont.resumeWithException(e)
}
}
override fun onResponse(call: Call, response: Response) {
- cont.resume(response)
+ if (cont.isActive) {
+ cont.resume(response)
+ }
}
})
cont.invokeOnCancellation {
- safe {
- this.cancel()
- }
+ this.cancel()
}
}
-fun CoroutineScope.launchAfter(
+inline fun CoroutineScope.launchAfter(
job: Job?,
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
- block: suspend CoroutineScope.() -> Unit
+ crossinline block: suspend CoroutineScope.() -> Unit
): Job = launch(context, start) {
try {
job?.join()
@@ -48,11 +49,11 @@ fun CoroutineScope.launchAfter(
block()
}
-fun CoroutineScope.launchInstead(
+inline fun CoroutineScope.launchInstead(
job: Job?,
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
- block: suspend CoroutineScope.() -> Unit
+ crossinline block: suspend CoroutineScope.() -> Unit
): Job = launch(context, start) {
try {
job?.cancelAndJoin()
@@ -71,5 +72,5 @@ val IgnoreErrors
}
}
-val processLifecycleScope: CoroutineScope
+val processLifecycleScope: LifecycleCoroutineScope
inline get() = ProcessLifecycleOwner.get().lifecycleScope
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/progress/Progress.kt b/app/src/main/java/org/koitharu/kotatsu/utils/progress/Progress.kt
index f6a181f35..7dff7fbf5 100644
--- a/app/src/main/java/org/koitharu/kotatsu/utils/progress/Progress.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/utils/progress/Progress.kt
@@ -10,13 +10,15 @@ data class Progress(
) : Parcelable, Comparable