From 7fd71c13f3b159dd63a82c38fd011e2c8b9d76ac Mon Sep 17 00:00:00 2001 From: Koitharu Date: Tue, 22 Dec 2020 07:48:34 +0200 Subject: [PATCH] Fixes --- app/build.gradle | 30 +++++----- app/src/debug/res/values/strings.xml | 3 + .../kotatsu/base/ui/AlertDialogFragment.kt | 2 + .../koitharu/kotatsu/base/ui/BaseActivity.kt | 6 ++ .../koitharu/kotatsu/base/ui/BaseFragment.kt | 5 ++ .../browser/cloudflare/CloudFlareClient.kt | 9 ++- .../browser/cloudflare/CloudFlareDialog.kt | 17 ++++-- .../CloudFlareProtectedException.kt | 9 ++- .../exceptions/resolve/ExceptionResolver.kt | 38 +++++++++++++ .../exceptions/resolve/ResolvableException.kt | 6 ++ .../koitharu/kotatsu/core/ui/ChipsFactory.kt | 1 + .../kotatsu/list/ui/MangaListFragment.kt | 19 ++++++- .../kotatsu/list/ui/adapter/ErrorFooterAD.kt | 4 +- .../list/ui/adapter/ErrorStateListAD.kt | 9 ++- .../list/ui/adapter/MangaListAdapter.kt | 2 +- .../kotatsu/list/ui/model/ErrorState.kt | 4 +- .../list/ui/model/ListModelConversionExt.kt | 4 +- .../kotatsu/reader/ui/pager/BasePageHolder.kt | 6 +- .../reader/ui/pager/BaseReaderAdapter.kt | 11 ++-- .../reader/ui/pager/PageHolderDelegate.kt | 57 ++++++++++++------- .../ui/pager/reversed/ReversedPageHolder.kt | 6 +- .../ui/pager/reversed/ReversedPagesAdapter.kt | 12 ++-- .../pager/reversed/ReversedReaderFragment.kt | 4 +- .../reader/ui/pager/standard/PageHolder.kt | 10 +++- .../ui/pager/standard/PagerReaderFragment.kt | 4 +- .../reader/ui/pager/standard/PagesAdapter.kt | 12 ++-- .../reader/ui/pager/wetoon/WebtoonAdapter.kt | 12 ++-- .../reader/ui/pager/wetoon/WebtoonHolder.kt | 11 +++- .../ui/pager/wetoon/WebtoonReaderFragment.kt | 2 +- .../kotatsu/settings/AppUpdateChecker.kt | 33 +++++++---- .../koitharu/kotatsu/utils/ext/CommonExt.kt | 2 + .../kotatsu/utils/ext/CoroutineExt.kt | 21 +++---- .../kotatsu/utils/progress/Progress.kt | 8 ++- app/src/main/res/layout/fragment_details.xml | 2 + app/src/main/res/values-ru/strings.xml | 4 +- app/src/main/res/values/strings.xml | 2 + 36 files changed, 274 insertions(+), 113 deletions(-) create mode 100644 app/src/debug/res/values/strings.xml create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ResolvableException.kt 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 { override fun compareTo(other: Progress): Int { - if (this.total == other.total) { - return this.value.compareTo(other.value) + return if (this.total == other.total) { + this.value.compareTo(other.value) } else { - TODO() + this.part().compareTo(other.part()) } } val isIndeterminate: Boolean get() = total <= 0 + + private fun part() = if (isIndeterminate) -1.0 else value / total.toDouble() } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index ce03c8da6..9f8be4962 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -119,6 +119,8 @@ android:layout_height="wrap_content" android:layout_marginTop="4dp" android:padding="6dp" + app:chipSpacingHorizontal="4dp" + app:chipSpacingVertical="6dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/divider_top" /> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2c440bc02..fe09c4386 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -5,7 +5,7 @@ Избранное История Произошла ошибка - Ошибка подключения + Ошибка сети Подробности Главы Список @@ -188,4 +188,6 @@ Попробовать ещё раз Выбранный режим будет сохранён для текущей манги Без звука + Необходимо пройти CAPTCHA + Resolve \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0fd1c86c4..94444f5a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -190,4 +190,6 @@ Tap to try again Chosen configuration will be remembered for this manga Silent + CAPTCHA is required + Resolve \ No newline at end of file