Avoid memory leak in ExceptionResolver
This commit is contained in:
@@ -8,9 +8,10 @@ import androidx.collection.MutableScatterMap
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import dagger.assisted.Assisted
|
import androidx.lifecycle.Lifecycle
|
||||||
import dagger.assisted.AssistedFactory
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import dagger.assisted.AssistedInject
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.async
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.browser.BrowserActivity
|
import org.koitharu.kotatsu.browser.BrowserActivity
|
||||||
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity
|
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity
|
||||||
@@ -32,14 +33,15 @@ import org.koitharu.kotatsu.scrobbling.common.domain.ScrobblerAuthRequiredExcept
|
|||||||
import org.koitharu.kotatsu.scrobbling.common.ui.ScrobblerAuthHelper
|
import org.koitharu.kotatsu.scrobbling.common.ui.ScrobblerAuthHelper
|
||||||
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
|
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
import javax.net.ssl.SSLException
|
import javax.net.ssl.SSLException
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
class ExceptionResolver @AssistedInject constructor(
|
class ExceptionResolver private constructor(
|
||||||
@Assisted private val host: Host,
|
private val host: Host,
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
private val scrobblerAuthHelperProvider: Provider<ScrobblerAuthHelper>,
|
private val scrobblerAuthHelperProvider: Provider<ScrobblerAuthHelper>,
|
||||||
) {
|
) {
|
||||||
@@ -56,10 +58,11 @@ class ExceptionResolver @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showErrorDetails(e: Throwable, url: String? = null) {
|
fun showErrorDetails(e: Throwable, url: String? = null) {
|
||||||
host.router()?.showErrorDialog(e, url)
|
host.router.showErrorDialog(e, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun resolve(e: Throwable): Boolean = when (e) {
|
suspend fun resolve(e: Throwable): Boolean = host.lifecycleScope.async {
|
||||||
|
when (e) {
|
||||||
is CloudFlareProtectedException -> resolveCF(e)
|
is CloudFlareProtectedException -> resolveCF(e)
|
||||||
is AuthRequiredException -> resolveAuthException(e.source)
|
is AuthRequiredException -> resolveAuthException(e.source)
|
||||||
is SSLException,
|
is SSLException,
|
||||||
@@ -71,7 +74,7 @@ class ExceptionResolver @AssistedInject constructor(
|
|||||||
is InteractiveActionRequiredException -> resolveBrowserAction(e)
|
is InteractiveActionRequiredException -> resolveBrowserAction(e)
|
||||||
|
|
||||||
is ProxyConfigException -> {
|
is ProxyConfigException -> {
|
||||||
host.router()?.openProxySettings()
|
host.router.openProxySettings()
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +102,7 @@ class ExceptionResolver @AssistedInject constructor(
|
|||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
}.await()
|
||||||
|
|
||||||
private suspend fun resolveBrowserAction(
|
private suspend fun resolveBrowserAction(
|
||||||
e: InteractiveActionRequiredException
|
e: InteractiveActionRequiredException
|
||||||
@@ -118,11 +122,11 @@ class ExceptionResolver @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openInBrowser(url: String) {
|
private fun openInBrowser(url: String) {
|
||||||
host.router()?.openBrowser(url, null, null)
|
host.router.openBrowser(url, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openAlternatives(manga: Manga) {
|
private fun openAlternatives(manga: Manga) {
|
||||||
host.router()?.openAlternatives(manga)
|
host.router.openAlternatives(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleActivityResult(tag: String, result: Boolean) {
|
private fun handleActivityResult(tag: String, result: Boolean) {
|
||||||
@@ -130,7 +134,7 @@ class ExceptionResolver @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showSslErrorDialog() {
|
private fun showSslErrorDialog() {
|
||||||
val ctx = host.getContext() ?: return
|
val ctx = host.context ?: return
|
||||||
if (settings.isSSLBypassEnabled) {
|
if (settings.isSSLBypassEnabled) {
|
||||||
Toast.makeText(ctx, R.string.operation_not_supported, Toast.LENGTH_SHORT).show()
|
Toast.makeText(ctx, R.string.operation_not_supported, Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
@@ -147,27 +151,65 @@ class ExceptionResolver @AssistedInject constructor(
|
|||||||
}.show()
|
}.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun Host.withContext(block: Context.() -> Unit) {
|
class Factory @Inject constructor(
|
||||||
getContext()?.apply(block)
|
private val settings: AppSettings,
|
||||||
|
private val scrobblerAuthHelperProvider: Provider<ScrobblerAuthHelper>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun create(fragment: Fragment) = ExceptionResolver(
|
||||||
|
host = Host.FragmentHost(fragment),
|
||||||
|
settings = settings,
|
||||||
|
scrobblerAuthHelperProvider = scrobblerAuthHelperProvider,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun create(activity: FragmentActivity) = ExceptionResolver(
|
||||||
|
host = Host.ActivityHost(activity),
|
||||||
|
settings = settings,
|
||||||
|
scrobblerAuthHelperProvider = scrobblerAuthHelperProvider,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Host.router(): AppRouter? = when (this) {
|
private sealed interface Host : ActivityResultCaller, LifecycleOwner {
|
||||||
is FragmentActivity -> router
|
|
||||||
is Fragment -> router
|
val context: Context?
|
||||||
else -> null
|
|
||||||
|
val router: AppRouter
|
||||||
|
|
||||||
|
val fragmentManager: FragmentManager
|
||||||
|
|
||||||
|
inline fun withContext(block: Context.() -> Unit) {
|
||||||
|
context?.apply(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Host : ActivityResultCaller {
|
class ActivityHost(val activity: FragmentActivity) : Host,
|
||||||
|
ActivityResultCaller by activity,
|
||||||
|
LifecycleOwner by activity {
|
||||||
|
|
||||||
fun getChildFragmentManager(): FragmentManager
|
override val context: Context
|
||||||
|
get() = activity
|
||||||
|
|
||||||
fun getContext(): Context?
|
override val router: AppRouter
|
||||||
|
get() = activity.router
|
||||||
|
|
||||||
|
override val fragmentManager: FragmentManager
|
||||||
|
get() = activity.supportFragmentManager
|
||||||
}
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
class FragmentHost(val fragment: Fragment) : Host,
|
||||||
interface Factory {
|
ActivityResultCaller by fragment {
|
||||||
|
|
||||||
fun create(host: Host): ExceptionResolver
|
override val context: Context?
|
||||||
|
get() = fragment.context
|
||||||
|
|
||||||
|
override val router: AppRouter
|
||||||
|
get() = fragment.router
|
||||||
|
|
||||||
|
override val fragmentManager: FragmentManager
|
||||||
|
get() = fragment.childFragmentManager
|
||||||
|
|
||||||
|
override val lifecycle: Lifecycle
|
||||||
|
get() = fragment.viewLifecycleOwner.lifecycle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import androidx.appcompat.R as appcompatR
|
|||||||
|
|
||||||
abstract class BaseActivity<B : ViewBinding> :
|
abstract class BaseActivity<B : ViewBinding> :
|
||||||
AppCompatActivity(),
|
AppCompatActivity(),
|
||||||
ExceptionResolver.Host,
|
|
||||||
OnApplyWindowInsetsListener,
|
OnApplyWindowInsetsListener,
|
||||||
ScreenshotPolicyHelper.ContentContainer {
|
ScreenshotPolicyHelper.ContentContainer {
|
||||||
|
|
||||||
@@ -87,10 +86,6 @@ abstract class BaseActivity<B : ViewBinding> :
|
|||||||
@Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR)
|
@Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR)
|
||||||
override fun setContentView(view: View?) = throw UnsupportedOperationException()
|
override fun setContentView(view: View?) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override fun getContext() = this
|
|
||||||
|
|
||||||
override fun getChildFragmentManager(): FragmentManager = supportFragmentManager
|
|
||||||
|
|
||||||
protected fun setContentView(binding: B) {
|
protected fun setContentView(binding: B) {
|
||||||
this.viewBinding = binding
|
this.viewBinding = binding
|
||||||
super.setContentView(binding.root)
|
super.setContentView(binding.root)
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import org.koitharu.kotatsu.core.ui.util.ActionModeDelegate
|
|||||||
|
|
||||||
abstract class BaseFragment<B : ViewBinding> :
|
abstract class BaseFragment<B : ViewBinding> :
|
||||||
OnApplyWindowInsetsListener,
|
OnApplyWindowInsetsListener,
|
||||||
Fragment(),
|
Fragment() {
|
||||||
ExceptionResolver.Host {
|
|
||||||
|
|
||||||
var viewBinding: B? = null
|
var viewBinding: B? = null
|
||||||
private set
|
private set
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ import com.google.android.material.R as materialR
|
|||||||
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
||||||
PreferenceFragmentCompat(),
|
PreferenceFragmentCompat(),
|
||||||
OnApplyWindowInsetsListener,
|
OnApplyWindowInsetsListener,
|
||||||
RecyclerViewOwner,
|
RecyclerViewOwner {
|
||||||
ExceptionResolver.Host {
|
|
||||||
|
|
||||||
protected lateinit var exceptionResolver: ExceptionResolver
|
protected lateinit var exceptionResolver: ExceptionResolver
|
||||||
private set
|
private set
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ import org.koitharu.kotatsu.core.ui.util.ActionModeDelegate
|
|||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment(),
|
abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment(),
|
||||||
OnApplyWindowInsetsListener,
|
OnApplyWindowInsetsListener {
|
||||||
ExceptionResolver.Host {
|
|
||||||
|
|
||||||
private var waitingForDismissAllowingStateLoss = false
|
private var waitingForDismissAllowingStateLoss = false
|
||||||
private var isFitToContentsDisabled = false
|
private var isFitToContentsDisabled = false
|
||||||
|
|||||||
Reference in New Issue
Block a user