diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index a2a1bb997f..9e370d10f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -43,17 +43,6 @@ fun com.tfcporciuncula.flow.Preference.asImmediateFlowIn(scope: Coroutine .launchIn(scope) } -fun Collection>.asFlowsIn(scope: CoroutineScope, dropFirst: Boolean = false, block: () -> Unit): Collection { - return map { pref -> - pref.asFlow() - .apply { - if (dropFirst) drop(1) - } - .onEach { block() } - .launchIn(scope) - } -} - fun com.tfcporciuncula.flow.Preference.toggle() = set(!get()) private class DateFormatConverter : Preference.Adapter { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt index 0de8ed8336..c3710e422e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt @@ -13,12 +13,16 @@ import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.RestoreViewOnCreateController import eu.kanade.tachiyomi.util.view.removeQueryListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel import timber.log.Timber abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle) { lateinit var binding: VB + lateinit var viewScope: CoroutineScope val isBindingInitialized get() = this::binding.isInitialized init { @@ -29,6 +33,7 @@ abstract class BaseController(bundle: Bundle? = null) : } override fun preCreateView(controller: Controller) { + viewScope = MainScope() Timber.d("Create view for ${controller.instance()}") } @@ -41,6 +46,7 @@ abstract class BaseController(bundle: Bundle? = null) : } override fun preDestroyView(controller: Controller, view: View) { + viewScope.cancel() Timber.d("Destroy view for ${controller.instance()}") } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/details/ExtensionDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/details/ExtensionDetailsController.kt index 2763462f6b..85ad89b870 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/details/ExtensionDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/details/ExtensionDetailsController.kt @@ -46,7 +46,6 @@ import eu.kanade.tachiyomi.util.view.openInBrowser import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.widget.preference.ListMatPreference -import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import uy.kohesive.injekt.Injekt @@ -64,7 +63,6 @@ class ExtensionDetailsController(bundle: Bundle? = null) : private val preferences: PreferencesHelper = Injekt.get() - private val viewScope = MainScope() init { setHasOptionsMenu(true) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 8f9b825d2d..8723d484f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -28,7 +28,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.GestureDetectorCompat import androidx.core.view.isInvisible import androidx.core.view.isVisible -import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -53,7 +52,6 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.asFlowsIn import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.databinding.LibraryControllerBinding import eu.kanade.tachiyomi.source.LocalSource @@ -99,11 +97,11 @@ import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.updatePaddingRelative import eu.kanade.tachiyomi.util.view.withFadeTransaction import eu.kanade.tachiyomi.widget.EndAnimatorListener -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Locale @@ -150,8 +148,6 @@ class LibraryController( var singleCategory: Boolean = false private set - val scope = CoroutineScope(Job() + Dispatchers.Main) - /** * Library search query. */ @@ -835,8 +831,13 @@ class LibraryController( preferences.uniformGrid(), preferences.gridSize(), preferences.unreadBadgeType() - ).asFlowsIn(scope, true) { - reattachAdapter() + ).forEach { + it.asFlow() + .drop(1) + .onEach { + reattachAdapter() + } + .launchIn(viewScope) } } @@ -885,7 +886,6 @@ class LibraryController( override fun onDestroy() { if (::presenter.isInitialized) presenter.onDestroy() - scope.cancel() super.onDestroy() } @@ -897,6 +897,7 @@ class LibraryController( } displaySheet?.dismiss() displaySheet = null + presenter.cancelScope() super.onDestroyView(view) } @@ -913,11 +914,6 @@ class LibraryController( ) } adapter.setItems(mangaMap) - if (binding.libraryGridRecycler.recycler.itemAnimator == null) { - binding.libraryGridRecycler.recycler.post { - binding.libraryGridRecycler.recycler.itemAnimator = DefaultItemAnimator() - } - } singleCategory = presenter.categories.size <= 1 showDropdown() binding.progress.isVisible = false @@ -951,9 +947,7 @@ class LibraryController( listOf(activityBinding?.toolbar, binding.headerTitle).forEach { it?.setOnClickListener { val recycler = binding.libraryGridRecycler.recycler - if (singleCategory) { - recycler.scrollToPosition(0) - } else { + if (!singleCategory) { showCategories(recycler.translationY == 0f) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 2f03e5b33c..d0dd4f8aed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -33,6 +33,7 @@ import eu.kanade.tachiyomi.util.system.executeOnIO import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import uy.kohesive.injekt.Injekt @@ -86,6 +87,10 @@ class LibraryPresenter( val libraryIsGrouped get() = groupType != UNGROUPED + fun cancelScope() { + scope.cancel() + } + /** Save the current list to speed up loading later */ fun onDestroy() { lastLibraryItems = libraryItems @@ -105,6 +110,7 @@ class LibraryPresenter( if (categories.isEmpty()) { categories = lastCategories ?: db.getCategories().executeAsBlocking().toMutableList() } + scope = CoroutineScope(Job() + Dispatchers.Default) scope.launch { val library = withContext(Dispatchers.IO) { getLibraryFromDB() } library.apply { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt index a356e1ccf9..127fbf5c68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt @@ -181,7 +181,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri filterOrder = it clearFilters() } - .launchIn(controller.scope) + .launchIn(controller.viewScope) } private fun stateChanged(state: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 5f6e28d755..c5f5a86bb8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -37,7 +37,6 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.asFlowsIn import eu.kanade.tachiyomi.data.preference.asImmediateFlowIn import eu.kanade.tachiyomi.data.preference.toggle import eu.kanade.tachiyomi.databinding.ReaderActivityBinding @@ -637,7 +636,11 @@ class ReaderActivity : } listOf(preferences.cropBorders(), preferences.cropBordersWebtoon()) - .asFlowsIn(scope) { updateCropBordersShortcut() } + .forEach { pref -> + pref.asFlow() + .onEach { updateCropBordersShortcut() } + .launchIn(scope) + } preferences.rotation().asImmediateFlowIn(scope) { updateRotationShortcut(it) } binding.chaptersSheet.shiftPageButton.setOnClickListener { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt index 8cdf760aae..1083fa1a74 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt @@ -54,10 +54,6 @@ import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.updatePaddingRelative import eu.kanade.tachiyomi.util.view.withFadeTransaction -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel import java.util.Locale import kotlin.math.abs import kotlin.math.max @@ -89,12 +85,10 @@ class RecentsController(bundle: Bundle? = null) : presenter.toggleGroupRecents(viewType, false) } - private val adapterScope: CoroutineScope = CoroutineScope(Job() + Dispatchers.Main) - /** * Adapter containing the recent manga. */ - private var adapter = RecentMangaAdapter(this) + private lateinit var adapter: RecentMangaAdapter var displaySheet: TabbedRecentsOptionsSheet? = null private var progressItem: ProgressItem? = null @@ -383,14 +377,13 @@ class RecentsController(bundle: Bundle? = null) : snack?.dismiss() presenter.onDestroy() snack = null - adapterScope.cancel() - presenter.cancelScope() } override fun onDestroyView(view: View) { super.onDestroyView(view) displaySheet?.dismiss() displaySheet = null + presenter.cancelScope() } fun refresh() = presenter.getRecents() @@ -497,7 +490,7 @@ class RecentsController(bundle: Bundle? = null) : override fun getViewType(): Int = presenter.viewType - override fun scope() = adapterScope + override fun scope() = viewScope override fun onItemClick(view: View?, position: Int): Boolean { val item = adapter.getItem(position) ?: return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt index e568514b75..8ab4a2591e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt @@ -12,13 +12,15 @@ import eu.kanade.tachiyomi.data.download.model.DownloadQueue import eu.kanade.tachiyomi.data.library.LibraryServiceListener import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.asFlowsIn import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.util.system.executeOnIO import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import uy.kohesive.injekt.Injekt @@ -71,17 +73,6 @@ class RecentsPresenter( private val isOnFirstPage: Boolean get() = pageOffset == 0 - init { - listOf( - preferences.groupChaptersHistory(), - preferences.showReadInAllRecents(), - preferences.groupChaptersUpdates() - ).asFlowsIn(scope, true) { - resetOffsets() - getRecents() - } - } - fun onCreate() { downloadManager.addListener(this) LibraryUpdateService.setListener(this) @@ -92,6 +83,20 @@ class RecentsPresenter( lastRecents = null } getRecents() + scope = CoroutineScope(Job() + Dispatchers.Default) + listOf( + preferences.groupChaptersHistory(), + preferences.showReadInAllRecents(), + preferences.groupChaptersUpdates() + ).forEach { + it.asFlow() + .drop(1) + .onEach { + resetOffsets() + getRecents() + } + .launchIn(scope) + } } fun getRecents(updatePageCount: Boolean = false) {