mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Address more coroutine scope leaks
This commit is contained in:
		@@ -272,7 +272,7 @@ dependencies {
 | 
			
		||||
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
 | 
			
		||||
 | 
			
		||||
    // 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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
@@ -27,7 +25,7 @@ class DownloadProvider(private val context: Context) {
 | 
			
		||||
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    private val scope = MainScope()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The root directory for downloads.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,32 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.base.presenter
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.cancel
 | 
			
		||||
import nucleus.presenter.RxPresenter
 | 
			
		||||
import nucleus.presenter.delivery.Delivery
 | 
			
		||||
import rx.Observable
 | 
			
		||||
 | 
			
		||||
open class BasePresenter<V> : RxPresenter<V>() {
 | 
			
		||||
 | 
			
		||||
    lateinit var presenterScope: CoroutineScope
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        try {
 | 
			
		||||
            super.onCreate(savedState)
 | 
			
		||||
            presenterScope = MainScope()
 | 
			
		||||
        } catch (e: NullPointerException) {
 | 
			
		||||
            // 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.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroy() {
 | 
			
		||||
        super.onDestroy()
 | 
			
		||||
        presenterScope.cancel()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle
 | 
			
		||||
     * subscription list.
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,6 @@ import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.databinding.ExtensionDetailHeaderBinding
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.getApplicationIcon
 | 
			
		||||
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.onEach
 | 
			
		||||
import reactivecircus.flowbinding.android.view.clicks
 | 
			
		||||
@@ -19,7 +16,6 @@ import reactivecircus.flowbinding.android.view.clicks
 | 
			
		||||
class ExtensionDetailsHeaderAdapter(private val presenter: ExtensionDetailsPresenter) :
 | 
			
		||||
    RecyclerView.Adapter<ExtensionDetailsHeaderAdapter.HeaderViewHolder>() {
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    private lateinit var binding: ExtensionDetailHeaderBinding
 | 
			
		||||
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
 | 
			
		||||
@@ -47,7 +43,7 @@ class ExtensionDetailsHeaderAdapter(private val presenter: ExtensionDetailsPrese
 | 
			
		||||
 | 
			
		||||
            binding.extensionUninstallButton.clicks()
 | 
			
		||||
                .onEach { presenter.uninstallExtension() }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(presenter.presenterScope)
 | 
			
		||||
 | 
			
		||||
            if (extension.isObsolete) {
 | 
			
		||||
                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.SourceManager
 | 
			
		||||
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.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.drop
 | 
			
		||||
@@ -33,8 +30,6 @@ class SourcePresenter(
 | 
			
		||||
    private val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
) : BasePresenter<SourceController>() {
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
 | 
			
		||||
    var sources = getEnabledSources()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -98,7 +93,7 @@ class SourcePresenter(
 | 
			
		||||
            .onStart { delay(500) }
 | 
			
		||||
            .distinctUntilChanged()
 | 
			
		||||
            .onEach { updateLastUsedSource(it) }
 | 
			
		||||
            .launchIn(scope)
 | 
			
		||||
            .launchIn(presenterScope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updateLastUsedSource(sourceId: Long) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,16 +10,14 @@ import eu.davidea.flexibleadapter.items.IFlexible
 | 
			
		||||
import eu.davidea.viewholders.FlexibleViewHolder
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Filter
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import reactivecircus.flowbinding.android.widget.textChanges
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
        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.view.inflate
 | 
			
		||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import reactivecircus.flowbinding.recyclerview.scrollStateChanges
 | 
			
		||||
@@ -37,7 +35,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
 | 
			
		||||
    FlexibleAdapter.OnItemClickListener,
 | 
			
		||||
    FlexibleAdapter.OnItemLongClickListener {
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    private val scope = MainScope()
 | 
			
		||||
 | 
			
		||||
    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.ui.manga.MangaController
 | 
			
		||||
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.merge
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
@@ -25,7 +22,6 @@ class MangaChaptersHeaderAdapter(
 | 
			
		||||
    private var numChapters: Int? = null
 | 
			
		||||
    private var hasActiveFilters: Boolean = false
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    private lateinit var binding: MangaChaptersHeaderBinding
 | 
			
		||||
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
 | 
			
		||||
@@ -68,7 +64,7 @@ class MangaChaptersHeaderAdapter(
 | 
			
		||||
 | 
			
		||||
            merge(view.clicks(), binding.btnChaptersFilter.clicks())
 | 
			
		||||
                .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.getResourceColor
 | 
			
		||||
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.merge
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
@@ -47,7 +44,6 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
    private var source: Source = controller.presenter.source
 | 
			
		||||
    private var trackCount: Int = 0
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    private lateinit var binding: MangaInfoHeaderBinding
 | 
			
		||||
 | 
			
		||||
    private var initialLoad: Boolean = true
 | 
			
		||||
@@ -90,12 +86,12 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
 | 
			
		||||
            binding.btnFavorite.clicks()
 | 
			
		||||
                .onEach { controller.onFavoriteClick() }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            if (controller.presenter.manga.favorite && controller.presenter.getCategories().isNotEmpty()) {
 | 
			
		||||
                binding.btnFavorite.longClicks()
 | 
			
		||||
                    .onEach { controller.onCategoriesClick() }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(controller.viewScope)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            with(binding.btnTracking) {
 | 
			
		||||
@@ -118,7 +114,7 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
 | 
			
		||||
                    clicks()
 | 
			
		||||
                        .onEach { controller.onTrackingClick() }
 | 
			
		||||
                        .launchIn(scope)
 | 
			
		||||
                        .launchIn(controller.viewScope)
 | 
			
		||||
                } else {
 | 
			
		||||
                    isVisible = false
 | 
			
		||||
                }
 | 
			
		||||
@@ -128,7 +124,7 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                binding.btnWebview.isVisible = true
 | 
			
		||||
                binding.btnWebview.clicks()
 | 
			
		||||
                    .onEach { controller.openMangaInWebView() }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(controller.viewScope)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            binding.mangaFullTitle.longClicks()
 | 
			
		||||
@@ -138,13 +134,13 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                        binding.mangaFullTitle.text.toString()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaFullTitle.clicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
                    controller.performGlobalSearch(binding.mangaFullTitle.text.toString())
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaAuthor.longClicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
@@ -153,13 +149,13 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                        binding.mangaAuthor.text.toString()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaAuthor.clicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
                    controller.performGlobalSearch(binding.mangaAuthor.text.toString())
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaArtist.longClicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
@@ -168,13 +164,13 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                        binding.mangaArtist.text.toString()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaArtist.clicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
                    controller.performGlobalSearch(binding.mangaArtist.text.toString())
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaSummaryText.longClicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
@@ -183,7 +179,7 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                        binding.mangaSummaryText.text.toString()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            binding.mangaCover.longClicks()
 | 
			
		||||
                .onEach {
 | 
			
		||||
@@ -192,7 +188,7 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                        controller.presenter.manga.title
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
            setMangaInfo(manga, source)
 | 
			
		||||
        }
 | 
			
		||||
@@ -300,7 +296,7 @@ class MangaInfoHeaderAdapter(
 | 
			
		||||
                    binding.mangaInfoToggleLess.clicks()
 | 
			
		||||
                )
 | 
			
		||||
                    .onEach { toggleMangaInfo() }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(controller.viewScope)
 | 
			
		||||
 | 
			
		||||
                // Expand manga info if navigated from source listing
 | 
			
		||||
                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.PreferencesHelper
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
@@ -13,9 +11,7 @@ import kotlinx.coroutines.flow.onEach
 | 
			
		||||
/**
 | 
			
		||||
 * Common configuration for all viewers.
 | 
			
		||||
 */
 | 
			
		||||
abstract class ViewerConfig(preferences: PreferencesHelper) {
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: CoroutineScope) {
 | 
			
		||||
 | 
			
		||||
    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.KindlishNavigation
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Configuration used by pager viewers.
 | 
			
		||||
 */
 | 
			
		||||
class PagerConfig(private val viewer: PagerViewer, preferences: PreferencesHelper = Injekt.get()) :
 | 
			
		||||
    ViewerConfig(preferences) {
 | 
			
		||||
class PagerConfig(
 | 
			
		||||
    private val viewer: PagerViewer,
 | 
			
		||||
    scope: CoroutineScope,
 | 
			
		||||
    preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
) : ViewerConfig(preferences, scope) {
 | 
			
		||||
 | 
			
		||||
    var imageScaleType = 1
 | 
			
		||||
        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.viewer.BaseViewer
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.cancel
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
@@ -25,6 +27,8 @@ import kotlin.math.min
 | 
			
		||||
@Suppress("LeakingThis")
 | 
			
		||||
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
 | 
			
		||||
     * 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...
 | 
			
		||||
     */
 | 
			
		||||
    val config = PagerConfig(this)
 | 
			
		||||
    val config = PagerConfig(this, scope)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -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.KindlishNavigation
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
        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.viewer.BaseViewer
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.cancel
 | 
			
		||||
import rx.subscriptions.CompositeSubscription
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
@@ -26,6 +28,8 @@ import kotlin.math.min
 | 
			
		||||
 */
 | 
			
		||||
class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true) : BaseViewer {
 | 
			
		||||
 | 
			
		||||
    private val scope = MainScope()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    val config = WebtoonConfig()
 | 
			
		||||
    val config = WebtoonConfig(scope)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Subscriptions to keep while this viewer is used.
 | 
			
		||||
@@ -168,6 +172,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
 | 
			
		||||
     */
 | 
			
		||||
    override fun destroy() {
 | 
			
		||||
        super.destroy()
 | 
			
		||||
        scope.cancel()
 | 
			
		||||
        subscriptions.unsubscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -133,14 +133,14 @@ class SettingsBackupController : SettingsController() {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
 | 
			
		||||
                preferences.backupsDirectory().asFlow()
 | 
			
		||||
                    .onEach { path ->
 | 
			
		||||
                        val dir = UniFile.fromUri(context, path.toUri())
 | 
			
		||||
                        summary = dir.filePath + "/automatic"
 | 
			
		||||
                    }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            intListPreference {
 | 
			
		||||
                key = Keys.numberOfBackups
 | 
			
		||||
@@ -151,7 +151,7 @@ class SettingsBackupController : SettingsController() {
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.createLegacyBackup
 | 
			
		||||
@@ -159,7 +159,7 @@ class SettingsBackupController : SettingsController() {
 | 
			
		||||
                defaultValue = true
 | 
			
		||||
 | 
			
		||||
                preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ class SettingsBrowseController : SettingsController() {
 | 
			
		||||
                titleRes = R.string.pref_label_nsfw_extension
 | 
			
		||||
                defaultValue = true
 | 
			
		||||
 | 
			
		||||
                preferences.showNsfwExtension().asImmediateFlow { isVisible = it }.launchIn(scope)
 | 
			
		||||
                preferences.showNsfwExtension().asImmediateFlow { isVisible = it }.launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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.RootController
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.getResourceColor
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.subscriptions.CompositeSubscription
 | 
			
		||||
@@ -35,7 +33,7 @@ abstract class SettingsController : PreferenceController() {
 | 
			
		||||
 | 
			
		||||
    var preferenceKey: String? = null
 | 
			
		||||
    val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
    val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    val viewScope = MainScope()
 | 
			
		||||
 | 
			
		||||
    var untilDestroySubscriptions = CompositeSubscription()
 | 
			
		||||
        private set
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ class SettingsDownloadController : SettingsController() {
 | 
			
		||||
                    val dir = UniFile.fromUri(context, path.toUri())
 | 
			
		||||
                    summary = dir.filePath ?: path
 | 
			
		||||
                }
 | 
			
		||||
                .launchIn(scope)
 | 
			
		||||
                .launchIn(viewScope)
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.downloadOnlyOverWifi
 | 
			
		||||
@@ -112,7 +112,7 @@ class SettingsDownloadController : SettingsController() {
 | 
			
		||||
                entryValues = categories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
 | 
			
		||||
                preferences.downloadNew().asImmediateFlow { isVisible = it }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
 | 
			
		||||
                preferences.downloadNewCategories().asFlow()
 | 
			
		||||
                    .onEach { mutableSet ->
 | 
			
		||||
@@ -126,7 +126,7 @@ class SettingsDownloadController : SettingsController() {
 | 
			
		||||
                            selectedCategories.joinToString { it.name }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ class SettingsGeneralController : SettingsController() {
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.themeMode().asImmediateFlow { isVisible = it != Values.ThemeMode.dark }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
 | 
			
		||||
                onChange {
 | 
			
		||||
                    if (preferences.themeMode().get() != Values.ThemeMode.dark) {
 | 
			
		||||
@@ -142,7 +142,7 @@ class SettingsGeneralController : SettingsController() {
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.themeMode().asImmediateFlow { isVisible = it != Values.ThemeMode.light }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
 | 
			
		||||
                onChange {
 | 
			
		||||
                    if (preferences.themeMode().get() != Values.ThemeMode.light) {
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,7 @@ class SettingsLibraryController : SettingsController() {
 | 
			
		||||
                        summary = "${context.getString(R.string.portrait)}: $portrait, " +
 | 
			
		||||
                            "${context.getString(R.string.landscape)}: $landscape"
 | 
			
		||||
                    }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.jumpToChapters
 | 
			
		||||
@@ -150,7 +150,7 @@ class SettingsLibraryController : SettingsController() {
 | 
			
		||||
                defaultValue = setOf("wifi")
 | 
			
		||||
 | 
			
		||||
                preferences.libraryUpdateInterval().asImmediateFlow { isVisible = it > 0 }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
 | 
			
		||||
                onChange {
 | 
			
		||||
                    // Post to event looper to allow the preference to be updated.
 | 
			
		||||
@@ -180,7 +180,7 @@ class SettingsLibraryController : SettingsController() {
 | 
			
		||||
                            selectedCategories.joinToString { it.name }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            intListPreference {
 | 
			
		||||
                key = Keys.libraryUpdatePrioritization
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,7 @@ class SettingsReaderController : SettingsController() {
 | 
			
		||||
                defaultValue = "0"
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope)
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            listPreference {
 | 
			
		||||
                key = Keys.pagerNavInverted
 | 
			
		||||
@@ -170,7 +170,7 @@ class SettingsReaderController : SettingsController() {
 | 
			
		||||
                defaultValue = TappingInvertMode.NONE.name
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope)
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            intListPreference {
 | 
			
		||||
                key = Keys.imageScaleType
 | 
			
		||||
@@ -223,7 +223,7 @@ class SettingsReaderController : SettingsController() {
 | 
			
		||||
                defaultValue = "0"
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope)
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            listPreference {
 | 
			
		||||
                key = Keys.webtoonNavInverted
 | 
			
		||||
@@ -243,7 +243,7 @@ class SettingsReaderController : SettingsController() {
 | 
			
		||||
                defaultValue = TappingInvertMode.NONE.name
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(scope)
 | 
			
		||||
                preferences.readWithTapping().asImmediateFlow { isVisible = it }.launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
            intListPreference {
 | 
			
		||||
                key = Keys.webtoonSidePadding
 | 
			
		||||
@@ -289,7 +289,7 @@ class SettingsReaderController : SettingsController() {
 | 
			
		||||
                titleRes = R.string.pref_read_with_volume_keys_inverted
 | 
			
		||||
                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"
 | 
			
		||||
 | 
			
		||||
                preferences.useBiometricLock().asImmediateFlow { isVisible = it }
 | 
			
		||||
                    .launchIn(scope)
 | 
			
		||||
                    .launchIn(viewScope)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,7 @@ import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.LinearLayout
 | 
			
		||||
import eu.kanade.tachiyomi.databinding.DownloadCustomAmountBinding
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.MainScope
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import reactivecircus.flowbinding.android.widget.textChanges
 | 
			
		||||
@@ -37,7 +35,7 @@ class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs
 | 
			
		||||
     */
 | 
			
		||||
    private var max = 0
 | 
			
		||||
 | 
			
		||||
    private val scope = CoroutineScope(Job() + Dispatchers.Main)
 | 
			
		||||
    private val scope = MainScope()
 | 
			
		||||
 | 
			
		||||
    private val binding: DownloadCustomAmountBinding
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user