diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 7fe2dfd02..3be2f59ae 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -13,3 +13,4 @@ -dontwarn okhttp3.internal.platform.ConscryptPlatform -keep class org.koitharu.kotatsu.core.exceptions.* { *; } +-keep class org.koitharu.kotatsu.settings.NotificationSettingsLegacyFragment 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 cc9e5fb33..dc18898b3 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 @@ -55,6 +55,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud override fun onDestroyView() { binding.webView.stopLoading() + binding.webView.destroy() super.onDestroyView() } @@ -83,7 +84,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud override fun onCheckPassed() { pendingResult.putBoolean(EXTRA_RESULT, true) - dismiss() + dismissAllowingStateLoss() } companion object { diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt index 3436ca259..eefc9ba6d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsFragment.kt @@ -2,6 +2,8 @@ package org.koitharu.kotatsu.settings import android.os.Bundle import android.view.View +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope import androidx.preference.Preference import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint @@ -53,7 +55,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { super.onViewCreated(view, savedInstanceState) findPreference(KEY_AUTH)?.run { if (isVisible) { - loadUsername(this) + loadUsername(viewLifecycleOwner, this) } } } @@ -68,7 +70,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { } } - private fun loadUsername(preference: Preference) = viewLifecycleScope.launch { + private fun loadUsername(owner: LifecycleOwner, preference: Preference) = owner.lifecycleScope.launch { runCatching { preference.summary = null withContext(Dispatchers.Default) { @@ -99,7 +101,8 @@ class SourceSettingsFragment : BasePreferenceFragment(0) { viewLifecycleScope.launch { if (exceptionResolver.resolve(error)) { val pref = findPreference(KEY_AUTH) ?: return@launch - loadUsername(pref) + val lifecycleOwner = awaitViewLifecycle() + loadUsername(lifecycleOwner, pref) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt index 6d7e49a77..0950a69a4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt @@ -10,6 +10,7 @@ import coil.request.ImageResult import coil.request.SuccessResult import coil.util.CoilUtils import com.google.android.material.progressindicator.BaseProgressIndicator +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.utils.progress.ImageRequestIndicatorListener @@ -47,7 +48,18 @@ fun ImageResult.toBitmapOrNull() = when (this) { } fun ImageRequest.Builder.referer(referer: String): ImageRequest.Builder { - return setHeader(CommonHeaders.REFERER, referer) + if (referer.isEmpty()) { + return this + } + try { + setHeader(CommonHeaders.REFERER, referer) + } catch (e: IllegalArgumentException) { + val baseUrl = referer.baseUrl() + if (baseUrl != null) { + setHeader(CommonHeaders.REFERER, baseUrl) + } + } + return this } fun ImageRequest.Builder.indicator(indicator: BaseProgressIndicator<*>): ImageRequest.Builder { @@ -63,3 +75,11 @@ fun ImageRequest.Builder.crossfade(context: Context?): ImageRequest.Builder { val duration = context.resources.getInteger(R.integer.config_defaultAnimTime) * context.animatorDurationScale return crossfade(duration.toInt()) } + +private fun String.baseUrl(): String? { + return (this.toHttpUrlOrNull()?.newBuilder("/") ?: return null) + .username("") + .password("") + .build() + .toString() +} diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/FragmentExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/FragmentExt.kt index 41336e509..5ea1fb4af 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/FragmentExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/FragmentExt.kt @@ -6,8 +6,12 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.Observer import androidx.lifecycle.coroutineScope import java.io.Serializable +import kotlin.coroutines.resume +import kotlinx.coroutines.suspendCancellableCoroutine inline fun T.withArgs(size: Int, block: Bundle.() -> Unit): T { val b = Bundle(size) @@ -22,7 +26,7 @@ val Fragment.viewLifecycleScope fun Fragment.serializableArgument(name: String): Lazy { return lazy(LazyThreadSafetyMode.NONE) { @Suppress("UNCHECKED_CAST") - requireNotNull(arguments?.getSerializable(name)) { + requireNotNull(arguments?.getSerializableCompat(name)) { "No argument $name passed into ${javaClass.simpleName}" } as T } @@ -41,3 +45,19 @@ fun DialogFragment.showAllowStateLoss(manager: FragmentManager, tag: String?) { fun Fragment.addMenuProvider(provider: MenuProvider) { requireActivity().addMenuProvider(provider, viewLifecycleOwner, Lifecycle.State.STARTED) } + +suspend fun Fragment.awaitViewLifecycle(): LifecycleOwner = suspendCancellableCoroutine { cont -> + val liveData = viewLifecycleOwnerLiveData + val observer = object : Observer { + override fun onChanged(result: LifecycleOwner?) { + if (result != null) { + liveData.removeObserver(this) + cont.resume(result) + } + } + } + liveData.observeForever(observer) + cont.invokeOnCancellation { + liveData.removeObserver(observer) + } +}