Remove unnecessary base Nucleus classes

The reader still uses it, but we just move stuff to there.
This commit is contained in:
arkon 2022-12-02 13:23:26 -05:00
parent 5b189a909b
commit 5313a5d5d2
13 changed files with 48 additions and 251 deletions

View file

@ -274,7 +274,7 @@ dependencies {
implementation(libs.wheelpicker)
// Conductor
implementation(libs.bundles.conductor)
implementation(libs.conductor)
// FlowBinding
implementation(libs.flowbinding.android)

View file

@ -3,21 +3,17 @@ package eu.kanade.tachiyomi.ui.base.activity
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegateImpl
import eu.kanade.tachiyomi.util.system.prepareTabletUiContext
import uy.kohesive.injekt.injectLazy
open class BaseActivity :
AppCompatActivity(),
SecureActivityDelegate by SecureActivityDelegateImpl(),
ThemingDelegate by ThemingDelegateImpl() {
protected val preferences: BasePreferences by injectLazy()
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase.prepareTabletUiContext())
}

View file

@ -1,30 +0,0 @@
package eu.kanade.tachiyomi.ui.base.activity
import android.content.Context
import android.os.Bundle
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegateImpl
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.system.prepareTabletUiContext
import nucleus.view.NucleusAppCompatActivity
import uy.kohesive.injekt.injectLazy
open class BaseRxActivity<P : BasePresenter<*>> :
NucleusAppCompatActivity<P>(),
SecureActivityDelegate by SecureActivityDelegateImpl(),
ThemingDelegate by ThemingDelegateImpl() {
protected val preferences: BasePreferences by injectLazy()
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase.prepareTabletUiContext())
}
override fun onCreate(savedInstanceState: Bundle?) {
applyAppTheme(this)
super.onCreate(savedInstanceState)
}
}

View file

