Address more coroutine scope leaks

(cherry picked from commit 6fb7a85e8a77aa379a8138e1f2fe1b5f65b45ed7)

# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt
This commit is contained in:
arkon 2021-01-07 18:15:57 -05:00 committed by Jobobby04
parent fd3e0bc449
commit 7330be555d
29 changed files with 116 additions and 120 deletions

View File

@ -281,7 +281,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
// For detecting memory leaks; see https://square.github.io/leakcanary/ // For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation("com.squareup.leakcanary:leakcanary-android:2.4") // debugImplementation("com.squareup.leakcanary:leakcanary-android:2.6")
// SY --> // SY -->
// [EXH] Android 7 SSL Workaround // [EXH] Android 7 SSL Workaround

View File

@ -9,9 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
@ -27,7 +25,7 @@ class DownloadProvider(private val context: Context) {
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = MainScope()
/** /**
* The root directory for downloads. * The root directory for downloads.

View File

@ -1,21 +1,32 @@
package eu.kanade.tachiyomi.ui.base.presenter package eu.kanade.tachiyomi.ui.base.presenter
import android.os.Bundle import android.os.Bundle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import nucleus.presenter.RxPresenter import nucleus.presenter.RxPresenter
import nucleus.presenter.delivery.Delivery import nucleus.presenter.delivery.Delivery
import rx.Observable import rx.Observable
open class BasePresenter<V> : RxPresenter<V>() { open class BasePresenter<V> : RxPresenter<V>() {
lateinit var presenterScope: CoroutineScope
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
try { try {
super.onCreate(savedState) super.onCreate(savedState)
presenterScope = MainScope()
} catch (e: NullPointerException) { } catch (e: NullPointerException) {
// Swallow this error. This should be fixed in the library but since it's not critical // Swallow this error. This should be fixed in the library but since it's not critical
// (only used by restartables) it should be enough. It saves me a fork. // (only used by restartables) it should be enough. It saves me a fork.
} }
} }
override fun onDestroy() {
super.onDestroy()
presenterScope.cancel()
}
/** /**
* Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle * Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle
* subscription list. * subscription list.

View File

@ -9,9 +9,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ExtensionDetailHeaderBinding import eu.kanade.tachiyomi.databinding.ExtensionDetailHeaderBinding
import eu.kanade.tachiyomi.ui.browse.extension.getApplicationIcon import eu.kanade.tachiyomi.ui.browse.extension.getApplicationIcon
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
@ -19,7 +16,6 @@ import reactivecircus.flowbinding.android.view.clicks
class ExtensionDetailsHeaderAdapter(private val presenter: ExtensionDetailsPresenter) : class ExtensionDetailsHeaderAdapter(private val presenter: ExtensionDetailsPresenter) :
RecyclerView.Adapter<ExtensionDetailsHeaderAdapter.HeaderViewHolder>() { RecyclerView.Adapter<ExtensionDetailsHeaderAdapter.HeaderViewHolder>() {
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: ExtensionDetailHeaderBinding private lateinit var binding: ExtensionDetailHeaderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
@ -47,7 +43,7 @@ class ExtensionDetailsHeaderAdapter(private val presenter: ExtensionDetailsPrese
binding.extensionUninstallButton.clicks() binding.extensionUninstallButton.clicks()
.onEach { presenter.uninstallExtension() } .onEach { presenter.uninstallExtension() }
.launchIn(scope) .launchIn(presenter.presenterScope)
if (extension.isObsolete) { if (extension.isObsolete) {
binding.extensionWarningBanner.isVisible = true binding.extensionWarningBanner.isVisible = true

View File

@ -191,7 +191,7 @@ class SourceFilterController : SettingsController() {
this.query = it.toString() this.query = it.toString()
drawSources() drawSources()
} }
.launchIn(scope) .launchIn(viewScope)
// Fixes problem with the overflow icon showing up in lieu of search // Fixes problem with the overflow icon showing up in lieu of search
searchItem.setOnActionExpandListener( searchItem.setOnActionExpandListener(

View File

@ -6,9 +6,6 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
@ -36,8 +33,6 @@ class SourcePresenter(
// SY <-- // SY <--
) : BasePresenter<SourceController>() { ) : BasePresenter<SourceController>() {
private val scope = CoroutineScope(Job() + Dispatchers.Main)
var sources = getEnabledSources() var sources = getEnabledSources()
/** /**
@ -153,7 +148,7 @@ class SourcePresenter(
.onStart { delay(500) } .onStart { delay(500) }
.distinctUntilChanged() .distinctUntilChanged()
.onEach { updateLastUsedSource(it) } .onEach { updateLastUsedSource(it) }
.launchIn(scope) .launchIn(presenterScope)
} }
private fun updateLastUsedSource(sourceId: Long) { private fun updateLastUsedSource(sourceId: Long) {

View File

@ -10,16 +10,14 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges import reactivecircus.flowbinding.android.widget.textChanges
open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem<TextItem.Holder>() { open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem<TextItem.Holder>() {
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = MainScope()
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.navigation_view_text return R.layout.navigation_view_text

View File

@ -25,7 +25,7 @@ import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import exh.ui.LoadingHandle import exh.ui.LoadingHandle
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -52,7 +52,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
CategoryAdapter.OnItemReleaseListener { CategoryAdapter.OnItemReleaseListener {
// SY <-- // SY <--
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = MainScope()
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()

View File

@ -9,9 +9,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.MangaChaptersHeaderBinding import eu.kanade.tachiyomi.databinding.MangaChaptersHeaderBinding
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -25,7 +22,6 @@ class MangaChaptersHeaderAdapter(
private var numChapters: Int? = null private var numChapters: Int? = null
private var hasActiveFilters: Boolean = false private var hasActiveFilters: Boolean = false
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaChaptersHeaderBinding private lateinit var binding: MangaChaptersHeaderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
@ -68,7 +64,7 @@ class MangaChaptersHeaderAdapter(
merge(view.clicks(), binding.btnChaptersFilter.clicks()) merge(view.clicks(), binding.btnChaptersFilter.clicks())
.onEach { controller.showSettingsSheet() } .onEach { controller.showSettingsSheet() }
.launchIn(scope) .launchIn(controller.viewScope)
} }
} }
} }

View File

@ -8,9 +8,6 @@ import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MangaInfoButtonsBinding import eu.kanade.tachiyomi.databinding.MangaInfoButtonsBinding
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
@ -24,7 +21,6 @@ class MangaInfoButtonsAdapter(
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaInfoButtonsBinding private lateinit var binding: MangaInfoButtonsBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
@ -45,7 +41,7 @@ class MangaInfoButtonsAdapter(
binding.recommendBtn.isVisible = !preferences.recommendsInOverflow().get() binding.recommendBtn.isVisible = !preferences.recommendsInOverflow().get()
binding.recommendBtn.clicks() binding.recommendBtn.clicks()
.onEach { controller.openRecommends() } .onEach { controller.openRecommends() }
.launchIn(scope) .launchIn(controller.viewScope)
} else { } else {
if (controller.smartSearchConfig.origMangaId != null) { if (controller.smartSearchConfig.origMangaId != null) {
binding.mergeBtn.isVisible = true binding.mergeBtn.isVisible = true
@ -55,7 +51,7 @@ class MangaInfoButtonsAdapter(
controller.mergeWithAnother() controller.mergeWithAnother()
} }
.launchIn(scope) .launchIn(controller.viewScope)
} }
// EXH <-- // EXH <--
} }

View File

@ -27,9 +27,6 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
@ -54,7 +51,6 @@ class MangaInfoHeaderAdapter(
private var source: Source = controller.presenter.source private var source: Source = controller.presenter.source
private var trackCount: Int = 0 private var trackCount: Int = 0
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaInfoHeaderBinding private lateinit var binding: MangaInfoHeaderBinding
private var currentMangaThumbnail: MangaThumbnail? = null private var currentMangaThumbnail: MangaThumbnail? = null
@ -96,17 +92,17 @@ class MangaInfoHeaderAdapter(
// SY --> // SY -->
binding.mangaCover.clicks() binding.mangaCover.clicks()
.onEach { controller.onThumbnailClick(binding.mangaCover) } .onEach { controller.onThumbnailClick(binding.mangaCover) }
.launchIn(scope) .launchIn(controller.viewScope)
// SY <-- // SY <--
binding.btnFavorite.clicks() binding.btnFavorite.clicks()
.onEach { controller.onFavoriteClick() } .onEach { controller.onFavoriteClick() }
.launchIn(scope) .launchIn(controller.viewScope)
if (controller.presenter.manga.favorite && controller.presenter.getCategories().isNotEmpty()) { if (controller.presenter.manga.favorite && controller.presenter.getCategories().isNotEmpty()) {
binding.btnFavorite.longClicks() binding.btnFavorite.longClicks()
.onEach { controller.onCategoriesClick() } .onEach { controller.onCategoriesClick() }
.launchIn(scope) .launchIn(controller.viewScope)
} }
with(binding.btnTracking) { with(binding.btnTracking) {
@ -132,7 +128,7 @@ class MangaInfoHeaderAdapter(
clicks() clicks()
.onEach { controller.onTrackingClick() } .onEach { controller.onTrackingClick() }
.launchIn(scope) .launchIn(controller.viewScope)
} else { } else {
isVisible = false isVisible = false
} }
@ -142,14 +138,14 @@ class MangaInfoHeaderAdapter(
binding.btnWebview.isVisible = true binding.btnWebview.isVisible = true
binding.btnWebview.clicks() binding.btnWebview.clicks()
.onEach { controller.openMangaInWebView() } .onEach { controller.openMangaInWebView() }
.launchIn(scope) .launchIn(controller.viewScope)
} }
// SY --> // SY -->
binding.btnMerge.isVisible = controller.presenter.manga.favorite binding.btnMerge.isVisible = controller.presenter.manga.favorite
binding.btnMerge.clicks() binding.btnMerge.clicks()
.onEach { controller.openSmartSearch() } .onEach { controller.openSmartSearch() }
.launchIn(scope) .launchIn(controller.viewScope)
// SY <-- // SY <--
binding.mangaFullTitle.longClicks() binding.mangaFullTitle.longClicks()
@ -159,13 +155,13 @@ class MangaInfoHeaderAdapter(
binding.mangaFullTitle.text.toString() binding.mangaFullTitle.text.toString()
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaFullTitle.clicks() binding.mangaFullTitle.clicks()
.onEach { .onEach {
controller.performGlobalSearch(binding.mangaFullTitle.text.toString()) controller.performGlobalSearch(binding.mangaFullTitle.text.toString())
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaAuthor.longClicks() binding.mangaAuthor.longClicks()
.onEach { .onEach {
@ -177,7 +173,7 @@ class MangaInfoHeaderAdapter(
) )
// SY <-- // SY <--
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaAuthor.clicks() binding.mangaAuthor.clicks()
.onEach { .onEach {
@ -186,7 +182,7 @@ class MangaInfoHeaderAdapter(
controller.performGlobalSearch(SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = author) ?: author) controller.performGlobalSearch(SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = author) ?: author)
// SY <-- // SY <--
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaArtist.longClicks() binding.mangaArtist.longClicks()
.onEach { .onEach {
@ -198,7 +194,7 @@ class MangaInfoHeaderAdapter(
) )
// SY <-- // SY <--
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaArtist.clicks() binding.mangaArtist.clicks()
.onEach { .onEach {
@ -207,7 +203,7 @@ class MangaInfoHeaderAdapter(
controller.performGlobalSearch(SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist) controller.performGlobalSearch(SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist)
// SY <-- // SY <--
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaCover.longClicks() binding.mangaCover.longClicks()
.onEach { .onEach {
@ -216,7 +212,7 @@ class MangaInfoHeaderAdapter(
controller.presenter.manga.title controller.presenter.manga.title
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
setMangaInfo(manga, source) setMangaInfo(manga, source)
} }

View File

@ -27,9 +27,6 @@ import exh.source.getMainSource
import exh.util.getRaisedTags import exh.util.getRaisedTags
import exh.util.makeSearchChip import exh.util.makeSearchChip
import exh.util.setChipsExtended import exh.util.setChipsExtended
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -47,7 +44,6 @@ class MangaInfoItemAdapter(
private var source: Source = controller.presenter.source private var source: Source = controller.presenter.source
private var meta: RaisedSearchMetadata? = controller.presenter.meta private var meta: RaisedSearchMetadata? = controller.presenter.meta
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaInfoItemBinding private lateinit var binding: MangaInfoItemBinding
private var initialLoad: Boolean = true private var initialLoad: Boolean = true
@ -88,14 +84,14 @@ class MangaInfoItemAdapter(
binding.mangaSummaryText.text.toString() binding.mangaSummaryText.text.toString()
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.genreGroups.layoutManager = LinearLayoutManager(itemView.context) binding.genreGroups.layoutManager = LinearLayoutManager(itemView.context)
binding.genreGroups.adapter = mangaTagsInfoAdapter binding.genreGroups.adapter = mangaTagsInfoAdapter
// SY --> // SY -->
mangaTagsInfoAdapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, _ -> mangaTagsInfoAdapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, _ ->
scope.launch { controller.viewScope.launch {
toggleMangaInfo() toggleMangaInfo()
} }
false false
@ -174,7 +170,7 @@ class MangaInfoItemAdapter(
binding.mangaInfoToggleLess.clicks() binding.mangaInfoToggleLess.clicks()
) )
.onEach { toggleMangaInfo() } .onEach { toggleMangaInfo() }
.launchIn(scope) .launchIn(controller.viewScope)
// Expand manga info if navigated from source listing // Expand manga info if navigated from source listing
if (initialLoad && fromSource) { if (initialLoad && fromSource) {

View File

@ -4,8 +4,6 @@ import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -13,9 +11,7 @@ import kotlinx.coroutines.flow.onEach
/** /**
* Common configuration for all viewers. * Common configuration for all viewers.
*/ */
abstract class ViewerConfig(preferences: PreferencesHelper) { abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: CoroutineScope) {
private val scope = CoroutineScope(Job() + Dispatchers.Main)
var imagePropertyChangedListener: (() -> Unit)? = null var imagePropertyChangedListener: (() -> Unit)? = null

View File

@ -6,14 +6,18 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
import kotlinx.coroutines.CoroutineScope
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
/** /**
* Configuration used by pager viewers. * Configuration used by pager viewers.
*/ */
class PagerConfig(private val viewer: PagerViewer, preferences: PreferencesHelper = Injekt.get()) : class PagerConfig(
ViewerConfig(preferences) { private val viewer: PagerViewer,
scope: CoroutineScope,
preferences: PreferencesHelper = Injekt.get()
) : ViewerConfig(preferences, scope) {
var imageScaleType = 1 var imageScaleType = 1
private set private set

View File

@ -16,6 +16,8 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import timber.log.Timber import timber.log.Timber
import kotlin.math.min import kotlin.math.min
@ -25,6 +27,8 @@ import kotlin.math.min
@Suppress("LeakingThis") @Suppress("LeakingThis")
abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer { abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
private val scope = MainScope()
/** /**
* View pager used by this viewer. It's abstract to implement L2R, R2L and vertical pagers on * View pager used by this viewer. It's abstract to implement L2R, R2L and vertical pagers on
* top of this class. * top of this class.
@ -34,7 +38,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
/** /**
* Configuration used by the pager, like allow taps, scale mode on images, page transitions... * Configuration used by the pager, like allow taps, scale mode on images, page transitions...
*/ */
val config = PagerConfig(this) val config = PagerConfig(this, scope)
/** /**
* Adapter of the pager. * Adapter of the pager.
@ -114,6 +118,11 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
} }
} }
override fun destroy() {
super.destroy()
scope.cancel()
}
/** /**
* Creates a new ViewPager. * Creates a new ViewPager.
*/ */

View File

@ -6,13 +6,17 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
import kotlinx.coroutines.CoroutineScope
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
/** /**
* Configuration used by webtoon viewers. * Configuration used by webtoon viewers.
*/ */
class WebtoonConfig(preferences: PreferencesHelper = Injekt.get()) : ViewerConfig(preferences) { class WebtoonConfig(
scope: CoroutineScope,
preferences: PreferencesHelper = Injekt.get()
) : ViewerConfig(preferences, scope) {
var imageCropBorders = false var imageCropBorders = false
private set private set

View File

@ -16,6 +16,8 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import rx.subscriptions.CompositeSubscription import rx.subscriptions.CompositeSubscription
import timber.log.Timber import timber.log.Timber
import kotlin.math.max import kotlin.math.max
@ -26,6 +28,8 @@ import kotlin.math.min
*/ */
class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true, private val tapByPage: Boolean = false) : BaseViewer { class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true, private val tapByPage: Boolean = false) : BaseViewer {
private val scope = MainScope()
/** /**
* Recycler view used by this viewer. * Recycler view used by this viewer.
*/ */
@ -59,7 +63,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
/** /**
* Configuration used by this viewer, like allow taps, or crop image borders. * Configuration used by this viewer, like allow taps, or crop image borders.
*/ */
val config = WebtoonConfig() val config = WebtoonConfig(scope)
/** /**
* Subscriptions to keep while this viewer is used. * Subscriptions to keep while this viewer is used.
@ -174,6 +178,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
override fun destroy() { override fun destroy() {
super.destroy() super.destroy()
scope.cancel()
subscriptions.unsubscribe() subscriptions.unsubscribe()
} }

View File

@ -198,7 +198,7 @@ class SettingsAdvancedController : SettingsController() {
summaryRes = R.string.data_saver_server_summary summaryRes = R.string.data_saver_server_summary
preferences.dataSaver().asImmediateFlow { isVisible = it } preferences.dataSaver().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -207,7 +207,7 @@ class SettingsAdvancedController : SettingsController() {
defaultValue = false defaultValue = false
preferences.dataSaver().asImmediateFlow { isVisible = it } preferences.dataSaver().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -216,7 +216,7 @@ class SettingsAdvancedController : SettingsController() {
defaultValue = true defaultValue = true
preferences.dataSaver().asImmediateFlow { isVisible = it } preferences.dataSaver().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
intListPreference { intListPreference {
@ -228,7 +228,7 @@ class SettingsAdvancedController : SettingsController() {
summaryRes = R.string.data_saver_image_quality_summary summaryRes = R.string.data_saver_image_quality_summary
preferences.dataSaver().asImmediateFlow { isVisible = it } preferences.dataSaver().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -239,7 +239,7 @@ class SettingsAdvancedController : SettingsController() {
summaryOff = context.getString(R.string.data_saver_image_format_summary_off) summaryOff = context.getString(R.string.data_saver_image_format_summary_off)
preferences.dataSaver().asImmediateFlow { isVisible = it } preferences.dataSaver().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -248,7 +248,7 @@ class SettingsAdvancedController : SettingsController() {
defaultValue = false defaultValue = false
preferences.dataSaver().asImmediateFlow { isVisible = it } preferences.dataSaver().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
} }

View File

@ -133,14 +133,14 @@ class SettingsBackupController : SettingsController() {
} }
preferences.backupInterval().asImmediateFlow { isVisible = it > 0 } preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
.launchIn(scope) .launchIn(viewScope)
preferences.backupsDirectory().asFlow() preferences.backupsDirectory().asFlow()
.onEach { path -> .onEach { path ->
val dir = UniFile.fromUri(context, path.toUri()) val dir = UniFile.fromUri(context, path.toUri())
summary = dir.filePath + "/automatic" summary = dir.filePath + "/automatic"
} }
.launchIn(scope) .launchIn(viewScope)
} }
intListPreference { intListPreference {
key = Keys.numberOfBackups key = Keys.numberOfBackups
@ -151,7 +151,7 @@ class SettingsBackupController : SettingsController() {
summary = "%s" summary = "%s"
preferences.backupInterval().asImmediateFlow { isVisible = it > 0 } preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
key = Keys.createLegacyBackup key = Keys.createLegacyBackup
@ -159,7 +159,7 @@ class SettingsBackupController : SettingsController() {
defaultValue = true defaultValue = true
preferences.backupInterval().asImmediateFlow { isVisible = it > 0 } preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
.launchIn(scope) .launchIn(viewScope)
} }
} }
} }

View File

@ -129,7 +129,7 @@ class SettingsBrowseController : SettingsController() {
titleRes = R.string.pref_label_nsfw_extension titleRes = R.string.pref_label_nsfw_extension
defaultValue = true defaultValue = true
preferences.showNsfwExtension().asImmediateFlow { isVisible = it }.launchIn(scope) preferences.showNsfwExtension().asImmediateFlow { isVisible = it }.launchIn(viewScope)
} }
infoPreference(R.string.parental_controls_info) infoPreference(R.string.parental_controls_info)

View File

@ -22,9 +22,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.subscriptions.CompositeSubscription import rx.subscriptions.CompositeSubscription
@ -35,7 +33,7 @@ abstract class SettingsController : PreferenceController() {
var preferenceKey: String? = null var preferenceKey: String? = null
val preferences: PreferencesHelper = Injekt.get() val preferences: PreferencesHelper = Injekt.get()
val scope = CoroutineScope(Job() + Dispatchers.Main) val viewScope = MainScope()
var untilDestroySubscriptions = CompositeSubscription() var untilDestroySubscriptions = CompositeSubscription()
private set private set

View File

@ -57,7 +57,7 @@ class SettingsDownloadController : SettingsController() {
val dir = UniFile.fromUri(context, path.toUri()) val dir = UniFile.fromUri(context, path.toUri())
summary = dir.filePath ?: path summary = dir.filePath ?: path
} }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
key = Keys.downloadOnlyOverWifi key = Keys.downloadOnlyOverWifi
@ -80,7 +80,7 @@ class SettingsDownloadController : SettingsController() {
defaultValue = "0" defaultValue = "0"
preferences.saveChaptersAsCBZ().asImmediateFlow { isVisible = it } preferences.saveChaptersAsCBZ().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
// SY <-- // SY <--
@ -137,7 +137,7 @@ class SettingsDownloadController : SettingsController() {
} }
) )
} }
.launchIn(scope) .launchIn(viewScope)
} }
// SY <-- // SY <--
} }
@ -160,7 +160,7 @@ class SettingsDownloadController : SettingsController() {
entryValues = categories.map { it.id.toString() }.toTypedArray() entryValues = categories.map { it.id.toString() }.toTypedArray()
preferences.downloadNew().asImmediateFlow { isVisible = it } preferences.downloadNew().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
preferences.downloadNewCategories().asFlow() preferences.downloadNewCategories().asFlow()
.onEach { mutableSet -> .onEach { mutableSet ->
@ -174,7 +174,7 @@ class SettingsDownloadController : SettingsController() {
selectedCategories.joinToString { it.name } selectedCategories.joinToString { it.name }
} }
} }
.launchIn(scope) .launchIn(viewScope)
} }
} }
} }

View File

@ -81,7 +81,7 @@ class SettingsEhController : SettingsController() {
// Only listen for first change commit // Only listen for first change commit
WarnConfigureDialogController.uploadSettings(router) WarnConfigureDialogController.uploadSettings(router)
} }
.launchIn(scope) .launchIn(viewScope)
// Always return true to save changes // Always return true to save changes
return true return true
@ -104,7 +104,7 @@ class SettingsEhController : SettingsController() {
.onEach { .onEach {
isChecked = it isChecked = it
} }
.launchIn(scope) .launchIn(viewScope)
onChange { newVal -> onChange { newVal ->
newVal as Boolean newVal as Boolean
@ -136,7 +136,7 @@ class SettingsEhController : SettingsController() {
onChange { preferences.useHentaiAtHome().reconfigure() } onChange { preferences.useHentaiAtHome().reconfigure() }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -149,7 +149,7 @@ class SettingsEhController : SettingsController() {
onChange { preferences.useJapaneseTitle().reconfigure() } onChange { preferences.useJapaneseTitle().reconfigure() }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -162,7 +162,7 @@ class SettingsEhController : SettingsController() {
onChange { preferences.exhUseOriginalImages().reconfigure() } onChange { preferences.exhUseOriginalImages().reconfigure() }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
preference { preference {
@ -179,7 +179,7 @@ class SettingsEhController : SettingsController() {
} }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
preference { preference {
@ -218,7 +218,7 @@ class SettingsEhController : SettingsController() {
} }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
preference { preference {
@ -258,7 +258,7 @@ class SettingsEhController : SettingsController() {
} }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
preference { preference {
@ -402,7 +402,7 @@ class SettingsEhController : SettingsController() {
} }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
preference { preference {
@ -459,7 +459,7 @@ class SettingsEhController : SettingsController() {
} }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -469,7 +469,7 @@ class SettingsEhController : SettingsController() {
summaryRes = R.string.watched_list_state_summary summaryRes = R.string.watched_list_state_summary
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
listPreference { listPreference {
@ -497,7 +497,7 @@ class SettingsEhController : SettingsController() {
onChange { preferences.imageQuality().reconfigure() } onChange { preferences.imageQuality().reconfigure() }
preferences.enableExhentai().asImmediateFlow { isVisible = it } preferences.enableExhentai().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
@ -592,7 +592,7 @@ class SettingsEhController : SettingsController() {
context.getString(R.string.time_between_batches_summary_2, context.getString(R.string.app_name), newVal, EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION) context.getString(R.string.time_between_batches_summary_2, context.getString(R.string.app_name), newVal, EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION)
} }
} }
.launchIn(scope) .launchIn(viewScope)
onChange { newValue -> onChange { newValue ->
val interval = (newValue as String).toInt() val interval = (newValue as String).toInt()
@ -610,7 +610,7 @@ class SettingsEhController : SettingsController() {
preferences.exhAutoUpdateFrequency().asFlow() preferences.exhAutoUpdateFrequency().asFlow()
.onEach { isVisible = it > 0 } .onEach { isVisible = it > 0 }
.launchIn(scope) .launchIn(viewScope)
onChange { onChange {
// Post to event looper to allow the preference to be updated. // Post to event looper to allow the preference to be updated.
@ -630,7 +630,7 @@ class SettingsEhController : SettingsController() {
progress.show() progress.show()
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
scope.launch(Dispatchers.IO) { viewScope.launch(Dispatchers.IO) {
val updateInfo = try { val updateInfo = try {
val stats = val stats =
preferences.exhAutoUpdateStats().get().nullIfBlank()?.let { preferences.exhAutoUpdateStats().get().nullIfBlank()?.let {

View File

@ -117,7 +117,7 @@ class SettingsGeneralController : SettingsController() {
summary = "%s" summary = "%s"
preferences.themeMode().asImmediateFlow { isVisible = it != Values.ThemeMode.dark } preferences.themeMode().asImmediateFlow { isVisible = it != Values.ThemeMode.dark }
.launchIn(scope) .launchIn(viewScope)
onChange { onChange {
if (preferences.themeMode().get() != Values.ThemeMode.dark) { if (preferences.themeMode().get() != Values.ThemeMode.dark) {
@ -147,7 +147,7 @@ class SettingsGeneralController : SettingsController() {
summary = "%s" summary = "%s"
preferences.themeMode().asImmediateFlow { isVisible = it != Values.ThemeMode.light } preferences.themeMode().asImmediateFlow { isVisible = it != Values.ThemeMode.light }
.launchIn(scope) .launchIn(viewScope)
onChange { onChange {
if (preferences.themeMode().get() != Values.ThemeMode.light) { if (preferences.themeMode().get() != Values.ThemeMode.light) {

View File

@ -79,7 +79,7 @@ class SettingsLibraryController : SettingsController() {
summary = "${context.getString(R.string.portrait)}: $portrait, " + summary = "${context.getString(R.string.portrait)}: $portrait, " +
"${context.getString(R.string.landscape)}: $landscape" "${context.getString(R.string.landscape)}: $landscape"
} }
.launchIn(scope) .launchIn(viewScope)
} }
switchPreference { switchPreference {
key = Keys.jumpToChapters key = Keys.jumpToChapters
@ -174,7 +174,7 @@ class SettingsLibraryController : SettingsController() {
defaultValue = setOf("wifi") defaultValue = setOf("wifi")
preferences.libraryUpdateInterval().asImmediateFlow { isVisible = it > 0 } preferences.libraryUpdateInterval().asImmediateFlow { isVisible = it > 0 }
.launchIn(scope) .launchIn(viewScope)
onChange { onChange {
// Post to event looper to allow the preference to be updated. // Post to event looper to allow the preference to be updated.
@ -204,7 +204,7 @@ class SettingsLibraryController : SettingsController() {
selectedCategories.joinToString { it.name } selectedCategories.joinToString { it.name }
} }
} }
.launchIn(scope) .launchIn(viewScope)
} }
// SY --> // SY -->
listPreference { listPreference {

View File

@ -152,7 +152,7 @@ class SettingsMangaDexController :
SimilarUpdateJob.setupTask(context, true) SimilarUpdateJob.setupTask(context, true)
} }
.drop(1) .drop(1)
.launchIn(scope) .launchIn(viewScope)
} }
preference { preference {

View File

@ -256,7 +256,7 @@ class SettingsReaderController : SettingsController() {
defaultValue = "0" defaultValue = "0"
summary = "%s" summary = "%s"
preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope) preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
} }
listPreference { listPreference {
key = Keys.pagerNavInverted key = Keys.pagerNavInverted
@ -276,7 +276,7 @@ class SettingsReaderController : SettingsController() {
defaultValue = TappingInvertMode.NONE.name defaultValue = TappingInvertMode.NONE.name
summary = "%s" summary = "%s"
preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope) preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
} }
intListPreference { intListPreference {
key = Keys.imageScaleType key = Keys.imageScaleType
@ -329,7 +329,7 @@ class SettingsReaderController : SettingsController() {
defaultValue = "0" defaultValue = "0"
summary = "%s" summary = "%s"
preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope) preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
} }
listPreference { listPreference {
key = Keys.webtoonNavInverted key = Keys.webtoonNavInverted
@ -349,7 +349,7 @@ class SettingsReaderController : SettingsController() {
defaultValue = TappingInvertMode.NONE.name defaultValue = TappingInvertMode.NONE.name
summary = "%s" summary = "%s"
preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope) preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
} }
intListPreference { intListPreference {
key = Keys.webtoonSidePadding key = Keys.webtoonSidePadding
@ -418,7 +418,7 @@ class SettingsReaderController : SettingsController() {
titleRes = R.string.pref_read_with_volume_keys_inverted titleRes = R.string.pref_read_with_volume_keys_inverted
defaultValue = false defaultValue = false
preferences.readWithVolumeKeys().asImmediateFlow { isVisible = it }.launchIn(scope) preferences.readWithVolumeKeys().asImmediateFlow { isVisible = it }.launchIn(viewScope)
} }
} }
} }

View File

@ -43,7 +43,7 @@ class SettingsSecurityController : SettingsController() {
summary = "%s" summary = "%s"
preferences.useBiometricLock().asImmediateFlow { isVisible = it } preferences.useBiometricLock().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
} }
@ -66,7 +66,7 @@ class SettingsSecurityController : SettingsController() {
summary = context.resources.getQuantityString(R.plurals.num_lock_times, timeRanges, timeRanges) summary = context.resources.getQuantityString(R.plurals.num_lock_times, timeRanges, timeRanges)
preferences.useBiometricLock().asImmediateFlow { isVisible = it } preferences.useBiometricLock().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
onClick { onClick {
router.pushController(BiometricTimesController().withFadeTransaction()) router.pushController(BiometricTimesController().withFadeTransaction())

View File

@ -7,9 +7,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import eu.kanade.tachiyomi.databinding.DownloadCustomAmountBinding import eu.kanade.tachiyomi.databinding.DownloadCustomAmountBinding
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges import reactivecircus.flowbinding.android.widget.textChanges
@ -37,7 +35,7 @@ class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs
*/ */
private var max = 0 private var max = 0
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = MainScope()
private val binding: DownloadCustomAmountBinding private val binding: DownloadCustomAmountBinding