Address more coroutine scope leaks

pull/4276/head
arkon 4 years ago
parent 36f81b4a62
commit 6fb7a85e8a

@ -272,7 +272,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")
} }
tasks { tasks {

@ -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.

@ -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.

@ -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

@ -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
@ -33,8 +30,6 @@ class SourcePresenter(
private val preferences: PreferencesHelper = Injekt.get() private val preferences: PreferencesHelper = Injekt.get()
) : BasePresenter<SourceController>() { ) : BasePresenter<SourceController>() {
private val scope = CoroutineScope(Job() + Dispatchers.Main)
var sources = getEnabledSources() var sources = getEnabledSources()
/** /**
@ -98,7 +93,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) {

@ -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

@ -19,9 +19,7 @@ import eu.kanade.tachiyomi.util.lang.plusAssign
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.inflate import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.AutofitRecyclerView
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.recyclerview.scrollStateChanges import reactivecircus.flowbinding.recyclerview.scrollStateChanges
@ -37,7 +35,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener { FlexibleAdapter.OnItemLongClickListener {
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = MainScope()
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()

@ -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)
} }
} }
} }

@ -23,9 +23,6 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setChips import eu.kanade.tachiyomi.util.view.setChips
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 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 initialLoad: Boolean = true private var initialLoad: Boolean = true
@ -90,12 +86,12 @@ class MangaInfoHeaderAdapter(
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) {
@ -118,7 +114,7 @@ class MangaInfoHeaderAdapter(
clicks() clicks()
.onEach { controller.onTrackingClick() } .onEach { controller.onTrackingClick() }
.launchIn(scope) .launchIn(controller.viewScope)
} else { } else {
isVisible = false isVisible = false
} }
@ -128,7 +124,7 @@ 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)
} }
binding.mangaFullTitle.longClicks() binding.mangaFullTitle.longClicks()
@ -138,13 +134,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 {
@ -153,13 +149,13 @@ class MangaInfoHeaderAdapter(
binding.mangaAuthor.text.toString() binding.mangaAuthor.text.toString()
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaAuthor.clicks() binding.mangaAuthor.clicks()
.onEach { .onEach {
controller.performGlobalSearch(binding.mangaAuthor.text.toString()) controller.performGlobalSearch(binding.mangaAuthor.text.toString())
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaArtist.longClicks() binding.mangaArtist.longClicks()
.onEach { .onEach {
@ -168,13 +164,13 @@ class MangaInfoHeaderAdapter(
binding.mangaArtist.text.toString() binding.mangaArtist.text.toString()
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaArtist.clicks() binding.mangaArtist.clicks()
.onEach { .onEach {
controller.performGlobalSearch(binding.mangaArtist.text.toString()) controller.performGlobalSearch(binding.mangaArtist.text.toString())
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaSummaryText.longClicks() binding.mangaSummaryText.longClicks()
.onEach { .onEach {
@ -183,7 +179,7 @@ class MangaInfoHeaderAdapter(
binding.mangaSummaryText.text.toString() binding.mangaSummaryText.text.toString()
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
binding.mangaCover.longClicks() binding.mangaCover.longClicks()
.onEach { .onEach {
@ -192,7 +188,7 @@ class MangaInfoHeaderAdapter(
controller.presenter.manga.title controller.presenter.manga.title
) )
} }
.launchIn(scope) .launchIn(controller.viewScope)
setMangaInfo(manga, source) setMangaInfo(manga, source)
} }
@ -300,7 +296,7 @@ class MangaInfoHeaderAdapter(
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) {

@ -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

@ -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

@ -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.
*/ */

@ -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

@ -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) : BaseViewer { class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true) : 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.
@ -168,6 +172,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()
} }

@ -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)
} }
} }
} }

@ -64,7 +64,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)

@ -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

@ -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
@ -112,7 +112,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 ->
@ -126,7 +126,7 @@ class SettingsDownloadController : SettingsController() {
selectedCategories.joinToString { it.name } selectedCategories.joinToString { it.name }
} }
} }
.launchIn(scope) .launchIn(viewScope)
} }
} }
} }

@ -116,7 +116,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) {
@ -142,7 +142,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) {

@ -70,7 +70,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
@ -150,7 +150,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.
@ -180,7 +180,7 @@ class SettingsLibraryController : SettingsController() {
selectedCategories.joinToString { it.name } selectedCategories.joinToString { it.name }
} }
} }
.launchIn(scope) .launchIn(viewScope)
} }
intListPreference { intListPreference {
key = Keys.libraryUpdatePrioritization key = Keys.libraryUpdatePrioritization

@ -150,7 +150,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
@ -170,7 +170,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
@ -223,7 +223,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
@ -243,7 +243,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
@ -289,7 +289,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)
} }
} }
} }

@ -39,7 +39,7 @@ class SettingsSecurityController : SettingsController() {
summary = "%s" summary = "%s"
preferences.useBiometricLock().asImmediateFlow { isVisible = it } preferences.useBiometricLock().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(viewScope)
} }
} }

@ -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

Loading…
Cancel
Save