@ -9,37 +9,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import eu.kanade.presentation.util.LocalRouter
import eu.kanade.tachiyomi.databinding.ComposeControllerBinding
import eu.kanade.tachiyomi.util.view.setComposeContent
import nucleus.presenter.Presenter
abstract class FullComposeController<P : Presenter<*>>(bundle: Bundle? = null) :
NucleusController<ComposeControllerBinding, P>(bundle),
ComposeContentController {
override fun createBinding(inflater: LayoutInflater) =
ComposeControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.root.apply {
setComposeContent {
CompositionLocalProvider(LocalRouter provides router) {
ComposeContent()
}
}
}
}
override fun handleBack(): Boolean {
val dispatcher = (activity as? OnBackPressedDispatcherOwner)?.onBackPressedDispatcher ?: return false
return if (dispatcher.hasEnabledCallbacks()) {
dispatcher.onBackPressed()
true
} else {
false
}
}
}
/**
* Basic Compose controller without a presenter.

View file

@ -1,7 +1,5 @@
package eu.kanade.tachiyomi.ui.base.controller
import android.content.pm.PackageManager.PERMISSION_GRANTED
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.Router
@ -12,28 +10,10 @@ fun Router.setRoot(controller: Controller, id: Int) {
setRoot(controller.withFadeTransaction().tag(id.toString()))
}
fun Router.popControllerWithTag(tag: String): Boolean {
val controller = getControllerWithTag(tag)
if (controller != null) {
popController(controller)
return true
}
return false
}
fun Router.pushController(controller: Controller) {
pushController(controller.withFadeTransaction())
}
fun Controller.requestPermissionsSafe(permissions: Array<String>, requestCode: Int) {
val activity = activity ?: return
permissions.forEach { permission ->
if (ContextCompat.checkSelfPermission(activity, permission) != PERMISSION_GRANTED) {
requestPermissions(arrayOf(permission), requestCode)
}
}
}
fun Controller.withFadeTransaction(): RouterTransaction {
return RouterTransaction.with(this)
.pushChangeHandler(OneWayFadeChangeHandler())

View file

@ -1,23 +0,0 @@
package eu.kanade.tachiyomi.ui.base.controller
import android.os.Bundle
import androidx.viewbinding.ViewBinding
import eu.kanade.tachiyomi.ui.base.presenter.NucleusConductorDelegate
import eu.kanade.tachiyomi.ui.base.presenter.NucleusConductorLifecycleListener
import nucleus.factory.PresenterFactory
import nucleus.presenter.Presenter
@Suppress("LeakingThis")
abstract class NucleusController<VB : ViewBinding, P : Presenter<*>>(val bundle: Bundle? = null) :
BaseController<VB>(bundle),
PresenterFactory<P> {
private val delegate = NucleusConductorDelegate(this)
val presenter: P
get() = delegate.presenter!!
init {
addLifecycleListener(NucleusConductorLifecycleListener(delegate))
}
}

View file

@ -1,36 +0,0 @@
package eu.kanade.tachiyomi.ui.base.presenter
import android.os.Bundle
import eu.kanade.core.prefs.PreferenceMutableState
import eu.kanade.tachiyomi.core.preference.Preference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import nucleus.presenter.RxPresenter
open class BasePresenter<V> : RxPresenter<V>() {
var presenterScope: CoroutineScope = MainScope()
override fun onCreate(savedState: Bundle?) {
try {
super.onCreate(savedState)
} 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()
}
// We're trying to avoid using Rx, so we "undeprecate" this
@Suppress("DEPRECATION")
override fun getView(): V? {
return super.getView()
}
fun <T> Preference<T>.asState() = PreferenceMutableState(this, presenterScope)
}

View file

@ -1,46 +0,0 @@
package eu.kanade.tachiyomi.ui.base.presenter
import android.os.Bundle
import nucleus.factory.PresenterFactory
import nucleus.presenter.Presenter
class NucleusConductorDelegate<P : Presenter<*>>(private val factory: PresenterFactory<P>) {
var presenter: P? = null
get() {
if (field == null) {
field = factory.createPresenter()
field!!.create(bundle)
bundle = null
}
return field
}
private var bundle: Bundle? = null
fun onSaveInstanceState(): Bundle {
val bundle = Bundle()
// getPresenter(); // Workaround a crash related to saving instance state with child routers
presenter?.save(bundle)
return bundle
}
fun onRestoreInstanceState(presenterState: Bundle?) {
bundle = presenterState
}
@Suppress("UNCHECKED_CAST")
private fun <View> Presenter<View>.takeView(view: Any) = takeView(view as View)
fun onTakeView(view: Any) {
presenter?.takeView(view)
}
fun onDropView() {
presenter?.dropView()
}
fun onDestroy() {
presenter?.destroy()
}
}

View file

@ -1,32 +0,0 @@
package eu.kanade.tachiyomi.ui.base.presenter
import android.os.Bundle
import android.view.View
import com.bluelinelabs.conductor.Controller
class NucleusConductorLifecycleListener(private val delegate: NucleusConductorDelegate<*>) : Controller.LifecycleListener() {
override fun postCreateView(controller: Controller, view: View) {
delegate.onTakeView(controller)
}
override fun preDestroyView(controller: Controller, view: View) {
delegate.onDropView()
}
override fun preDestroy(controller: Controller) {
delegate.onDestroy()
}
override fun onSaveInstanceState(controller: Controller, outState: Bundle) {
outState.putBundle(PRESENTER_STATE_KEY, delegate.onSaveInstanceState())
}
override fun onRestoreInstanceState(controller: Controller, savedInstanceState: Bundle) {
delegate.onRestoreInstanceState(savedInstanceState.getBundle(PRESENTER_STATE_KEY))
}
companion object {
private const val PRESENTER_STATE_KEY = "presenter_state"
}
}

View file

@ -21,7 +21,6 @@ import androidx.core.view.isVisible
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceDialogController
import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler
@ -30,6 +29,7 @@ import com.bluelinelabs.conductor.RouterTransaction
import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
@ -84,6 +84,7 @@ class MainActivity : BaseActivity() {
private val sourcePreferences: SourcePreferences by injectLazy()
private val libraryPreferences: LibraryPreferences by injectLazy()
private val uiPreferences: UiPreferences by injectLazy()
private val preferences: BasePreferences by injectLazy()
lateinit var binding: MainActivityBinding
@ -565,7 +566,7 @@ class MainActivity : BaseActivity() {
// Then we'll assume the top controller is the parent controller of this dialog
val backstack = router.backstack
internalTo = backstack.lastOrNull()?.controller
if (internalTo is DialogController || internalTo is PreferenceDialogController) {
if (internalTo is DialogController) {
internalTo = backstack.getOrNull(backstack.size - 2)?.controller ?: return
}
} else {
@ -573,9 +574,6 @@ class MainActivity : BaseActivity() {
if (from is DialogController || internalTo is DialogController) {
return
}
if (from is PreferenceDialogController || internalTo is PreferenceDialogController) {
return
}
}
supportActionBar?.setDisplayHomeAsUpEnabled(router.backstackSize != 1)

View file

@ -44,13 +44,17 @@ import com.google.android.material.slider.Slider
import com.google.android.material.transition.platform.MaterialContainerTransform
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegateImpl
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.AddToLibraryFirst
@ -74,6 +78,7 @@ import eu.kanade.tachiyomi.util.system.getThemeColor
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.prepareTabletUiContext
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.copy
@ -87,6 +92,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import logcat.LogPriority
import nucleus.factory.RequiresPresenter
import nucleus.view.NucleusAppCompatActivity
import uy.kohesive.injekt.injectLazy
import kotlin.math.abs
import kotlin.math.max
@ -96,10 +102,12 @@ import kotlin.math.max
* viewers, to which calls from the presenter or UI events are delegated.
*/
@RequiresPresenter(ReaderPresenter::class)
class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
class ReaderActivity :
NucleusAppCompatActivity<ReaderPresenter>(),
SecureActivityDelegate by SecureActivityDelegateImpl(),
ThemingDelegate by ThemingDelegateImpl() {
companion object {
fun newIntent(context: Context, mangaId: Long?, chapterId: Long?): Intent {
return Intent(context, ReaderActivity::class.java).apply {
putExtra("manga", mangaId)
@ -116,6 +124,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
}
private val readerPreferences: ReaderPreferences by injectLazy()
private val preferences: BasePreferences by injectLazy()
lateinit var binding: ReaderActivityBinding
@ -155,10 +164,15 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
var isScrollingThroughPages = false
private set
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase.prepareTabletUiContext())
}
/**
* Called when the activity is created. Initializes the presenter and configuration.
*/
override fun onCreate(savedInstanceState: Bundle?) {
applyAppTheme(this)
registerSecureActivity(this)
// Setup shared element transitions

View file

@ -37,7 +37,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader
@ -60,17 +59,20 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.cacheImageDir
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import nucleus.presenter.RxPresenter
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.Date
import java.util.concurrent.TimeUnit
import eu.kanade.domain.manga.model.Manga as DomainManga
@ -82,6 +84,7 @@ class ReaderPresenter(
private val sourceManager: SourceManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val downloadProvider: DownloadProvider = Injekt.get(),
private val imageSaver: ImageSaver = Injekt.get(),
preferences: BasePreferences = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(),
private val readerPreferences: ReaderPreferences = Injekt.get(),
@ -95,7 +98,9 @@ class ReaderPresenter(
private val upsertHistory: UpsertHistory = Injekt.get(),
private val updateChapter: UpdateChapter = Injekt.get(),
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
) : BasePresenter<ReaderActivity>() {
) : RxPresenter<ReaderActivity>() {
private val coroutineScope: CoroutineScope = MainScope()
/**
* The manga loaded in the reader. It can be null when instantiated for a short time.
@ -133,8 +138,6 @@ class ReaderPresenter(
*/
private val isLoadingAdjacentChapterRelay = BehaviorRelay.create<Boolean>()
private val imageSaver: ImageSaver by injectLazy()
private var chapterToDownload: Download? = null
/**
@ -205,6 +208,7 @@ class ReaderPresenter(
*/
override fun onDestroy() {
super.onDestroy()
coroutineScope.cancel()
val currentChapters = viewerChaptersRelay.value
if (currentChapters != null) {
currentChapters.unref()
@ -242,7 +246,7 @@ class ReaderPresenter(
*/
fun onSaveInstanceStateNonConfigurationChange() {
val currentChapter = getCurrentChapter() ?: return
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
saveChapterProgress(currentChapter)
}
}
@ -261,7 +265,7 @@ class ReaderPresenter(
fun init(mangaId: Long, initialChapterId: Long) {
if (!needsInit()) return
presenterScope.launchIO {
coroutineScope.launchIO {
try {
val manga = getManga.await(mangaId)
withUIContext {
@ -467,7 +471,7 @@ class ReaderPresenter(
if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return
val nextChapter = viewerChaptersRelay.value?.nextChapter?.chapter ?: return
presenterScope.launchIO {
coroutineScope.launchIO {
val isNextChapterDownloaded = downloadManager.isChapterDownloaded(
nextChapter.name,
nextChapter.scanlator,
@ -523,7 +527,7 @@ class ReaderPresenter(
* Called when reader chapter is changed in reader or when activity is paused.
*/
private fun saveReadingProgress(readerChapter: ReaderChapter) {
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
saveChapterProgress(readerChapter)
saveChapterHistory(readerChapter)
}
@ -613,7 +617,7 @@ class ReaderPresenter(
fun bookmarkCurrentChapter(bookmarked: Boolean) {
val chapter = getCurrentChapter()?.chapter ?: return
chapter.bookmark = bookmarked // Otherwise the bookmark icon doesn't update
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
updateChapter.await(
ChapterUpdate(
id = chapter.id!!.toLong(),
@ -726,7 +730,7 @@ class ReaderPresenter(
// Copy file in background.
try {
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
val uri = imageSaver.save(
image = Image.Page(
inputStream = page.stream!!,
@ -762,7 +766,7 @@ class ReaderPresenter(
val filename = generateFilename(manga, page)
try {
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
destDir.deleteRecursively()
val uri = imageSaver.save(
image = Image.Page(
@ -788,7 +792,7 @@ class ReaderPresenter(
val manga = manga?.toDomainManga() ?: return
val stream = page.stream ?: return
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
try {
manga.editCover(context, stream())
withUIContext {
@ -834,7 +838,7 @@ class ReaderPresenter(
val trackManager = Injekt.get<TrackManager>()
val context = Injekt.get<Application>()
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
getTracks.await(manga.id!!)
.mapNotNull { track ->
val service = trackManager.getService(track.syncId)
@ -874,7 +878,7 @@ class ReaderPresenter(
if (!chapter.chapter.read) return
val manga = manga ?: return
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
downloadManager.enqueueChaptersToDelete(listOf(chapter.chapter.toDomainChapter()!!), manga.toDomainManga()!!)
}
}
@ -884,11 +888,17 @@ class ReaderPresenter(
* are ignored.
*/
private fun deletePendingChapters() {
presenterScope.launchNonCancellable {
coroutineScope.launchNonCancellable {
downloadManager.deletePendingChapters()
}
}
// We're trying to avoid using Rx, so we "undeprecate" this
@Suppress("DEPRECATION")
override fun getView(): ReaderActivity? {
return super.getView()
}
/**
* Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle
* subscription list.

View file

@ -3,7 +3,6 @@ aboutlib_version = "10.5.2"
okhttp_version = "5.0.0-alpha.10"
nucleus_version = "3.0.0"
coil_version = "2.2.2"
conductor_version = "3.1.8"
shizuku_version = "12.2.0"
sqldelight = "1.5.4"
leakcanary = "2.10"
@ -64,8 +63,7 @@ insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
cascade = "me.saket.cascade:cascade-compose:2.0.0-beta1"
wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11"
conductor-core = { module = "com.bluelinelabs:conductor", version.ref = "conductor_version" }
conductor-support-preference = { module = "com.github.tachiyomiorg:conductor-support-preference", version.ref = "conductor_version" }
conductor = "com.bluelinelabs:conductor:3.1.8"
flowbinding-android = "io.github.reactivecircus.flowbinding:flowbinding-android:1.2.0"
@ -100,7 +98,6 @@ js-engine = ["quickjs-android"]
sqlite = ["sqlitektx", "sqlite-android"]
nucleus = ["nucleus-core", "nucleus-supportv7"]
coil = ["coil-core", "coil-gif", "coil-compose"]
conductor = ["conductor-core", "conductor-support-preference"]
shizuku = ["shizuku-api", "shizuku-provider"]
voyager = ["voyager-navigator", "voyager-transitions"]