Replace reader's Presenter with ViewModel (#8698)
includes: * Use coroutines in more places * Use domain Manga data class and effectively changing the state system * Replace deprecated onBackPress method Co-authored-by: arkon <arkon@users.noreply.github.com> (cherry picked from commit f7a92cf6ac58cae26b09b02578318e12cd888f4c) # Conflicts: # .github/renovate.json # app/src/main/java/eu/kanade/domain/manga/model/Manga.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
This commit is contained in:
parent
3d8f3b34b7
commit
de6a5bf67b
@ -236,9 +236,6 @@ dependencies {
|
|||||||
// Preferences
|
// Preferences
|
||||||
implementation(libs.preferencektx)
|
implementation(libs.preferencektx)
|
||||||
|
|
||||||
// Model View Presenter
|
|
||||||
implementation(libs.bundles.nucleus)
|
|
||||||
|
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
implementation(libs.injekt.core)
|
implementation(libs.injekt.core)
|
||||||
|
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
package eu.kanade.domain.manga.model
|
package eu.kanade.domain.manga.model
|
||||||
|
|
||||||
import eu.kanade.data.listOfStringsAdapter
|
|
||||||
import eu.kanade.data.listOfStringsAndAdapter
|
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
|
||||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga as DbManga
|
|
||||||
|
|
||||||
data class Manga(
|
data class Manga(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
@ -83,6 +81,12 @@ data class Manga(
|
|||||||
val bookmarkedFilterRaw: Long
|
val bookmarkedFilterRaw: Long
|
||||||
get() = chapterFlags and CHAPTER_BOOKMARKED_MASK
|
get() = chapterFlags and CHAPTER_BOOKMARKED_MASK
|
||||||
|
|
||||||
|
val readingModeType: Long
|
||||||
|
get() = viewerFlags and ReadingModeType.MASK.toLong()
|
||||||
|
|
||||||
|
val orientationType: Long
|
||||||
|
get() = viewerFlags and OrientationType.MASK.toLong()
|
||||||
|
|
||||||
val unreadFilter: TriStateFilter
|
val unreadFilter: TriStateFilter
|
||||||
get() = when (unreadFilterRaw) {
|
get() = when (unreadFilterRaw) {
|
||||||
CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS
|
CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS
|
||||||
@ -240,33 +244,6 @@ fun TriStateFilter.toTriStateGroupState(): ExtendedNavigationView.Item.TriStateG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove when all deps are migrated
|
|
||||||
fun Manga.toDbManga(): DbManga = MangaImpl().also {
|
|
||||||
it.id = id
|
|
||||||
it.source = source
|
|
||||||
it.favorite = favorite
|
|
||||||
it.last_update = lastUpdate
|
|
||||||
it.date_added = dateAdded
|
|
||||||
it.viewer_flags = viewerFlags.toInt()
|
|
||||||
it.chapter_flags = chapterFlags.toInt()
|
|
||||||
it.cover_last_modified = coverLastModified
|
|
||||||
it.url = url
|
|
||||||
// SY -->
|
|
||||||
it.title = ogTitle
|
|
||||||
it.artist = ogArtist
|
|
||||||
it.author = ogAuthor
|
|
||||||
it.description = ogDescription
|
|
||||||
it.genre = ogGenre?.let(listOfStringsAdapter::encode)
|
|
||||||
it.status = ogStatus.toInt()
|
|
||||||
// SY <--
|
|
||||||
it.thumbnail_url = thumbnailUrl
|
|
||||||
it.update_strategy = updateStrategy
|
|
||||||
it.initialized = initialized
|
|
||||||
// SY -->
|
|
||||||
it.filtered_scanlators = filteredScanlators?.let(listOfStringsAndAdapter::encode)
|
|
||||||
// SY <--
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Manga.toMangaUpdate(): MangaUpdate {
|
fun Manga.toMangaUpdate(): MangaUpdate {
|
||||||
return MangaUpdate(
|
return MangaUpdate(
|
||||||
id = id,
|
id = id,
|
||||||
|
@ -34,7 +34,9 @@ import android.widget.FrameLayout
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.transition.doOnEnd
|
import androidx.core.transition.doOnEnd
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
@ -54,9 +56,9 @@ import com.google.android.material.transition.platform.MaterialContainerTransfor
|
|||||||
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
||||||
import dev.chrisbanes.insetter.applyInsetter
|
import dev.chrisbanes.insetter.applyInsetter
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.R
|
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.NotificationReceiver
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
|
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
|
||||||
@ -67,9 +69,9 @@ import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl
|
|||||||
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
|
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
|
||||||
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegateImpl
|
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegateImpl
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.AddToLibraryFirst
|
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.AddToLibraryFirst
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error
|
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Error
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
|
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Success
|
||||||
import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterDialog
|
import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterDialog
|
||||||
import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader
|
import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||||
@ -89,6 +91,8 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
|||||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.Constants
|
import eu.kanade.tachiyomi.util.Constants
|
||||||
|
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.preference.toggle
|
import eu.kanade.tachiyomi.util.preference.toggle
|
||||||
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
|
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
|
||||||
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
|
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
|
||||||
@ -115,16 +119,18 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.merge
|
import kotlinx.coroutines.flow.merge
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import nucleus.factory.RequiresPresenter
|
|
||||||
import nucleus.view.NucleusAppCompatActivity
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
@ -134,9 +140,8 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
* Activity containing the reader of Tachiyomi. This activity is mostly a container of the
|
* Activity containing the reader of Tachiyomi. This activity is mostly a container of the
|
||||||
* viewers, to which calls from the presenter or UI events are delegated.
|
* viewers, to which calls from the presenter or UI events are delegated.
|
||||||
*/
|
*/
|
||||||
@RequiresPresenter(ReaderPresenter::class)
|
|
||||||
class ReaderActivity :
|
class ReaderActivity :
|
||||||
NucleusAppCompatActivity<ReaderPresenter>(),
|
AppCompatActivity(),
|
||||||
SecureActivityDelegate by SecureActivityDelegateImpl(),
|
SecureActivityDelegate by SecureActivityDelegateImpl(),
|
||||||
ThemingDelegate by ThemingDelegateImpl() {
|
ThemingDelegate by ThemingDelegateImpl() {
|
||||||
|
|
||||||
@ -169,6 +174,8 @@ class ReaderActivity :
|
|||||||
|
|
||||||
lateinit var binding: ReaderActivityBinding
|
lateinit var binding: ReaderActivityBinding
|
||||||
|
|
||||||
|
val viewModel by viewModels<ReaderViewModel>()
|
||||||
|
|
||||||
val hasCutout by lazy { hasDisplayCutout() }
|
val hasCutout by lazy { hasDisplayCutout() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,7 +252,7 @@ class ReaderActivity :
|
|||||||
binding = ReaderActivityBinding.inflate(layoutInflater)
|
binding = ReaderActivityBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
if (presenter.needsInit()) {
|
if (viewModel.needsInit()) {
|
||||||
val manga = intent.extras!!.getLong("manga", -1)
|
val manga = intent.extras!!.getLong("manga", -1)
|
||||||
val chapter = intent.extras!!.getLong("chapter", -1)
|
val chapter = intent.extras!!.getLong("chapter", -1)
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -256,7 +263,16 @@ class ReaderActivity :
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
NotificationReceiver.dismissNotification(this, manga.hashCode(), Notifications.ID_NEW_CHAPTERS)
|
NotificationReceiver.dismissNotification(this, manga.hashCode(), Notifications.ID_NEW_CHAPTERS)
|
||||||
presenter.init(manga, chapter /* SY --> */, page/* SY <-- */)
|
|
||||||
|
lifecycleScope.launchNonCancellable {
|
||||||
|
val initResult = viewModel.init(manga, chapter/* SY --> */, page/* SY <-- */)
|
||||||
|
if (!initResult.getOrDefault(false)) {
|
||||||
|
val exception = initResult.exceptionOrNull() ?: IllegalStateException("Unknown err")
|
||||||
|
withUIContext {
|
||||||
|
setInitialChapterError(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
@ -279,6 +295,48 @@ class ReaderActivity :
|
|||||||
.drop(1)
|
.drop(1)
|
||||||
.onEach { if (!it) finish() }
|
.onEach { if (!it) finish() }
|
||||||
.launchIn(lifecycleScope)
|
.launchIn(lifecycleScope)
|
||||||
|
|
||||||
|
viewModel.state
|
||||||
|
.map { it.isLoadingAdjacentChapter }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.onEach(::setProgressDialog)
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
|
||||||
|
viewModel.state
|
||||||
|
.map { it.manga }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.filterNotNull()
|
||||||
|
.onEach(::setManga)
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
|
||||||
|
viewModel.state
|
||||||
|
.map { it.viewerChapters }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.filterNotNull()
|
||||||
|
.onEach(::setChapters)
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
|
||||||
|
viewModel.eventFlow
|
||||||
|
.onEach { event ->
|
||||||
|
when (event) {
|
||||||
|
ReaderViewModel.Event.ReloadViewerChapters -> {
|
||||||
|
viewModel.state.value.viewerChapters?.let(::setChapters)
|
||||||
|
}
|
||||||
|
is ReaderViewModel.Event.SetOrientation -> {
|
||||||
|
setOrientation(event.orientation)
|
||||||
|
}
|
||||||
|
is ReaderViewModel.Event.SavedImage -> {
|
||||||
|
onSaveImageResult(event.result)
|
||||||
|
}
|
||||||
|
is ReaderViewModel.Event.ShareImage -> {
|
||||||
|
onShareImageResult(event.uri, event.page /* SY --> */, event.secondPage /* SY <-- */)
|
||||||
|
}
|
||||||
|
is ReaderViewModel.Event.SetCoverResult -> {
|
||||||
|
onSetAsCoverResult(event.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -329,13 +387,13 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
if (!isChangingConfigurations) {
|
if (!isChangingConfigurations) {
|
||||||
presenter.onSaveInstanceStateNonConfigurationChange()
|
viewModel.onSaveInstanceStateNonConfigurationChange()
|
||||||
}
|
}
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
presenter.saveCurrentChapterReadingProgress()
|
viewModel.saveCurrentChapterReadingProgress()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +403,7 @@ class ReaderActivity :
|
|||||||
*/
|
*/
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
presenter.setReadStartTime()
|
viewModel.setReadStartTime()
|
||||||
setMenuVisibility(menuVisible, animate = false)
|
setMenuVisibility(menuVisible, animate = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,7 +424,7 @@ class ReaderActivity :
|
|||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.reader, menu)
|
menuInflater.inflate(R.menu.reader, menu)
|
||||||
|
|
||||||
/*val isChapterBookmarked = presenter?.getCurrentChapter()?.chapter?.bookmark ?: false
|
/*val isChapterBookmarked = viewModel.getCurrentChapter()?.chapter?.bookmark ?: false
|
||||||
menu.findItem(R.id.action_bookmark).isVisible = !isChapterBookmarked
|
menu.findItem(R.id.action_bookmark).isVisible = !isChapterBookmarked
|
||||||
menu.findItem(R.id.action_remove_bookmark).isVisible = isChapterBookmarked*/
|
menu.findItem(R.id.action_remove_bookmark).isVisible = isChapterBookmarked*/
|
||||||
|
|
||||||
@ -383,11 +441,11 @@ class ReaderActivity :
|
|||||||
openChapterInWebview()
|
openChapterInWebview()
|
||||||
}
|
}
|
||||||
R.id.action_bookmark -> {
|
R.id.action_bookmark -> {
|
||||||
presenter.bookmarkCurrentChapter(true)
|
viewModel.bookmarkCurrentChapter(true)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
R.id.action_remove_bookmark -> {
|
R.id.action_remove_bookmark -> {
|
||||||
presenter.bookmarkCurrentChapter(false)
|
viewModel.bookmarkCurrentChapter(false)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -398,17 +456,17 @@ class ReaderActivity :
|
|||||||
* Called when the user clicks the back key or the button on the toolbar. The call is
|
* Called when the user clicks the back key or the button on the toolbar. The call is
|
||||||
* delegated to the presenter.
|
* delegated to the presenter.
|
||||||
*/
|
*/
|
||||||
override fun onBackPressed() {
|
override fun finish() {
|
||||||
presenter.onBackPressed()
|
viewModel.onActivityFinish()
|
||||||
super.onBackPressed()
|
super.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
if (keyCode == KeyEvent.KEYCODE_N) {
|
if (keyCode == KeyEvent.KEYCODE_N) {
|
||||||
presenter.loadNextChapter()
|
loadNextChapter()
|
||||||
return true
|
return true
|
||||||
} else if (keyCode == KeyEvent.KEYCODE_P) {
|
} else if (keyCode == KeyEvent.KEYCODE_P) {
|
||||||
presenter.loadPreviousChapter()
|
loadPreviousChapter()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return super.onKeyUp(keyCode, event)
|
return super.onKeyUp(keyCode, event)
|
||||||
@ -475,7 +533,7 @@ class ReaderActivity :
|
|||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
binding.toolbar.setNavigationOnClickListener {
|
binding.toolbar.setNavigationOnClickListener {
|
||||||
onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.header.applyInsetter {
|
binding.header.applyInsetter {
|
||||||
@ -490,7 +548,7 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.toolbar.setOnClickListener {
|
binding.toolbar.setOnClickListener {
|
||||||
presenter.manga?.id?.let { id ->
|
viewModel.manga?.id?.let { id ->
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(this, MainActivity::class.java).apply {
|
Intent(this, MainActivity::class.java).apply {
|
||||||
action = MainActivity.SHORTCUT_MANGA
|
action = MainActivity.SHORTCUT_MANGA
|
||||||
@ -612,11 +670,11 @@ class ReaderActivity :
|
|||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
popupMenu(
|
popupMenu(
|
||||||
items = ReadingModeType.values().map { it.flagValue to it.stringRes },
|
items = ReadingModeType.values().map { it.flagValue to it.stringRes },
|
||||||
selectedItemId = presenter.getMangaReadingMode(resolveDefault = false),
|
selectedItemId = viewModel.getMangaReadingMode(resolveDefault = false),
|
||||||
) {
|
) {
|
||||||
val newReadingMode = ReadingModeType.fromPreference(itemId)
|
val newReadingMode = ReadingModeType.fromPreference(itemId)
|
||||||
|
|
||||||
presenter.setMangaReadingMode(newReadingMode.flagValue)
|
viewModel.setMangaReadingMode(newReadingMode.flagValue)
|
||||||
|
|
||||||
menuToggleToast?.cancel()
|
menuToggleToast?.cancel()
|
||||||
if (!readerPreferences.showReadingMode().get()) {
|
if (!readerPreferences.showReadingMode().get()) {
|
||||||
@ -634,7 +692,7 @@ class ReaderActivity :
|
|||||||
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
// SY -->
|
// SY -->
|
||||||
val mangaViewer = presenter.getMangaReadingMode()
|
val mangaViewer = viewModel.getMangaReadingMode()
|
||||||
// SY <--
|
// SY <--
|
||||||
val isPagerType = ReadingModeType.isPagerType(mangaViewer)
|
val isPagerType = ReadingModeType.isPagerType(mangaViewer)
|
||||||
val enabled = if (isPagerType) {
|
val enabled = if (isPagerType) {
|
||||||
@ -674,12 +732,12 @@ class ReaderActivity :
|
|||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
popupMenu(
|
popupMenu(
|
||||||
items = OrientationType.values().map { it.flagValue to it.stringRes },
|
items = OrientationType.values().map { it.flagValue to it.stringRes },
|
||||||
selectedItemId = presenter.manga?.orientationType
|
selectedItemId = viewModel.manga?.orientationType?.toInt()
|
||||||
?: readerPreferences.defaultOrientationType().get(),
|
?: readerPreferences.defaultOrientationType().get(),
|
||||||
) {
|
) {
|
||||||
val newOrientation = OrientationType.fromPreference(itemId)
|
val newOrientation = OrientationType.fromPreference(itemId)
|
||||||
|
|
||||||
presenter.setMangaOrientationType(newOrientation.flagValue)
|
viewModel.setMangaOrientationType(newOrientation.flagValue)
|
||||||
|
|
||||||
menuToggleToast?.cancel()
|
menuToggleToast?.cancel()
|
||||||
menuToggleToast = toast(newOrientation.stringRes)
|
menuToggleToast = toast(newOrientation.stringRes)
|
||||||
@ -804,9 +862,9 @@ class ReaderActivity :
|
|||||||
binding.ehRetryAll.setOnClickListener {
|
binding.ehRetryAll.setOnClickListener {
|
||||||
var retried = 0
|
var retried = 0
|
||||||
|
|
||||||
presenter.viewerChaptersRelay.value
|
viewModel.state.value.viewerChapters
|
||||||
.currChapter
|
?.currChapter
|
||||||
.pages
|
?.pages
|
||||||
?.forEachIndexed { _, page ->
|
?.forEachIndexed { _, page ->
|
||||||
var shouldQueuePage = false
|
var shouldQueuePage = false
|
||||||
if (page.status == Page.State.ERROR) {
|
if (page.status == Page.State.ERROR) {
|
||||||
@ -823,7 +881,7 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we are using EHentai/ExHentai, get a new image URL
|
// If we are using EHentai/ExHentai, get a new image URL
|
||||||
presenter.manga?.let { m ->
|
viewModel.manga?.let { m ->
|
||||||
val src = sourceManager.get(m.source)
|
val src = sourceManager.get(m.source)
|
||||||
if (src?.isEhBasedSource() == true) {
|
if (src?.isEhBasedSource() == true) {
|
||||||
page.imageUrl = null
|
page.imageUrl = null
|
||||||
@ -865,7 +923,7 @@ class ReaderActivity :
|
|||||||
} else if (curPage.status == Page.State.READY) {
|
} else if (curPage.status == Page.State.READY) {
|
||||||
toast(R.string.eh_boost_page_downloaded)
|
toast(R.string.eh_boost_page_downloaded)
|
||||||
} else {
|
} else {
|
||||||
val loader = (presenter.viewerChaptersRelay.value.currChapter.pageLoader as? HttpPageLoader)
|
val loader = (viewModel.state.value.viewerChapters?.currChapter?.pageLoader as? HttpPageLoader)
|
||||||
if (loader != null) {
|
if (loader != null) {
|
||||||
loader.boostPage(curPage)
|
loader.boostPage(curPage)
|
||||||
toast(R.string.eh_boost_boosted)
|
toast(R.string.eh_boost_boosted)
|
||||||
@ -886,7 +944,7 @@ class ReaderActivity :
|
|||||||
|
|
||||||
private fun exhCurrentpage(): ReaderPage? {
|
private fun exhCurrentpage(): ReaderPage? {
|
||||||
val currentPage = (((viewer as? PagerViewer)?.currentPage ?: (viewer as? WebtoonViewer)?.currentPage) as? ReaderPage)?.index
|
val currentPage = (((viewer as? PagerViewer)?.currentPage ?: (viewer as? WebtoonViewer)?.currentPage) as? ReaderPage)?.index
|
||||||
return currentPage?.let { presenter.viewerChaptersRelay.value.currChapter.pages?.getOrNull(it) }
|
return currentPage?.let { viewModel.state.value.viewerChapters?.currChapter?.pages?.getOrNull(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateBottomButtons() {
|
fun updateBottomButtons() {
|
||||||
@ -924,7 +982,7 @@ class ReaderActivity :
|
|||||||
} else {
|
} else {
|
||||||
pViewer.config.doublePages = doublePages
|
pViewer.config.doublePages = doublePages
|
||||||
}
|
}
|
||||||
val currentChapter = presenter.getCurrentChapter()
|
val currentChapter = viewModel.getCurrentChapter()
|
||||||
if (doublePages) {
|
if (doublePages) {
|
||||||
// If we're moving from singe to double, we want the current page to be the first page
|
// If we're moving from singe to double, we want the current page to be the first page
|
||||||
pViewer.config.shiftDoublePage = (
|
pViewer.config.shiftDoublePage = (
|
||||||
@ -932,7 +990,7 @@ class ReaderActivity :
|
|||||||
(currentChapter?.pages?.take(binding.pageSlider.value.floor())?.count { it.fullPage || it.isolatedPage } ?: 0)
|
(currentChapter?.pages?.take(binding.pageSlider.value.floor())?.count { it.fullPage || it.isolatedPage } ?: 0)
|
||||||
) % 2 != 0
|
) % 2 != 0
|
||||||
}
|
}
|
||||||
presenter.viewerChaptersRelay.value?.let {
|
viewModel.state.value.viewerChapters?.let {
|
||||||
pViewer.setChaptersDoubleShift(it)
|
pViewer.setChaptersDoubleShift(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -945,7 +1003,7 @@ class ReaderActivity :
|
|||||||
private fun shiftDoublePages() {
|
private fun shiftDoublePages() {
|
||||||
(viewer as? PagerViewer)?.config?.let { config ->
|
(viewer as? PagerViewer)?.config?.let { config ->
|
||||||
config.shiftDoublePage = !config.shiftDoublePage
|
config.shiftDoublePage = !config.shiftDoublePage
|
||||||
presenter.viewerChaptersRelay.value?.let {
|
viewModel.state.value.viewerChapters?.let {
|
||||||
(viewer as? PagerViewer)?.updateShifting()
|
(viewer as? PagerViewer)?.updateShifting()
|
||||||
(viewer as? PagerViewer)?.setChaptersDoubleShift(it)
|
(viewer as? PagerViewer)?.setChaptersDoubleShift(it)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
@ -960,7 +1018,7 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCropBordersShortcut() {
|
private fun updateCropBordersShortcut() {
|
||||||
val mangaViewer = presenter.getMangaReadingMode()
|
val mangaViewer = viewModel.getMangaReadingMode()
|
||||||
val isPagerType = ReadingModeType.isPagerType(mangaViewer)
|
val isPagerType = ReadingModeType.isPagerType(mangaViewer)
|
||||||
val enabled = if (isPagerType) {
|
val enabled = if (isPagerType) {
|
||||||
readerPreferences.cropBorders().get()
|
readerPreferences.cropBorders().get()
|
||||||
@ -1070,19 +1128,19 @@ class ReaderActivity :
|
|||||||
fun setManga(manga: Manga) {
|
fun setManga(manga: Manga) {
|
||||||
val prevViewer = viewer
|
val prevViewer = viewer
|
||||||
|
|
||||||
val viewerMode = ReadingModeType.fromPreference(presenter.getMangaReadingMode(resolveDefault = false))
|
val viewerMode = ReadingModeType.fromPreference(viewModel.getMangaReadingMode(resolveDefault = false))
|
||||||
binding.actionReadingMode.setImageResource(viewerMode.iconRes)
|
binding.actionReadingMode.setImageResource(viewerMode.iconRes)
|
||||||
|
|
||||||
val newViewer = ReadingModeType.toViewer(presenter.getMangaReadingMode(), this)
|
val newViewer = ReadingModeType.toViewer(viewModel.getMangaReadingMode(), this)
|
||||||
|
|
||||||
updateCropBordersShortcut()
|
updateCropBordersShortcut()
|
||||||
if (window.sharedElementEnterTransition is MaterialContainerTransform) {
|
if (window.sharedElementEnterTransition is MaterialContainerTransform) {
|
||||||
// Wait until transition is complete to avoid crash on API 26
|
// Wait until transition is complete to avoid crash on API 26
|
||||||
window.sharedElementEnterTransition.doOnEnd {
|
window.sharedElementEnterTransition.doOnEnd {
|
||||||
setOrientation(presenter.getMangaOrientationType())
|
setOrientation(viewModel.getMangaOrientationType())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setOrientation(presenter.getMangaOrientationType())
|
setOrientation(viewModel.getMangaOrientationType())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy previous viewer if there was one
|
// Destroy previous viewer if there was one
|
||||||
@ -1103,12 +1161,12 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
val defaultReaderType = manga.defaultReaderType(manga.mangaType(sourceName = sourceManager.get(manga.source)?.name))
|
val defaultReaderType = manga.defaultReaderType(manga.mangaType(sourceName = sourceManager.get(manga.source)?.name))
|
||||||
if (readerPreferences.useAutoWebtoon().get() && manga.readingModeType == ReadingModeType.DEFAULT.flagValue && defaultReaderType != null && defaultReaderType == ReadingModeType.WEBTOON.prefValue) {
|
if (readerPreferences.useAutoWebtoon().get() && manga.readingModeType.toInt() == ReadingModeType.DEFAULT.flagValue && defaultReaderType != null && defaultReaderType == ReadingModeType.WEBTOON.prefValue) {
|
||||||
readingModeToast?.cancel()
|
readingModeToast?.cancel()
|
||||||
readingModeToast = toast(resources.getString(R.string.eh_auto_webtoon_snack))
|
readingModeToast = toast(resources.getString(R.string.eh_auto_webtoon_snack))
|
||||||
} else if (readerPreferences.showReadingMode().get()) {
|
} else if (readerPreferences.showReadingMode().get()) {
|
||||||
// SY <--
|
// SY <--
|
||||||
showReadingModeToast(presenter.getMangaReadingMode())
|
showReadingModeToast(viewModel.getMangaReadingMode())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -1171,9 +1229,9 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openChapterInWebview() {
|
private fun openChapterInWebview() {
|
||||||
val manga = presenter.manga ?: return
|
val manga = viewModel.manga ?: return
|
||||||
val source = presenter.getSource() ?: return
|
val source = viewModel.getSource() ?: return
|
||||||
val url = presenter.getChapterUrl() ?: return
|
val url = viewModel.getChapterUrl() ?: return
|
||||||
|
|
||||||
val intent = WebViewActivity.newIntent(this, url, source.id, manga.title)
|
val intent = WebViewActivity.newIntent(this, url, source.id, manga.title)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -1194,7 +1252,7 @@ class ReaderActivity :
|
|||||||
* method to the current viewer, but also set the subtitle on the toolbar, and
|
* method to the current viewer, but also set the subtitle on the toolbar, and
|
||||||
* hides or disables the reader prev/next buttons if there's a prev or next chapter
|
* hides or disables the reader prev/next buttons if there's a prev or next chapter
|
||||||
*/
|
*/
|
||||||
fun setChapters(viewerChapters: ViewerChapters) {
|
private fun setChapters(viewerChapters: ViewerChapters) {
|
||||||
binding.readerContainer.removeView(loadingIndicator)
|
binding.readerContainer.removeView(loadingIndicator)
|
||||||
// SY -->
|
// SY -->
|
||||||
if (indexChapterToShift != null && indexPageToShift != null) {
|
if (indexChapterToShift != null && indexPageToShift != null) {
|
||||||
@ -1280,7 +1338,7 @@ class ReaderActivity :
|
|||||||
*/
|
*/
|
||||||
fun moveToPageIndex(index: Int) {
|
fun moveToPageIndex(index: Int) {
|
||||||
val viewer = viewer ?: return
|
val viewer = viewer ?: return
|
||||||
val currentChapter = presenter.getCurrentChapter() ?: return
|
val currentChapter = viewModel.getCurrentChapter() ?: return
|
||||||
val page = currentChapter.pages?.getOrNull(index) ?: return
|
val page = currentChapter.pages?.getOrNull(index) ?: return
|
||||||
viewer.moveToPage(page)
|
viewer.moveToPage(page)
|
||||||
}
|
}
|
||||||
@ -1290,7 +1348,10 @@ class ReaderActivity :
|
|||||||
* should be automatically shown.
|
* should be automatically shown.
|
||||||
*/
|
*/
|
||||||
private fun loadNextChapter() {
|
private fun loadNextChapter() {
|
||||||
presenter.loadNextChapter()
|
lifecycleScope.launch {
|
||||||
|
viewModel.loadNextChapter()
|
||||||
|
moveToPageIndex(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1298,7 +1359,10 @@ class ReaderActivity :
|
|||||||
* should be automatically shown.
|
* should be automatically shown.
|
||||||
*/
|
*/
|
||||||
private fun loadPreviousChapter() {
|
private fun loadPreviousChapter() {
|
||||||
presenter.loadPreviousChapter()
|
lifecycleScope.launch {
|
||||||
|
viewModel.loadPreviousChapter()
|
||||||
|
moveToPageIndex(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1307,7 +1371,7 @@ class ReaderActivity :
|
|||||||
*/
|
*/
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) {
|
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) {
|
||||||
val newChapter = presenter.onPageSelected(page, hasExtraPage)
|
val newChapter = viewModel.onPageSelected(page, hasExtraPage)
|
||||||
val pages = page.chapter.pages ?: return
|
val pages = page.chapter.pages ?: return
|
||||||
|
|
||||||
val currentPage = if (hasExtraPage) {
|
val currentPage = if (hasExtraPage) {
|
||||||
@ -1372,7 +1436,7 @@ class ReaderActivity :
|
|||||||
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
||||||
*/
|
*/
|
||||||
fun requestPreloadChapter(chapter: ReaderChapter) {
|
fun requestPreloadChapter(chapter: ReaderChapter) {
|
||||||
presenter.preloadChapter(chapter)
|
lifecycleScope.launch { viewModel.preloadChapter(chapter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1406,12 +1470,12 @@ class ReaderActivity :
|
|||||||
* will call [onShareImageResult] with the path the image was saved on when it's ready.
|
* will call [onShareImageResult] with the path the image was saved on when it's ready.
|
||||||
*/
|
*/
|
||||||
fun shareImage(page: ReaderPage) {
|
fun shareImage(page: ReaderPage) {
|
||||||
presenter.shareImage(page)
|
viewModel.shareImage(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun shareImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) {
|
fun shareImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) {
|
||||||
presenter.shareImages(firstPage, secondPage, isLTR, bg)
|
viewModel.shareImages(firstPage, secondPage, isLTR, bg)
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
@ -1420,7 +1484,7 @@ class ReaderActivity :
|
|||||||
* sharing tool.
|
* sharing tool.
|
||||||
*/
|
*/
|
||||||
fun onShareImageResult(uri: Uri, page: ReaderPage /* SY --> */, secondPage: ReaderPage? = null /* SY <-- */) {
|
fun onShareImageResult(uri: Uri, page: ReaderPage /* SY --> */, secondPage: ReaderPage? = null /* SY <-- */) {
|
||||||
val manga = presenter.manga ?: return
|
val manga = viewModel.manga ?: return
|
||||||
val chapter = page.chapter.chapter
|
val chapter = page.chapter.chapter
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -1443,12 +1507,12 @@ class ReaderActivity :
|
|||||||
* storage to the presenter.
|
* storage to the presenter.
|
||||||
*/
|
*/
|
||||||
fun saveImage(page: ReaderPage) {
|
fun saveImage(page: ReaderPage) {
|
||||||
presenter.saveImage(page)
|
viewModel.saveImage(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun saveImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) {
|
fun saveImages(firstPage: ReaderPage, secondPage: ReaderPage, isLTR: Boolean, @ColorInt bg: Int) {
|
||||||
presenter.saveImages(firstPage, secondPage, isLTR, bg)
|
viewModel.saveImages(firstPage, secondPage, isLTR, bg)
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
@ -1456,12 +1520,12 @@ class ReaderActivity :
|
|||||||
* Called from the presenter when a page is saved or fails. It shows a message or logs the
|
* Called from the presenter when a page is saved or fails. It shows a message or logs the
|
||||||
* event depending on the [result].
|
* event depending on the [result].
|
||||||
*/
|
*/
|
||||||
fun onSaveImageResult(result: ReaderPresenter.SaveImageResult) {
|
private fun onSaveImageResult(result: ReaderViewModel.SaveImageResult) {
|
||||||
when (result) {
|
when (result) {
|
||||||
is ReaderPresenter.SaveImageResult.Success -> {
|
is ReaderViewModel.SaveImageResult.Success -> {
|
||||||
toast(R.string.picture_saved)
|
toast(R.string.picture_saved)
|
||||||
}
|
}
|
||||||
is ReaderPresenter.SaveImageResult.Error -> {
|
is ReaderViewModel.SaveImageResult.Error -> {
|
||||||
logcat(LogPriority.ERROR, result.error)
|
logcat(LogPriority.ERROR, result.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1472,14 +1536,14 @@ class ReaderActivity :
|
|||||||
* cover to the presenter.
|
* cover to the presenter.
|
||||||
*/
|
*/
|
||||||
fun setAsCover(page: ReaderPage) {
|
fun setAsCover(page: ReaderPage) {
|
||||||
presenter.setAsCover(this, page)
|
viewModel.setAsCover(this, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the presenter when a page is set as cover or fails. It shows a different message
|
* Called from the presenter when a page is set as cover or fails. It shows a different message
|
||||||
* depending on the [result].
|
* depending on the [result].
|
||||||
*/
|
*/
|
||||||
fun onSetAsCoverResult(result: ReaderPresenter.SetAsCoverResult) {
|
private fun onSetAsCoverResult(result: ReaderViewModel.SetAsCoverResult) {
|
||||||
toast(
|
toast(
|
||||||
when (result) {
|
when (result) {
|
||||||
Success -> R.string.cover_updated
|
Success -> R.string.cover_updated
|
||||||
@ -1492,12 +1556,12 @@ class ReaderActivity :
|
|||||||
/**
|
/**
|
||||||
* Forces the user preferred [orientation] on the activity.
|
* Forces the user preferred [orientation] on the activity.
|
||||||
*/
|
*/
|
||||||
fun setOrientation(orientation: Int) {
|
private fun setOrientation(orientation: Int) {
|
||||||
val newOrientation = OrientationType.fromPreference(orientation)
|
val newOrientation = OrientationType.fromPreference(orientation)
|
||||||
if (newOrientation.flag != requestedOrientation) {
|
if (newOrientation.flag != requestedOrientation) {
|
||||||
requestedOrientation = newOrientation.flag
|
requestedOrientation = newOrientation.flag
|
||||||
}
|
}
|
||||||
updateOrientationShortcut(presenter.getMangaOrientationType(resolveDefault = false))
|
updateOrientationShortcut(viewModel.getMangaOrientationType(resolveDefault = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,13 +3,16 @@ package eu.kanade.tachiyomi.ui.reader
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import eu.kanade.core.util.asFlow
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
|
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
|
||||||
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
||||||
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
import eu.kanade.domain.chapter.model.ChapterUpdate
|
import eu.kanade.domain.chapter.model.ChapterUpdate
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.download.service.DownloadPreferences
|
import eu.kanade.domain.download.service.DownloadPreferences
|
||||||
@ -21,8 +24,8 @@ import eu.kanade.domain.manga.interactor.GetManga
|
|||||||
import eu.kanade.domain.manga.interactor.GetMergedManga
|
import eu.kanade.domain.manga.interactor.GetMergedManga
|
||||||
import eu.kanade.domain.manga.interactor.GetMergedReferencesById
|
import eu.kanade.domain.manga.interactor.GetMergedReferencesById
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.isLocal
|
import eu.kanade.domain.manga.model.isLocal
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
import eu.kanade.domain.track.interactor.InsertTrack
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
@ -30,9 +33,7 @@ import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
|
|||||||
import eu.kanade.domain.track.service.TrackPreferences
|
import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
@ -63,6 +64,7 @@ import eu.kanade.tachiyomi.util.lang.byteSize
|
|||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||||
import eu.kanade.tachiyomi.util.lang.takeBytes
|
import eu.kanade.tachiyomi.util.lang.takeBytes
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
||||||
@ -77,36 +79,38 @@ import exh.source.getMainSource
|
|||||||
import exh.source.isEhBasedManga
|
import exh.source.isEhBasedManga
|
||||||
import exh.util.defaultReaderType
|
import exh.util.defaultReaderType
|
||||||
import exh.util.mangaType
|
import exh.util.mangaType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.MainScope
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import nucleus.presenter.RxPresenter
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.schedulers.Schedulers
|
|
||||||
import tachiyomi.decoder.ImageDecoder
|
import tachiyomi.decoder.ImageDecoder
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.DecimalFormatSymbols
|
import java.text.DecimalFormatSymbols
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import eu.kanade.domain.chapter.model.Chapter as DomainChapter
|
|
||||||
import eu.kanade.domain.manga.model.Manga as DomainManga
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presenter used by the activity to perform background operations.
|
* Presenter used by the activity to perform background operations.
|
||||||
*/
|
*/
|
||||||
class ReaderPresenter(
|
class ReaderViewModel(
|
||||||
|
private val savedState: SavedStateHandle = SavedStateHandle(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val downloadProvider: DownloadProvider = Injekt.get(),
|
private val downloadProvider: DownloadProvider = Injekt.get(),
|
||||||
@ -131,27 +135,28 @@ class ReaderPresenter(
|
|||||||
private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(),
|
private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(),
|
||||||
private val getMergedChapterByMangaId: GetMergedChapterByMangaId = Injekt.get(),
|
private val getMergedChapterByMangaId: GetMergedChapterByMangaId = Injekt.get(),
|
||||||
// SY <--
|
// SY <--
|
||||||
) : RxPresenter<ReaderActivity>() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val coroutineScope: CoroutineScope = MainScope()
|
private val mutableState = MutableStateFlow(State())
|
||||||
|
val state = mutableState.asStateFlow()
|
||||||
|
|
||||||
|
private val eventChannel = Channel<Event>()
|
||||||
|
val eventFlow = eventChannel.receiveAsFlow()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The manga loaded in the reader. It can be null when instantiated for a short time.
|
* The manga loaded in the reader. It can be null when instantiated for a short time.
|
||||||
*/
|
*/
|
||||||
var manga: Manga? = null
|
val manga: Manga?
|
||||||
private set
|
get() = state.value.manga
|
||||||
|
|
||||||
// SY -->
|
|
||||||
var meta: RaisedSearchMetadata? = null
|
|
||||||
private set
|
|
||||||
var mergedManga: Map<Long, DomainManga>? = null
|
|
||||||
private set
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The chapter id of the currently loaded chapter. Used to restore from process kill.
|
* The chapter id of the currently loaded chapter. Used to restore from process kill.
|
||||||
*/
|
*/
|
||||||
private var chapterId = -1L
|
private var chapterId = savedState.get<Long>("chapter_id") ?: -1L
|
||||||
|
set(value) {
|
||||||
|
savedState["chapter_id"] = value
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The chapter loader for the loaded manga. It'll be null until [manga] is set.
|
* The chapter loader for the loaded manga. It'll be null until [manga] is set.
|
||||||
@ -168,17 +173,6 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
private var activeChapterSubscription: Subscription? = null
|
private var activeChapterSubscription: Subscription? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* Relay for currently active viewer chapters.
|
|
||||||
*/
|
|
||||||
/* [EXH] private */
|
|
||||||
val viewerChaptersRelay = BehaviorRelay.create<ViewerChapters>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used when loading prev/next chapter needed to lock the UI (with a dialog).
|
|
||||||
*/
|
|
||||||
private val isLoadingAdjacentChapterEvent = Channel<Boolean>()
|
|
||||||
|
|
||||||
private var chapterToDownload: Download? = null
|
private var chapterToDownload: Download? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,7 +180,7 @@ class ReaderPresenter(
|
|||||||
* time in a background thread to avoid blocking the UI.
|
* time in a background thread to avoid blocking the UI.
|
||||||
*/
|
*/
|
||||||
private val chapterList by lazy {
|
private val chapterList by lazy {
|
||||||
val manga = manga!!.toDomainManga()!!
|
val manga = manga!!
|
||||||
val chapters = runBlocking {
|
val chapters = runBlocking {
|
||||||
/* SY --> */ if (manga.source == MERGED_SOURCE_ID) {
|
/* SY --> */ if (manga.source == MERGED_SOURCE_ID) {
|
||||||
getMergedChapterByMangaId.await(manga.id)
|
getMergedChapterByMangaId.await(manga.id)
|
||||||
@ -204,12 +198,12 @@ class ReaderPresenter(
|
|||||||
when {
|
when {
|
||||||
readerPreferences.skipRead().get() && it.read -> true
|
readerPreferences.skipRead().get() && it.read -> true
|
||||||
readerPreferences.skipFiltered().get() -> {
|
readerPreferences.skipFiltered().get() -> {
|
||||||
(manga.unreadFilterRaw == DomainManga.CHAPTER_SHOW_READ && !it.read) ||
|
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_READ && !it.read) ||
|
||||||
(manga.unreadFilterRaw == DomainManga.CHAPTER_SHOW_UNREAD && it.read) ||
|
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_UNREAD && it.read) ||
|
||||||
(manga.downloadedFilterRaw == DomainManga.CHAPTER_SHOW_DOWNLOADED && !downloadManager.isChapterDownloaded(it.name, it.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source)) ||
|
(manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED && !downloadManager.isChapterDownloaded(it.name, it.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source)) ||
|
||||||
(manga.downloadedFilterRaw == DomainManga.CHAPTER_SHOW_NOT_DOWNLOADED && downloadManager.isChapterDownloaded(it.name, it.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source)) ||
|
(manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED && downloadManager.isChapterDownloaded(it.name, it.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source)) ||
|
||||||
(manga.bookmarkedFilterRaw == DomainManga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
||||||
(manga.bookmarkedFilterRaw == DomainManga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) ||
|
||||||
// SY -->
|
// SY -->
|
||||||
(manga.filteredScanlators != null && MdUtil.getScanlators(it.scanlator).none { group -> manga.filteredScanlators.contains(group) })
|
(manga.filteredScanlators != null && MdUtil.getScanlators(it.scanlator).none { group -> manga.filteredScanlators.contains(group) })
|
||||||
// SY <--
|
// SY <--
|
||||||
@ -234,32 +228,15 @@ class ReaderPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var hasTrackers: Boolean = false
|
private var hasTrackers: Boolean = false
|
||||||
private val checkTrackers: (DomainManga) -> Unit = { manga ->
|
private val checkTrackers: (Manga) -> Unit = { manga ->
|
||||||
val tracks = runBlocking { getTracks.await(manga.id) }
|
val tracks = runBlocking { getTracks.await(manga.id) }
|
||||||
hasTrackers = tracks.isNotEmpty()
|
hasTrackers = tracks.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val incognitoMode = preferences.incognitoMode().get()
|
private val incognitoMode = preferences.incognitoMode().get()
|
||||||
|
|
||||||
/**
|
override fun onCleared() {
|
||||||
* Called when the presenter is created. It retrieves the saved active chapter if the process
|
val currentChapters = state.value.viewerChapters
|
||||||
* was restored.
|
|
||||||
*/
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
|
||||||
super.onCreate(savedState)
|
|
||||||
if (savedState != null) {
|
|
||||||
chapterId = savedState.getLong(::chapterId.name, -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the presenter is destroyed. It saves the current progress and cleans up
|
|
||||||
* references on the currently active chapters.
|
|
||||||
*/
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
coroutineScope.cancel()
|
|
||||||
val currentChapters = viewerChaptersRelay.value
|
|
||||||
if (currentChapters != null) {
|
if (currentChapters != null) {
|
||||||
currentChapters.unref()
|
currentChapters.unref()
|
||||||
saveReadingProgress(currentChapters.currChapter)
|
saveReadingProgress(currentChapters.currChapter)
|
||||||
@ -269,24 +246,24 @@ class ReaderPresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
init {
|
||||||
* Called when the presenter instance is being saved. It saves the currently active chapter
|
// To save state
|
||||||
* id and the last page read.
|
state.map { it.viewerChapters?.currChapter }
|
||||||
*/
|
.distinctUntilChanged()
|
||||||
override fun onSave(state: Bundle) {
|
.onEach { currentChapter ->
|
||||||
super.onSave(state)
|
|
||||||
val currentChapter = getCurrentChapter()
|
|
||||||
if (currentChapter != null) {
|
if (currentChapter != null) {
|
||||||
currentChapter.requestedPage = currentChapter.chapter.last_page_read
|
currentChapter.requestedPage = currentChapter.chapter.last_page_read
|
||||||
state.putLong(::chapterId.name, currentChapter.chapter.id!!)
|
chapterId = currentChapter.chapter.id!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user pressed the back button and is going to leave the reader. Used to
|
* Called when the user pressed the back button and is going to leave the reader. Used to
|
||||||
* trigger deletion of the downloaded chapters.
|
* trigger deletion of the downloaded chapters.
|
||||||
*/
|
*/
|
||||||
fun onBackPressed() {
|
fun onActivityFinish() {
|
||||||
deletePendingChapters()
|
deletePendingChapters()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +273,7 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
fun onSaveInstanceStateNonConfigurationChange() {
|
fun onSaveInstanceStateNonConfigurationChange() {
|
||||||
val currentChapter = getCurrentChapter() ?: return
|
val currentChapter = getCurrentChapter() ?: return
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
saveChapterProgress(currentChapter)
|
saveChapterProgress(currentChapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,71 +289,44 @@ class ReaderPresenter(
|
|||||||
* Initializes this presenter with the given [mangaId] and [initialChapterId]. This method will
|
* Initializes this presenter with the given [mangaId] and [initialChapterId]. This method will
|
||||||
* fetch the manga from the database and initialize the initial chapter.
|
* fetch the manga from the database and initialize the initial chapter.
|
||||||
*/
|
*/
|
||||||
fun init(mangaId: Long, initialChapterId: Long /* SY --> */, page: Int?/* SY <-- */) {
|
suspend fun init(mangaId: Long, initialChapterId: Long /* SY --> */, page: Int?/* SY <-- */): Result<Boolean> {
|
||||||
if (!needsInit()) return
|
if (!needsInit()) return Result.success(true)
|
||||||
|
return withIOContext {
|
||||||
coroutineScope.launchIO {
|
|
||||||
try {
|
try {
|
||||||
|
val manga = getManga.await(mangaId)
|
||||||
|
if (manga != null) {
|
||||||
// SY -->
|
// SY -->
|
||||||
val manga = getManga.await(mangaId) ?: return@launchIO
|
val source = sourceManager.getOrStub(manga.source)
|
||||||
val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>()
|
val metadataSource = source.getMainSource<MetadataSource<*, *>>()
|
||||||
val metadata = if (source != null) {
|
val metadata = if (metadataSource != null) {
|
||||||
getFlatMetadataById.await(mangaId)?.raise(source.metaClass)
|
getFlatMetadataById.await(mangaId)?.raise(metadataSource.metaClass)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
withUIContext {
|
val mergedReferences = if (source is MergedSource) runBlocking { getMergedReferencesById.await(manga.id) } else emptyList()
|
||||||
init(manga.toDbManga(), initialChapterId, metadata, page)
|
val mergedManga = if (source is MergedSource) runBlocking { getMergedManga.await() /* <-- TODO */ }.associateBy { it.id } else emptyMap()
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
view?.setInitialChapterError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this presenter with the given [manga] and [initialChapterId]. This method will
|
|
||||||
* set the chapter loader, view subscriptions and trigger an initial load.
|
|
||||||
*/
|
|
||||||
private fun init(manga: Manga, initialChapterId: Long /* SY --> */, metadata: RaisedSearchMetadata?, page: Int?/* SY <-- */) {
|
|
||||||
if (!needsInit()) return
|
|
||||||
|
|
||||||
this.manga = manga
|
|
||||||
// SY -->
|
|
||||||
this.meta = metadata
|
|
||||||
// SY <--
|
// SY <--
|
||||||
|
mutableState.update { it.copy(manga = manga /* SY --> */, meta = metadata, mergedManga = mergedManga/* SY <-- */) }
|
||||||
if (chapterId == -1L) chapterId = initialChapterId
|
if (chapterId == -1L) chapterId = initialChapterId
|
||||||
|
|
||||||
checkTrackers(manga.toDomainManga()!!)
|
checkTrackers(manga)
|
||||||
|
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
val source = sourceManager.getOrStub(manga.source)
|
// val source = sourceManager.getOrStub(manga.source)
|
||||||
val mergedReferences = if (source is MergedSource) runBlocking { getMergedReferencesById.await(manga.id!!) } else emptyList()
|
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source, /* SY --> */sourceManager, mergedReferences, mergedManga/* SY <-- */)
|
||||||
mergedManga = if (source is MergedSource) runBlocking { getMergedManga.await() }.associateBy { it.id } else emptyMap()
|
|
||||||
loader = ChapterLoader(context, downloadManager, downloadProvider, manga.toDomainManga()!!, source, sourceManager, mergedReferences, mergedManga ?: emptyMap())
|
|
||||||
|
|
||||||
Observable.just(manga).subscribeLatestCache(ReaderActivity::setManga)
|
getLoadObservable(loader!!, chapterList.first { chapterId == it.chapter.id } /* SY --> */, page/* SY <-- */)
|
||||||
viewerChaptersRelay.subscribeLatestCache(ReaderActivity::setChapters)
|
.asFlow()
|
||||||
coroutineScope.launch {
|
.first()
|
||||||
isLoadingAdjacentChapterEvent.receiveAsFlow().collectLatest {
|
Result.success(true)
|
||||||
view?.setProgressDialog(it)
|
} else {
|
||||||
|
// Unlikely but okay
|
||||||
|
Result.success(false)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read chapterList from an io thread because it's retrieved lazily and would block main.
|
|
||||||
activeChapterSubscription?.unsubscribe()
|
|
||||||
activeChapterSubscription = Observable
|
|
||||||
.fromCallable { chapterList.first { chapterId == it.chapter.id } }
|
|
||||||
.flatMap { getLoadObservable(loader!!, it /* SY --> */, page/* SY <-- */) }
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribeFirst(
|
|
||||||
{ _, _ ->
|
|
||||||
// Ignore onNext event
|
|
||||||
},
|
|
||||||
ReaderActivity::setInitialChapterError,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -391,7 +341,7 @@ class ReaderPresenter(
|
|||||||
return chapterList.map {
|
return chapterList.map {
|
||||||
ReaderChapterItem(
|
ReaderChapterItem(
|
||||||
it.chapter.toDomainChapter()!!,
|
it.chapter.toDomainChapter()!!,
|
||||||
manga!!.toDomainManga()!!,
|
manga!!,
|
||||||
it.chapter.id == currentChapter?.chapter?.id,
|
it.chapter.id == currentChapter?.chapter?.id,
|
||||||
context,
|
context,
|
||||||
UiPreferences.dateFormat(uiPreferences.dateFormat().get()),
|
UiPreferences.dateFormat(uiPreferences.dateFormat().get()),
|
||||||
@ -429,14 +379,14 @@ class ReaderPresenter(
|
|||||||
)
|
)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnNext { newChapters ->
|
.doOnNext { newChapters ->
|
||||||
val oldChapters = viewerChaptersRelay.value
|
mutableState.update {
|
||||||
|
|
||||||
// Add new references first to avoid unnecessary recycling
|
// Add new references first to avoid unnecessary recycling
|
||||||
newChapters.ref()
|
newChapters.ref()
|
||||||
oldChapters?.unref()
|
it.viewerChapters?.unref()
|
||||||
|
|
||||||
chapterToDownload = cancelQueuedDownloads(newChapters.currChapter)
|
chapterToDownload = cancelQueuedDownloads(newChapters.currChapter)
|
||||||
viewerChaptersRelay.call(newChapters)
|
it.copy(viewerChapters = newChapters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,20 +394,20 @@ class ReaderPresenter(
|
|||||||
* Called when the user changed to the given [chapter] when changing pages from the viewer.
|
* Called when the user changed to the given [chapter] when changing pages from the viewer.
|
||||||
* It's used only to set this chapter as active.
|
* It's used only to set this chapter as active.
|
||||||
*/
|
*/
|
||||||
private fun loadNewChapter(chapter: ReaderChapter) {
|
private suspend fun loadNewChapter(chapter: ReaderChapter) {
|
||||||
val loader = loader ?: return
|
val loader = loader ?: return
|
||||||
|
|
||||||
logcat { "Loading ${chapter.chapter.url}" }
|
logcat { "Loading ${chapter.chapter.url}" }
|
||||||
|
|
||||||
activeChapterSubscription?.unsubscribe()
|
withIOContext {
|
||||||
activeChapterSubscription = getLoadObservable(loader, chapter)
|
getLoadObservable(loader, chapter)
|
||||||
.toCompletable()
|
.asFlow()
|
||||||
.onErrorComplete()
|
.catch { logcat(LogPriority.ERROR, it) }
|
||||||
.subscribe()
|
.first()
|
||||||
.also(::add)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadNewChapterFromDialog(chapter: DomainChapter) {
|
suspend fun loadNewChapterFromDialog(chapter: Chapter) {
|
||||||
val newChapter = chapterList.firstOrNull { it.chapter.id == chapter.id } ?: return
|
val newChapter = chapterList.firstOrNull { it.chapter.id == chapter.id } ?: return
|
||||||
loadAdjacent(newChapter)
|
loadAdjacent(newChapter)
|
||||||
}
|
}
|
||||||
@ -467,37 +417,32 @@ class ReaderPresenter(
|
|||||||
* sets the [isLoadingAdjacentChapterRelay] that the view uses to prevent any further
|
* sets the [isLoadingAdjacentChapterRelay] that the view uses to prevent any further
|
||||||
* interaction until the chapter is loaded.
|
* interaction until the chapter is loaded.
|
||||||
*/
|
*/
|
||||||
private fun loadAdjacent(chapter: ReaderChapter) {
|
private suspend fun loadAdjacent(chapter: ReaderChapter) {
|
||||||
val loader = loader ?: return
|
val loader = loader ?: return
|
||||||
|
|
||||||
logcat { "Loading adjacent ${chapter.chapter.url}" }
|
logcat { "Loading adjacent ${chapter.chapter.url}" }
|
||||||
|
|
||||||
activeChapterSubscription?.unsubscribe()
|
mutableState.update { it.copy(isLoadingAdjacentChapter = true) }
|
||||||
activeChapterSubscription = getLoadObservable(loader, chapter)
|
withIOContext {
|
||||||
.doOnSubscribe { coroutineScope.launch { isLoadingAdjacentChapterEvent.send(true) } }
|
getLoadObservable(loader, chapter)
|
||||||
.doOnUnsubscribe { coroutineScope.launch { isLoadingAdjacentChapterEvent.send(false) } }
|
.asFlow()
|
||||||
.subscribeFirst(
|
.first()
|
||||||
{ view, _ ->
|
}
|
||||||
view.moveToPageIndex(0)
|
mutableState.update { it.copy(isLoadingAdjacentChapter = false) }
|
||||||
},
|
|
||||||
{ _, _ ->
|
|
||||||
// Ignore onError event, viewers handle that state
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so
|
* Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so
|
||||||
* that the user doesn't have to wait too long to continue reading.
|
* that the user doesn't have to wait too long to continue reading.
|
||||||
*/
|
*/
|
||||||
private fun preload(chapter: ReaderChapter) {
|
private suspend fun preload(chapter: ReaderChapter) {
|
||||||
if (chapter.pageLoader is HttpPageLoader) {
|
if (chapter.pageLoader is HttpPageLoader) {
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
val dbChapter = chapter.chapter
|
val dbChapter = chapter.chapter
|
||||||
val isDownloaded = downloadManager.isChapterDownloaded(
|
val isDownloaded = downloadManager.isChapterDownloaded(
|
||||||
dbChapter.name,
|
dbChapter.name,
|
||||||
dbChapter.scanlator,
|
dbChapter.scanlator,
|
||||||
/* SY --> */ manga.originalTitle /* SY <-- */,
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
manga.source,
|
manga.source,
|
||||||
skipCache = true,
|
skipCache = true,
|
||||||
)
|
)
|
||||||
@ -513,13 +458,14 @@ class ReaderPresenter(
|
|||||||
logcat { "Preloading ${chapter.chapter.url}" }
|
logcat { "Preloading ${chapter.chapter.url}" }
|
||||||
|
|
||||||
val loader = loader ?: return
|
val loader = loader ?: return
|
||||||
|
withIOContext {
|
||||||
loader.loadChapter(chapter)
|
loader.loadChapter(chapter)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.doOnCompleted { eventChannel.trySend(Event.ReloadViewerChapters) }
|
||||||
// Update current chapters whenever a chapter is preloaded
|
|
||||||
.doOnCompleted { viewerChaptersRelay.value?.let(viewerChaptersRelay::call) }
|
|
||||||
.onErrorComplete()
|
.onErrorComplete()
|
||||||
.subscribe()
|
.toObservable<Unit>()
|
||||||
.also(::add)
|
.asFlow()
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -528,7 +474,7 @@ class ReaderPresenter(
|
|||||||
* [page]'s chapter is different from the currently active.
|
* [page]'s chapter is different from the currently active.
|
||||||
*/
|
*/
|
||||||
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean) {
|
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean) {
|
||||||
val currentChapters = viewerChaptersRelay.value ?: return
|
val currentChapters = state.value.viewerChapters ?: return
|
||||||
|
|
||||||
val selectedChapter = page.chapter
|
val selectedChapter = page.chapter
|
||||||
|
|
||||||
@ -547,7 +493,7 @@ class ReaderPresenter(
|
|||||||
selectedChapter.chapter.read = true
|
selectedChapter.chapter.read = true
|
||||||
// SY -->
|
// SY -->
|
||||||
if (manga?.isEhBasedManga() == true) {
|
if (manga?.isEhBasedManga() == true) {
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
chapterList
|
chapterList
|
||||||
.filter { it.chapter.source_order > selectedChapter.chapter.source_order }
|
.filter { it.chapter.source_order > selectedChapter.chapter.source_order }
|
||||||
.onEach {
|
.onEach {
|
||||||
@ -565,7 +511,7 @@ class ReaderPresenter(
|
|||||||
logcat { "Setting ${selectedChapter.chapter.url} as active" }
|
logcat { "Setting ${selectedChapter.chapter.url} as active" }
|
||||||
saveReadingProgress(currentChapters.currChapter)
|
saveReadingProgress(currentChapters.currChapter)
|
||||||
setReadStartTime()
|
setReadStartTime()
|
||||||
loadNewChapter(selectedChapter)
|
viewModelScope.launch { loadNewChapter(selectedChapter) }
|
||||||
}
|
}
|
||||||
val pages = page.chapter.pages ?: return
|
val pages = page.chapter.pages ?: return
|
||||||
val inDownloadRange = page.number.toDouble() / pages.size > 0.25
|
val inDownloadRange = page.number.toDouble() / pages.size > 0.25
|
||||||
@ -581,23 +527,23 @@ class ReaderPresenter(
|
|||||||
|
|
||||||
// Only download ahead if current + next chapter is already downloaded too to avoid jank
|
// Only download ahead if current + next chapter is already downloaded too to avoid jank
|
||||||
if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return
|
if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return
|
||||||
val nextChapter = viewerChaptersRelay.value?.nextChapter?.chapter ?: return
|
val nextChapter = state.value.viewerChapters?.nextChapter?.chapter ?: return
|
||||||
|
|
||||||
coroutineScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
val isNextChapterDownloaded = downloadManager.isChapterDownloaded(
|
val isNextChapterDownloaded = downloadManager.isChapterDownloaded(
|
||||||
nextChapter.name,
|
nextChapter.name,
|
||||||
nextChapter.scanlator,
|
nextChapter.scanlator,
|
||||||
// SY -->
|
// SY -->
|
||||||
manga.originalTitle,
|
manga.ogTitle,
|
||||||
// SY <--
|
// SY <--
|
||||||
manga.source,
|
manga.source,
|
||||||
)
|
)
|
||||||
if (!isNextChapterDownloaded) return@launchIO
|
if (!isNextChapterDownloaded) return@launchIO
|
||||||
|
|
||||||
val chaptersToDownload = getNextChapters.await(manga.id!!, nextChapter.id!!)
|
val chaptersToDownload = getNextChapters.await(manga.id, nextChapter.id!!)
|
||||||
.take(amount)
|
.take(amount)
|
||||||
downloadManager.downloadChapters(
|
downloadManager.downloadChapters(
|
||||||
manga.toDomainManga()!!,
|
manga,
|
||||||
chaptersToDownload,
|
chaptersToDownload,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -641,7 +587,7 @@ class ReaderPresenter(
|
|||||||
* Called when reader chapter is changed in reader or when activity is paused.
|
* Called when reader chapter is changed in reader or when activity is paused.
|
||||||
*/
|
*/
|
||||||
private fun saveReadingProgress(readerChapter: ReaderChapter) {
|
private fun saveReadingProgress(readerChapter: ReaderChapter) {
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
saveChapterProgress(readerChapter)
|
saveChapterProgress(readerChapter)
|
||||||
saveChapterHistory(readerChapter)
|
saveChapterHistory(readerChapter)
|
||||||
}
|
}
|
||||||
@ -689,23 +635,23 @@ class ReaderPresenter(
|
|||||||
/**
|
/**
|
||||||
* Called from the activity to preload the given [chapter].
|
* Called from the activity to preload the given [chapter].
|
||||||
*/
|
*/
|
||||||
fun preloadChapter(chapter: ReaderChapter) {
|
suspend fun preloadChapter(chapter: ReaderChapter) {
|
||||||
preload(chapter)
|
preload(chapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the activity to load and set the next chapter as active.
|
* Called from the activity to load and set the next chapter as active.
|
||||||
*/
|
*/
|
||||||
fun loadNextChapter() {
|
suspend fun loadNextChapter() {
|
||||||
val nextChapter = viewerChaptersRelay.value?.nextChapter ?: return
|
val nextChapter = state.value.viewerChapters?.nextChapter ?: return
|
||||||
loadAdjacent(nextChapter)
|
loadAdjacent(nextChapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the activity to load and set the previous chapter as active.
|
* Called from the activity to load and set the previous chapter as active.
|
||||||
*/
|
*/
|
||||||
fun loadPreviousChapter() {
|
suspend fun loadPreviousChapter() {
|
||||||
val prevChapter = viewerChaptersRelay.value?.prevChapter ?: return
|
val prevChapter = state.value.viewerChapters?.prevChapter ?: return
|
||||||
loadAdjacent(prevChapter)
|
loadAdjacent(prevChapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,7 +659,7 @@ class ReaderPresenter(
|
|||||||
* Returns the currently active chapter.
|
* Returns the currently active chapter.
|
||||||
*/
|
*/
|
||||||
fun getCurrentChapter(): ReaderChapter? {
|
fun getCurrentChapter(): ReaderChapter? {
|
||||||
return viewerChaptersRelay.value?.currChapter
|
return state.value.viewerChapters?.currChapter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSource() = manga?.source?.let { sourceManager.getOrStub(it) } as? HttpSource
|
fun getSource() = manga?.source?.let { sourceManager.getOrStub(it) } as? HttpSource
|
||||||
@ -731,7 +677,7 @@ class ReaderPresenter(
|
|||||||
fun bookmarkCurrentChapter(bookmarked: Boolean) {
|
fun bookmarkCurrentChapter(bookmarked: Boolean) {
|
||||||
val chapter = getCurrentChapter()?.chapter ?: return
|
val chapter = getCurrentChapter()?.chapter ?: return
|
||||||
chapter.bookmark = bookmarked // Otherwise the bookmark icon doesn't update
|
chapter.bookmark = bookmarked // Otherwise the bookmark icon doesn't update
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
updateChapter.await(
|
updateChapter.await(
|
||||||
ChapterUpdate(
|
ChapterUpdate(
|
||||||
id = chapter.id!!.toLong(),
|
id = chapter.id!!.toLong(),
|
||||||
@ -745,7 +691,7 @@ class ReaderPresenter(
|
|||||||
fun toggleBookmark(chapterId: Long, bookmarked: Boolean) {
|
fun toggleBookmark(chapterId: Long, bookmarked: Boolean) {
|
||||||
val chapter = chapterList.find { it.chapter.id == chapterId }?.chapter ?: return
|
val chapter = chapterList.find { it.chapter.id == chapterId }?.chapter ?: return
|
||||||
chapter.bookmark = bookmarked
|
chapter.bookmark = bookmarked
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
updateChapter.await(
|
updateChapter.await(
|
||||||
ChapterUpdate(
|
ChapterUpdate(
|
||||||
id = chapter.id!!.toLong(),
|
id = chapter.id!!.toLong(),
|
||||||
@ -762,7 +708,7 @@ class ReaderPresenter(
|
|||||||
fun getMangaReadingMode(resolveDefault: Boolean = true): Int {
|
fun getMangaReadingMode(resolveDefault: Boolean = true): Int {
|
||||||
val default = readerPreferences.defaultReadingMode().get()
|
val default = readerPreferences.defaultReadingMode().get()
|
||||||
val manga = manga ?: return default
|
val manga = manga ?: return default
|
||||||
val readingMode = ReadingModeType.fromPreference(manga.readingModeType)
|
val readingMode = ReadingModeType.fromPreference(manga.readingModeType.toInt())
|
||||||
// SY -->
|
// SY -->
|
||||||
return when {
|
return when {
|
||||||
resolveDefault && readingMode == ReadingModeType.DEFAULT && readerPreferences.useAutoWebtoon().get() -> {
|
resolveDefault && readingMode == ReadingModeType.DEFAULT && readerPreferences.useAutoWebtoon().get() -> {
|
||||||
@ -770,7 +716,7 @@ class ReaderPresenter(
|
|||||||
?: default
|
?: default
|
||||||
}
|
}
|
||||||
resolveDefault && readingMode == ReadingModeType.DEFAULT -> default
|
resolveDefault && readingMode == ReadingModeType.DEFAULT -> default
|
||||||
else -> manga.readingModeType
|
else -> manga.readingModeType.toInt()
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
@ -780,22 +726,21 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
fun setMangaReadingMode(readingModeType: Int) {
|
fun setMangaReadingMode(readingModeType: Int) {
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
manga.readingModeType = readingModeType
|
viewModelScope.launchIO {
|
||||||
|
setMangaViewerFlags.awaitSetMangaReadingMode(manga.id, readingModeType.toLong())
|
||||||
coroutineScope.launchIO {
|
val currChapters = state.value.viewerChapters
|
||||||
setMangaViewerFlags.awaitSetMangaReadingMode(manga.id!!.toLong(), readingModeType.toLong())
|
|
||||||
delay(250)
|
|
||||||
val currChapters = viewerChaptersRelay.value
|
|
||||||
if (currChapters != null) {
|
if (currChapters != null) {
|
||||||
// Save current page
|
// Save current page
|
||||||
val currChapter = currChapters.currChapter
|
val currChapter = currChapters.currChapter
|
||||||
currChapter.requestedPage = currChapter.chapter.last_page_read
|
currChapter.requestedPage = currChapter.chapter.last_page_read
|
||||||
|
|
||||||
withUIContext {
|
mutableState.update {
|
||||||
// Emit manga and chapters to the new viewer
|
it.copy(
|
||||||
view?.setManga(manga)
|
manga = getManga.await(manga.id),
|
||||||
view?.setChapters(currChapters)
|
viewerChapters = currChapters,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
eventChannel.send(Event.ReloadViewerChapters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,10 +750,10 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
fun getMangaOrientationType(resolveDefault: Boolean = true): Int {
|
fun getMangaOrientationType(resolveDefault: Boolean = true): Int {
|
||||||
val default = readerPreferences.defaultOrientationType().get()
|
val default = readerPreferences.defaultOrientationType().get()
|
||||||
val orientation = OrientationType.fromPreference(manga?.orientationType)
|
val orientation = OrientationType.fromPreference(manga?.orientationType?.toInt())
|
||||||
return when {
|
return when {
|
||||||
resolveDefault && orientation == OrientationType.DEFAULT -> default
|
resolveDefault && orientation == OrientationType.DEFAULT -> default
|
||||||
else -> manga?.orientationType ?: default
|
else -> manga?.orientationType?.toInt() ?: default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,14 +762,22 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
fun setMangaOrientationType(rotationType: Int) {
|
fun setMangaOrientationType(rotationType: Int) {
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
manga.orientationType = rotationType
|
viewModelScope.launchIO {
|
||||||
|
setMangaViewerFlags.awaitSetOrientationType(manga.id, rotationType.toLong())
|
||||||
coroutineScope.launchIO {
|
val currChapters = state.value.viewerChapters
|
||||||
setMangaViewerFlags.awaitSetOrientationType(manga.id!!.toLong(), rotationType.toLong())
|
|
||||||
delay(250)
|
|
||||||
val currChapters = viewerChaptersRelay.value
|
|
||||||
if (currChapters != null) {
|
if (currChapters != null) {
|
||||||
withUIContext { view?.setOrientation(getMangaOrientationType()) }
|
// Save current page
|
||||||
|
val currChapter = currChapters.currChapter
|
||||||
|
currChapter.requestedPage = currChapter.chapter.last_page_read
|
||||||
|
|
||||||
|
mutableState.update {
|
||||||
|
it.copy(
|
||||||
|
manga = getManga.await(manga.id),
|
||||||
|
viewerChapters = currChapters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
eventChannel.send(Event.SetOrientation(getMangaOrientationType()))
|
||||||
|
eventChannel.send(Event.ReloadViewerChapters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -861,8 +814,8 @@ class ReaderPresenter(
|
|||||||
val relativePath = if (readerPreferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
|
val relativePath = if (readerPreferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
|
||||||
|
|
||||||
// Copy file in background.
|
// Copy file in background.
|
||||||
|
viewModelScope.launchNonCancellable {
|
||||||
try {
|
try {
|
||||||
coroutineScope.launchNonCancellable {
|
|
||||||
val uri = imageSaver.save(
|
val uri = imageSaver.save(
|
||||||
image = Image.Page(
|
image = Image.Page(
|
||||||
inputStream = page.stream!!,
|
inputStream = page.stream!!,
|
||||||
@ -872,12 +825,12 @@ class ReaderPresenter(
|
|||||||
)
|
)
|
||||||
withUIContext {
|
withUIContext {
|
||||||
notifier.onComplete(uri)
|
notifier.onComplete(uri)
|
||||||
view?.onSaveImageResult(SaveImageResult.Success(uri))
|
eventChannel.send(Event.SavedImage(SaveImageResult.Success(uri)))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
notifier.onError(e.message)
|
notifier.onError(e.message)
|
||||||
view?.onSaveImageResult(SaveImageResult.Error(e))
|
eventChannel.send(Event.SavedImage(SaveImageResult.Error(e)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -895,8 +848,8 @@ class ReaderPresenter(
|
|||||||
val relativePath = if (readerPreferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
|
val relativePath = if (readerPreferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
|
||||||
|
|
||||||
// Copy file in background.
|
// Copy file in background.
|
||||||
|
viewModelScope.launchNonCancellable {
|
||||||
try {
|
try {
|
||||||
coroutineScope.launchIO {
|
|
||||||
val uri = saveImages(
|
val uri = saveImages(
|
||||||
page1 = firstPage,
|
page1 = firstPage,
|
||||||
page2 = secondPage,
|
page2 = secondPage,
|
||||||
@ -905,14 +858,11 @@ class ReaderPresenter(
|
|||||||
location = Location.Pictures.create(relativePath),
|
location = Location.Pictures.create(relativePath),
|
||||||
manga = manga,
|
manga = manga,
|
||||||
)
|
)
|
||||||
withUIContext {
|
eventChannel.send(Event.SavedImage(SaveImageResult.Success(uri)))
|
||||||
notifier.onComplete(uri)
|
|
||||||
view!!.onSaveImageResult(SaveImageResult.Success(uri))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
notifier.onError(e.message)
|
notifier.onError(e.message)
|
||||||
view!!.onSaveImageResult(SaveImageResult.Error(e))
|
eventChannel.send(Event.SavedImage(SaveImageResult.Error(e)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,7 +916,7 @@ class ReaderPresenter(
|
|||||||
val filename = generateFilename(manga, page)
|
val filename = generateFilename(manga, page)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
destDir.deleteRecursively()
|
destDir.deleteRecursively()
|
||||||
val uri = imageSaver.save(
|
val uri = imageSaver.save(
|
||||||
image = Image.Page(
|
image = Image.Page(
|
||||||
@ -975,9 +925,7 @@ class ReaderPresenter(
|
|||||||
location = Location.Cache,
|
location = Location.Cache,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
withUIContext {
|
eventChannel.send(Event.ShareImage(uri, page))
|
||||||
view?.onShareImageResult(uri, page)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
@ -994,7 +942,7 @@ class ReaderPresenter(
|
|||||||
val destDir = context.cacheImageDir
|
val destDir = context.cacheImageDir
|
||||||
|
|
||||||
try {
|
try {
|
||||||
coroutineScope.launchIO {
|
viewModelScope.launchNonCancellable {
|
||||||
destDir.deleteRecursively()
|
destDir.deleteRecursively()
|
||||||
val uri = saveImages(
|
val uri = saveImages(
|
||||||
page1 = firstPage,
|
page1 = firstPage,
|
||||||
@ -1004,9 +952,7 @@ class ReaderPresenter(
|
|||||||
location = Location.Cache,
|
location = Location.Cache,
|
||||||
manga = manga,
|
manga = manga,
|
||||||
)
|
)
|
||||||
withUIContext {
|
eventChannel.send(Event.ShareImage(uri, firstPage, secondPage))
|
||||||
view!!.onShareImageResult(uri, firstPage, secondPage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
@ -1019,24 +965,21 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
fun setAsCover(context: Context, page: ReaderPage) {
|
fun setAsCover(context: Context, page: ReaderPage) {
|
||||||
if (page.status != Page.State.READY) return
|
if (page.status != Page.State.READY) return
|
||||||
val manga = manga?.toDomainManga() ?: return
|
val manga = manga ?: return
|
||||||
val stream = page.stream ?: return
|
val stream = page.stream ?: return
|
||||||
|
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
try {
|
val result = try {
|
||||||
manga.editCover(context, stream())
|
manga.editCover(context, stream())
|
||||||
withUIContext {
|
|
||||||
view?.onSetAsCoverResult(
|
|
||||||
if (manga.isLocal() || manga.favorite) {
|
if (manga.isLocal() || manga.favorite) {
|
||||||
SetAsCoverResult.Success
|
SetAsCoverResult.Success
|
||||||
} else {
|
} else {
|
||||||
SetAsCoverResult.AddToLibraryFirst
|
SetAsCoverResult.AddToLibraryFirst
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withUIContext { view?.onSetAsCoverResult(SetAsCoverResult.Error) }
|
SetAsCoverResult.Error
|
||||||
}
|
}
|
||||||
|
eventChannel.send(Event.SetCoverResult(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1068,8 +1011,8 @@ class ReaderPresenter(
|
|||||||
val trackManager = Injekt.get<TrackManager>()
|
val trackManager = Injekt.get<TrackManager>()
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
|
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
getTracks.await(manga.id!!)
|
getTracks.await(manga.id)
|
||||||
.mapNotNull { track ->
|
.mapNotNull { track ->
|
||||||
val service = trackManager.getService(track.syncId)
|
val service = trackManager.getService(track.syncId)
|
||||||
if (service != null && service.isLogged && chapterRead > track.lastChapterRead /* SY --> */ && ((service.id == TrackManager.MDLIST && track.status != FollowStatus.UNFOLLOWED.int.toLong()) || service.id != TrackManager.MDLIST)/* SY <-- */) {
|
if (service != null && service.isLogged && chapterRead > track.lastChapterRead /* SY --> */ && ((service.id == TrackManager.MDLIST && track.status != FollowStatus.UNFOLLOWED.int.toLong()) || service.id != TrackManager.MDLIST)/* SY <-- */) {
|
||||||
@ -1106,16 +1049,17 @@ class ReaderPresenter(
|
|||||||
*/
|
*/
|
||||||
private fun enqueueDeleteReadChapters(chapter: ReaderChapter) {
|
private fun enqueueDeleteReadChapters(chapter: ReaderChapter) {
|
||||||
if (!chapter.chapter.read) return
|
if (!chapter.chapter.read) return
|
||||||
|
val mergedManga = state.value.mergedManga
|
||||||
// SY -->
|
// SY -->
|
||||||
val manga = if (mergedManga.isNullOrEmpty()) {
|
val manga = if (mergedManga.isNullOrEmpty()) {
|
||||||
manga
|
manga
|
||||||
} else {
|
} else {
|
||||||
mergedManga.orEmpty()[chapter.chapter.manga_id]?.toDbManga()
|
mergedManga[chapter.chapter.manga_id]
|
||||||
} ?: return
|
} ?: return
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
downloadManager.enqueueChaptersToDelete(listOf(chapter.chapter.toDomainChapter()!!), manga.toDomainManga()!!)
|
downloadManager.enqueueChaptersToDelete(listOf(chapter.chapter.toDomainChapter()!!), manga)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1124,35 +1068,30 @@ class ReaderPresenter(
|
|||||||
* are ignored.
|
* are ignored.
|
||||||
*/
|
*/
|
||||||
private fun deletePendingChapters() {
|
private fun deletePendingChapters() {
|
||||||
coroutineScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
downloadManager.deletePendingChapters()
|
downloadManager.deletePendingChapters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're trying to avoid using Rx, so we "undeprecate" this
|
data class State(
|
||||||
@Suppress("DEPRECATION")
|
val manga: Manga? = null,
|
||||||
override fun getView(): ReaderActivity? {
|
val viewerChapters: ViewerChapters? = null,
|
||||||
return super.getView()
|
val isLoadingAdjacentChapter: Boolean = false,
|
||||||
|
// SY -->
|
||||||
|
val meta: RaisedSearchMetadata? = null,
|
||||||
|
val mergedManga: Map<Long, Manga>? = null,
|
||||||
|
// SY <--
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class Event {
|
||||||
|
object ReloadViewerChapters : Event()
|
||||||
|
data class SetOrientation(val orientation: Int) : Event()
|
||||||
|
data class SetCoverResult(val result: SetAsCoverResult) : Event()
|
||||||
|
|
||||||
|
data class SavedImage(val result: SaveImageResult) : Event()
|
||||||
|
data class ShareImage(val uri: Uri, val page: ReaderPage, val secondPage: ReaderPage? = null) : Event()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle
|
|
||||||
* subscription list.
|
|
||||||
*
|
|
||||||
* @param onNext function to execute when the observable emits an item.
|
|
||||||
* @param onError function to execute when the observable throws an error.
|
|
||||||
*/
|
|
||||||
private fun <T> Observable<T>.subscribeFirst(onNext: (ReaderActivity, T) -> Unit, onError: ((ReaderActivity, Throwable) -> Unit) = { _, _ -> }) = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
|
||||||
* subscription list.
|
|
||||||
*
|
|
||||||
* @param onNext function to execute when the observable emits an item.
|
|
||||||
* @param onError function to execute when the observable throws an error.
|
|
||||||
*/
|
|
||||||
private fun <T> Observable<T>.subscribeLatestCache(onNext: (ReaderActivity, T) -> Unit, onError: ((ReaderActivity, Throwable) -> Unit) = { _, _ -> }) = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Safe theoretical max filename size is 255 bytes and 1 char = 2-4 bytes (UTF-8)
|
// Safe theoretical max filename size is 255 bytes and 1 char = 2-4 bytes (UTF-8)
|
||||||
private const val MAX_FILE_NAME_BYTES = 250
|
private const val MAX_FILE_NAME_BYTES = 250
|
@ -7,10 +7,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.kanade.domain.chapter.model.Chapter
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
|
||||||
import eu.kanade.tachiyomi.databinding.ReaderChaptersDialogBinding
|
import eu.kanade.tachiyomi.databinding.ReaderChaptersDialogBinding
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter
|
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel
|
||||||
import eu.kanade.tachiyomi.util.chapter.getChapterSort
|
import eu.kanade.tachiyomi.util.chapter.getChapterSort
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -18,7 +17,7 @@ import kotlinx.coroutines.launch
|
|||||||
class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterAdapter.OnBookmarkClickListener {
|
class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterAdapter.OnBookmarkClickListener {
|
||||||
private val binding = ReaderChaptersDialogBinding.inflate(activity.layoutInflater)
|
private val binding = ReaderChaptersDialogBinding.inflate(activity.layoutInflater)
|
||||||
|
|
||||||
var presenter: ReaderPresenter = activity.presenter
|
var viewModel: ReaderViewModel = activity.viewModel
|
||||||
var adapter: FlexibleAdapter<ReaderChapterItem>? = null
|
var adapter: FlexibleAdapter<ReaderChapterItem>? = null
|
||||||
var dialog: AlertDialog
|
var dialog: AlertDialog
|
||||||
|
|
||||||
@ -35,9 +34,11 @@ class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterA
|
|||||||
|
|
||||||
adapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, position ->
|
adapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, position ->
|
||||||
val item = adapter?.getItem(position)
|
val item = adapter?.getItem(position)
|
||||||
if (item != null && item.chapter.id != presenter.getCurrentChapter()?.chapter?.id) {
|
if (item != null && item.chapter.id != viewModel.getCurrentChapter()?.chapter?.id) {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
presenter.loadNewChapterFromDialog(item.chapter)
|
activity.lifecycleScope.launch {
|
||||||
|
viewModel.loadNewChapterFromDialog(item.chapter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -51,8 +52,8 @@ class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterA
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshList(scroll: Boolean = true) {
|
private fun refreshList(scroll: Boolean = true) {
|
||||||
val chapterSort = getChapterSort(presenter.manga!!.toDomainManga()!!)
|
val chapterSort = getChapterSort(viewModel.manga!!)
|
||||||
val chapters = presenter.getChapters(activity)
|
val chapters = viewModel.getChapters(activity)
|
||||||
.sortedWith { a, b ->
|
.sortedWith { a, b ->
|
||||||
chapterSort(a.chapter, b.chapter)
|
chapterSort(a.chapter, b.chapter)
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterA
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun bookmarkChapter(chapter: Chapter) {
|
override fun bookmarkChapter(chapter: Chapter) {
|
||||||
presenter.toggleBookmark(chapter.id, !chapter.bookmark)
|
viewModel.toggleBookmark(chapter.id, !chapter.bookmark)
|
||||||
refreshList(scroll = false)
|
refreshList(scroll = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,22 +44,22 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
|
|||||||
private fun initGeneralPreferences() {
|
private fun initGeneralPreferences() {
|
||||||
binding.viewer.onItemSelectedListener = { position ->
|
binding.viewer.onItemSelectedListener = { position ->
|
||||||
val readingModeType = ReadingModeType.fromSpinner(position)
|
val readingModeType = ReadingModeType.fromSpinner(position)
|
||||||
(context as ReaderActivity).presenter.setMangaReadingMode(readingModeType.flagValue)
|
(context as ReaderActivity).viewModel.setMangaReadingMode(readingModeType.flagValue)
|
||||||
|
|
||||||
val mangaViewer = (context as ReaderActivity).presenter.getMangaReadingMode()
|
val mangaViewer = (context as ReaderActivity).viewModel.getMangaReadingMode()
|
||||||
if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) {
|
if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) {
|
||||||
initWebtoonPreferences()
|
initWebtoonPreferences()
|
||||||
} else {
|
} else {
|
||||||
initPagerPreferences()
|
initPagerPreferences()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.viewer.setSelection((context as ReaderActivity).presenter.manga?.readingModeType?.let { ReadingModeType.fromPreference(it).prefValue } ?: ReadingModeType.DEFAULT.prefValue)
|
binding.viewer.setSelection((context as ReaderActivity).viewModel.manga?.readingModeType?.let { ReadingModeType.fromPreference(it.toInt()).prefValue } ?: ReadingModeType.DEFAULT.prefValue)
|
||||||
|
|
||||||
binding.rotationMode.onItemSelectedListener = { position ->
|
binding.rotationMode.onItemSelectedListener = { position ->
|
||||||
val rotationType = OrientationType.fromSpinner(position)
|
val rotationType = OrientationType.fromSpinner(position)
|
||||||
(context as ReaderActivity).presenter.setMangaOrientationType(rotationType.flagValue)
|
(context as ReaderActivity).viewModel.setMangaOrientationType(rotationType.flagValue)
|
||||||
}
|
}
|
||||||
binding.rotationMode.setSelection((context as ReaderActivity).presenter.manga?.orientationType?.let { OrientationType.fromPreference(it).prefValue } ?: OrientationType.DEFAULT.prefValue)
|
binding.rotationMode.setSelection((context as ReaderActivity).viewModel.manga?.orientationType?.let { OrientationType.fromPreference(it.toInt()).prefValue } ?: OrientationType.DEFAULT.prefValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,8 +11,8 @@ import androidx.core.text.bold
|
|||||||
import androidx.core.text.buildSpannedString
|
import androidx.core.text.buildSpannedString
|
||||||
import androidx.core.text.inSpans
|
import androidx.core.text.inSpans
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding
|
import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding
|
||||||
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
|
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
|
||||||
@ -55,7 +55,7 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
val isPrevDownloaded = downloadManager.isChapterDownloaded(
|
val isPrevDownloaded = downloadManager.isChapterDownloaded(
|
||||||
prevChapter.name,
|
prevChapter.name,
|
||||||
prevChapter.scanlator,
|
prevChapter.scanlator,
|
||||||
/* SY --> */ manga.originalTitle /* SY <-- */,
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
manga.source,
|
manga.source,
|
||||||
skipCache = true,
|
skipCache = true,
|
||||||
)
|
)
|
||||||
@ -93,7 +93,7 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
val isNextDownloaded = downloadManager.isChapterDownloaded(
|
val isNextDownloaded = downloadManager.isChapterDownloaded(
|
||||||
nextChapter.name,
|
nextChapter.name,
|
||||||
nextChapter.scanlator,
|
nextChapter.scanlator,
|
||||||
/* SY --> */ manga.originalTitle /* SY <-- */,
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
manga.source,
|
manga.source,
|
||||||
skipCache = true,
|
skipCache = true,
|
||||||
)
|
)
|
||||||
|
@ -62,7 +62,7 @@ class PagerTransitionHolder(
|
|||||||
addView(transitionView)
|
addView(transitionView)
|
||||||
addView(pagesContainer)
|
addView(pagesContainer)
|
||||||
|
|
||||||
transitionView.bind(transition, viewer.downloadManager, viewer.activity.presenter.manga)
|
transitionView.bind(transition, viewer.downloadManager, viewer.activity.viewModel.manga)
|
||||||
|
|
||||||
transition.to?.let { observeStatus(it) }
|
transition.to?.let { observeStatus(it) }
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ class WebtoonTransitionHolder(
|
|||||||
* Binds the given [transition] with this view holder, subscribing to its state.
|
* Binds the given [transition] with this view holder, subscribing to its state.
|
||||||
*/
|
*/
|
||||||
fun bind(transition: ChapterTransition) {
|
fun bind(transition: ChapterTransition) {
|
||||||
transitionView.bind(transition, viewer.downloadManager, viewer.activity.presenter.manga)
|
transitionView.bind(transition, viewer.downloadManager, viewer.activity.viewModel.manga)
|
||||||
|
|
||||||
transition.to?.let { observeStatus(it, transition) }
|
transition.to?.let { observeStatus(it, transition) }
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package exh.util
|
package exh.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import eu.kanade.domain.manga.model.Manga as DomainManga
|
|
||||||
|
|
||||||
fun Manga.mangaType(context: Context): String {
|
fun Manga.mangaType(context: Context): String {
|
||||||
return context.getString(
|
return context.getString(
|
||||||
@ -26,30 +25,6 @@ fun Manga.mangaType(context: Context): String {
|
|||||||
* The type of comic the manga is (ie. manga, manhwa, manhua)
|
* The type of comic the manga is (ie. manga, manhwa, manhua)
|
||||||
*/
|
*/
|
||||||
fun Manga.mangaType(sourceName: String? = Injekt.get<SourceManager>().get(source)?.name): MangaType {
|
fun Manga.mangaType(sourceName: String? = Injekt.get<SourceManager>().get(source)?.name): MangaType {
|
||||||
val currentTags = getGenres().orEmpty()
|
|
||||||
return when {
|
|
||||||
currentTags.any { tag -> isMangaTag(tag) } -> {
|
|
||||||
MangaType.TYPE_MANGA
|
|
||||||
}
|
|
||||||
currentTags.any { tag -> isWebtoonTag(tag) } || sourceName?.let { isWebtoonSource(it) } == true -> {
|
|
||||||
MangaType.TYPE_WEBTOON
|
|
||||||
}
|
|
||||||
currentTags.any { tag -> isComicTag(tag) } || sourceName?.let { isComicSource(it) } == true -> {
|
|
||||||
MangaType.TYPE_COMIC
|
|
||||||
}
|
|
||||||
currentTags.any { tag -> isManhuaTag(tag) } || sourceName?.let { isManhuaSource(it) } == true -> {
|
|
||||||
MangaType.TYPE_MANHUA
|
|
||||||
}
|
|
||||||
currentTags.any { tag -> isManhwaTag(tag) } || sourceName?.let { isManhwaSource(it) } == true -> {
|
|
||||||
MangaType.TYPE_MANHWA
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
MangaType.TYPE_MANGA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DomainManga.mangaType(sourceName: String? = Injekt.get<SourceManager>().get(source)?.name): MangaType {
|
|
||||||
val currentTags = genre.orEmpty()
|
val currentTags = genre.orEmpty()
|
||||||
return when {
|
return when {
|
||||||
currentTags.any { tag -> isMangaTag(tag) } -> {
|
currentTags.any { tag -> isMangaTag(tag) } -> {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
aboutlib_version = "10.5.2"
|
aboutlib_version = "10.5.2"
|
||||||
okhttp_version = "5.0.0-alpha.10"
|
okhttp_version = "5.0.0-alpha.10"
|
||||||
nucleus_version = "3.0.0"
|
|
||||||
coil_version = "2.2.2"
|
coil_version = "2.2.2"
|
||||||
shizuku_version = "12.2.0"
|
shizuku_version = "12.2.0"
|
||||||
sqlite = "2.3.0-rc01"
|
sqlite = "2.3.0-rc01"
|
||||||
@ -41,9 +40,6 @@ sqlite-android = "com.github.requery:sqlite-android:3.39.2"
|
|||||||
|
|
||||||
preferencektx = "androidx.preference:preference-ktx:1.2.0"
|
preferencektx = "androidx.preference:preference-ktx:1.2.0"
|
||||||
|
|
||||||
nucleus-core = { module = "info.android15.nucleus:nucleus", version.ref = "nucleus_version" }
|
|
||||||
nucleus-supportv7 = { module = "info.android15.nucleus:nucleus-support-v7", version.ref = "nucleus_version" }
|
|
||||||
|
|
||||||
injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440"
|
injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440"
|
||||||
|
|
||||||
coil-core = { module = "io.coil-kt:coil", version.ref = "coil_version" }
|
coil-core = { module = "io.coil-kt:coil", version.ref = "coil_version" }
|
||||||
@ -97,7 +93,6 @@ reactivex = ["rxandroid", "rxjava", "rxrelay"]
|
|||||||
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-dnsoverhttps"]
|
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-dnsoverhttps"]
|
||||||
js-engine = ["quickjs-android"]
|
js-engine = ["quickjs-android"]
|
||||||
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
|
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
|
||||||
nucleus = ["nucleus-core", "nucleus-supportv7"]
|
|
||||||
coil = ["coil-core", "coil-gif", "coil-compose"]
|
coil = ["coil-core", "coil-gif", "coil-compose"]
|
||||||
shizuku = ["shizuku-api", "shizuku-provider"]
|
shizuku = ["shizuku-api", "shizuku-provider"]
|
||||||
voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"]
|
voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user