Reader refactoring

pull/332/head
Koitharu 3 years ago
parent 5e55bce529
commit a3cf52859b
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -95,8 +95,11 @@ class BookmarksFragment :
override fun onItemClick(item: Bookmark, view: View) { override fun onItemClick(item: Bookmark, view: View) {
if (selectionController?.onItemClick(item.manga, item.pageId) != true) { if (selectionController?.onItemClick(item.manga, item.pageId) != true) {
val intent = ReaderActivity.newIntent(view.context, item) val intent = ReaderActivity.IntentBuilder(view.context)
startActivity(intent, scaleUpActivityOptionsOf(view).toBundle()) .bookmark(item)
.incognito(true)
.build()
startActivity(intent, scaleUpActivityOptionsOf(view))
Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show() Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
} }
} }

@ -26,12 +26,12 @@ import org.koitharu.kotatsu.core.db.TABLE_HISTORY
import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -142,8 +142,9 @@ class ShortcutsUpdater @Inject constructor(
.setLongLabel(manga.title) .setLongLabel(manga.title)
.setIcon(icon) .setIcon(icon)
.setIntent( .setIntent(
ReaderActivity.newIntent(context, manga.id) ReaderActivity.IntentBuilder(context)
.setAction(ReaderActivity.ACTION_MANGA_READ), .mangaId(manga.id)
.build(),
) )
} }

@ -16,6 +16,7 @@ import android.database.SQLException
import android.graphics.Color import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.View import android.view.View
import android.view.ViewPropertyAnimator import android.view.ViewPropertyAnimator
@ -43,7 +44,6 @@ import org.jsoup.internal.StringUtil.StringJoiner
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
import kotlin.math.roundToLong import kotlin.math.roundToLong
@ -148,13 +148,17 @@ val Context.ramAvailable: Long
return result.availMem return result.availMem
} }
fun scaleUpActivityOptionsOf(view: View): ActivityOptions = ActivityOptions.makeScaleUpAnimation( fun scaleUpActivityOptionsOf(view: View): Bundle? = if (view.context.isAnimationsEnabled) {
ActivityOptions.makeScaleUpAnimation(
view, view,
0, 0,
0, 0,
view.width, view.width,
view.height, view.height,
) ).toBundle()
} else {
null
}
fun Resources.getLocalesConfig(): LocaleListCompat { fun Resources.getLocalesConfig(): LocaleListCompat {
val tagsList = StringJoiner(",") val tagsList = StringJoiner(",")

@ -25,7 +25,7 @@ import org.koitharu.kotatsu.details.ui.adapter.ChaptersSelectionDecoration
import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.local.ui.LocalChaptersRemoveService import org.koitharu.kotatsu.local.ui.LocalChaptersRemoveService
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -79,12 +79,11 @@ class ChaptersFragment :
return return
} }
startActivity( startActivity(
ReaderActivity.newIntent( IntentBuilder(view.context)
context = view.context, .manga(viewModel.manga.value ?: return)
manga = viewModel.manga.value ?: return, .state(ReaderState(item.chapter.id, 0, 0))
state = ReaderState(item.chapter.id, 0, 0), .build(),
), scaleUpActivityOptionsOf(view),
scaleUpActivityOptionsOf(view).toBundle(),
) )
} }

@ -53,7 +53,7 @@ import org.koitharu.kotatsu.details.ui.model.MangaBranch
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
@ -301,12 +301,11 @@ class DetailsActivity :
snackbar.show() snackbar.show()
} else { } else {
startActivity( startActivity(
ReaderActivity.newIntent( IntentBuilder(this)
context = this, .manga(manga)
manga = manga, .branch(viewModel.selectedBranchValue)
branch = viewModel.selectedBranchValue, .incognito(isIncognitoMode)
isIncognitoMode = isIncognitoMode, .build(),
),
) )
if (isIncognitoMode) { if (isIncognitoMode) {
Toast.makeText(this, R.string.incognito_mode, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.incognito_mode, Toast.LENGTH_SHORT).show()

@ -96,8 +96,8 @@ class DetailsFragment :
override fun onItemClick(item: Bookmark, view: View) { override fun onItemClick(item: Bookmark, view: View) {
startActivity( startActivity(
ReaderActivity.newIntent(view.context, item), ReaderActivity.IntentBuilder(view.context).bookmark(item).incognito(true).build(),
scaleUpActivityOptionsOf(view).toBundle(), scaleUpActivityOptionsOf(view),
) )
Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show() Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
} }
@ -257,7 +257,7 @@ class DetailsFragment :
manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl },
manga.source, manga.source,
), ),
scaleUpActivityOptionsOf(v).toBundle(), scaleUpActivityOptionsOf(v),
) )
} }
} }

@ -110,7 +110,7 @@ class ScrobblingInfoSheet :
R.id.imageView_cover -> { R.id.imageView_cover -> {
val coverUrl = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.coverUrl ?: return val coverUrl = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.coverUrl ?: return
val options = scaleUpActivityOptionsOf(v) val options = scaleUpActivityOptionsOf(v)
startActivity(ImageActivity.newIntent(v.context, coverUrl, null), options.toBundle()) startActivity(ImageActivity.newIntent(v.context, coverUrl, null), options)
} }
} }
} }

@ -112,7 +112,7 @@ class FavouriteCategoriesActivity :
} }
val intent = FavouritesActivity.newIntent(this, item) val intent = FavouritesActivity.newIntent(this, item)
val options = scaleUpActivityOptionsOf(view) val options = scaleUpActivityOptionsOf(view)
startActivity(intent, options.toBundle()) startActivity(intent, options)
} }
override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean { override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean {

@ -55,7 +55,7 @@ import org.koitharu.kotatsu.main.ui.MainActivity
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.MangaListActivity
import javax.inject.Inject import javax.inject.Inject
@ -148,8 +148,8 @@ abstract class MangaListFragment :
override fun onReadClick(manga: Manga, view: View) { override fun onReadClick(manga: Manga, view: View) {
if (selectionController?.onItemClick(manga.id) != true) { if (selectionController?.onItemClick(manga.id) != true) {
val intent = ReaderActivity.newIntent(context ?: return, manga) val intent = IntentBuilder(view.context).manga(manga).build()
startActivity(intent, scaleUpActivityOptionsOf(view).toBundle()) startActivity(intent, scaleUpActivityOptionsOf(view))
} }
} }

@ -60,7 +60,7 @@ import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity import org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment
@ -259,9 +259,9 @@ class MainActivity :
private fun onOpenReader(manga: Manga) { private fun onOpenReader(manga: Manga) {
val fab = viewBinding.fab ?: viewBinding.navRail?.headerView val fab = viewBinding.fab ?: viewBinding.navRail?.headerView
val options = fab?.let { val options = fab?.let {
scaleUpActivityOptionsOf(it).toBundle() scaleUpActivityOptionsOf(it)
} }
startActivity(ReaderActivity.newIntent(this, manga), options) startActivity(IntentBuilder(this).manga(manga).build(), options)
} }
private fun onCountersChanged(counters: SparseIntArray) { private fun onCountersChanged(counters: SparseIntArray) {

@ -398,45 +398,50 @@ class ReaderActivity :
} }
} }
companion object { class IntentBuilder(context: Context) {
const val ACTION_MANGA_READ = "${BuildConfig.APPLICATION_ID}.action.READ_MANGA" private val intent = Intent(context, ReaderActivity::class.java)
const val EXTRA_STATE = "state" .setAction(ACTION_MANGA_READ)
const val EXTRA_BRANCH = "branch"
const val EXTRA_INCOGNITO = "incognito" fun manga(manga: Manga) = apply {
private const val TOAST_DURATION = 1500L intent.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga, withChapters = true))
}
fun mangaId(mangaId: Long) = apply {
intent.putExtra(MangaIntent.KEY_ID, mangaId)
}
fun newIntent(context: Context, manga: Manga): Intent { fun incognito(incognito: Boolean) = apply {
return Intent(context, ReaderActivity::class.java) intent.putExtra(EXTRA_INCOGNITO, incognito)
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga, withChapters = true))
} }
fun newIntent(context: Context, manga: Manga, branch: String?, isIncognitoMode: Boolean): Intent { fun branch(branch: String?) = apply {
return Intent(context, ReaderActivity::class.java) intent.putExtra(EXTRA_BRANCH, branch)
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga, withChapters = true))
.putExtra(EXTRA_BRANCH, branch)
.putExtra(EXTRA_INCOGNITO, isIncognitoMode)
} }
fun newIntent(context: Context, manga: Manga, state: ReaderState?): Intent { fun state(state: ReaderState?) = apply {
return Intent(context, ReaderActivity::class.java) intent.putExtra(EXTRA_STATE, state)
.putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga, withChapters = true))
.putExtra(EXTRA_STATE, state)
} }
fun newIntent(context: Context, bookmark: Bookmark): Intent { fun bookmark(bookmark: Bookmark) = manga(
val state = ReaderState( bookmark.manga,
).state(
ReaderState(
chapterId = bookmark.chapterId, chapterId = bookmark.chapterId,
page = bookmark.page, page = bookmark.page,
scroll = bookmark.scroll, scroll = bookmark.scroll,
),
) )
return newIntent(context, bookmark.manga, state)
.putExtra(EXTRA_INCOGNITO, true)
}
fun newIntent(context: Context, mangaId: Long): Intent { fun build() = intent
return Intent(context, ReaderActivity::class.java)
.putExtra(MangaIntent.KEY_ID, mangaId)
} }
companion object {
const val ACTION_MANGA_READ = "${BuildConfig.APPLICATION_ID}.action.READ_MANGA"
const val EXTRA_STATE = "state"
const val EXTRA_BRANCH = "branch"
const val EXTRA_INCOGNITO = "incognito"
private const val TOAST_DURATION = 1500L
} }
} }

@ -312,8 +312,10 @@ class ReaderViewModel @Inject constructor(
private fun loadImpl() { private fun loadImpl() {
loadingJob = launchLoadingJob(Dispatchers.Default) { loadingJob = launchLoadingJob(Dispatchers.Default) {
var manga = var manga = DoubleManga(
DoubleManga(dataRepository.resolveIntent(intent) ?: throw NotFoundException("Cannot find manga", "")) dataRepository.resolveIntent(intent)
?: throw NotFoundException("Cannot find manga", ""),
)
mangaData.value = manga mangaData.value = manga
manga = doubleMangaLoadUseCase(intent) manga = doubleMangaLoadUseCase(intent)
chaptersLoader.init(manga) chaptersLoader.init(manga)

@ -17,9 +17,13 @@ abstract class BaseReaderFragment<B : ViewBinding> : BaseFragment<B>() {
protected val viewModel by activityViewModels<ReaderViewModel>() protected val viewModel by activityViewModels<ReaderViewModel>()
private var stateToSave: ReaderState? = null private var stateToSave: ReaderState? = null
protected var readerAdapter: BaseReaderAdapter<*>? = null
private set
override fun onViewBindingCreated(binding: B, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: B, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
var restoredState = savedInstanceState?.getParcelableCompat<ReaderState>(KEY_STATE) var restoredState = savedInstanceState?.getParcelableCompat<ReaderState>(KEY_STATE)
readerAdapter = onCreateAdapter()
viewModel.content.observe(viewLifecycleOwner) { viewModel.content.observe(viewLifecycleOwner) {
onPagesChanged(it.pages, restoredState ?: it.state) onPagesChanged(it.pages, restoredState ?: it.state)
@ -34,6 +38,7 @@ abstract class BaseReaderFragment<B : ViewBinding> : BaseFragment<B>() {
override fun onDestroyView() { override fun onDestroyView() {
stateToSave = getCurrentState() stateToSave = getCurrentState()
readerAdapter = null
super.onDestroyView() super.onDestroyView()
} }
@ -45,6 +50,10 @@ abstract class BaseReaderFragment<B : ViewBinding> : BaseFragment<B>() {
outState.putParcelable(KEY_STATE, stateToSave) outState.putParcelable(KEY_STATE, stateToSave)
} }
protected fun requireAdapter() = checkNotNull(readerAdapter) {
"Adapter was not created or already destroyed"
}
override fun onWindowInsetsChanged(insets: Insets) = Unit override fun onWindowInsetsChanged(insets: Insets) = Unit
abstract fun switchPageBy(delta: Int) abstract fun switchPageBy(delta: Int)
@ -55,5 +64,7 @@ abstract class BaseReaderFragment<B : ViewBinding> : BaseFragment<B>() {
abstract fun getCurrentState(): ReaderState? abstract fun getCurrentState(): ReaderState?
protected abstract fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) protected abstract fun onCreateAdapter(): BaseReaderAdapter<*>
protected abstract suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?)
} }

@ -4,16 +4,18 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.children import androidx.core.view.children
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.yield
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.recyclerView import org.koitharu.kotatsu.core.util.ext.recyclerView
import org.koitharu.kotatsu.core.util.ext.resetTransformations import org.koitharu.kotatsu.core.util.ext.resetTransformations
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
@ -33,8 +35,6 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
@Inject @Inject
lateinit var pageLoader: PageLoader lateinit var pageLoader: PageLoader
private var pagerAdapter: ReversedPagesAdapter? = null
override fun onCreateViewBinding( override fun onCreateViewBinding(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -42,15 +42,8 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
override fun onViewBindingCreated(binding: FragmentReaderStandardBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentReaderStandardBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
pagerAdapter = ReversedPagesAdapter(
lifecycleOwner = viewLifecycleOwner,
loader = pageLoader,
settings = viewModel.readerSettings,
networkState = networkState,
exceptionResolver = exceptionResolver,
)
with(binding.pager) { with(binding.pager) {
adapter = pagerAdapter adapter = readerAdapter
offscreenPageLimit = 2 offscreenPageLimit = 2
doOnPageChanged(::notifyPageChanged) doOnPageChanged(::notifyPageChanged)
} }
@ -67,11 +60,18 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
} }
override fun onDestroyView() { override fun onDestroyView() {
pagerAdapter = null
requireViewBinding().pager.adapter = null requireViewBinding().pager.adapter = null
super.onDestroyView() super.onDestroyView()
} }
override fun onCreateAdapter() = ReversedPagesAdapter(
lifecycleOwner = viewLifecycleOwner,
loader = pageLoader,
settings = viewModel.readerSettings,
networkState = networkState,
exceptionResolver = exceptionResolver,
)
override fun switchPageBy(delta: Int) { override fun switchPageBy(delta: Int) {
with(requireViewBinding().pager) { with(requireViewBinding().pager) {
setCurrentItem(currentItem - delta, context.isAnimationsEnabled) setCurrentItem(currentItem - delta, context.isAnimationsEnabled)
@ -87,26 +87,28 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
} }
} }
override fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) { override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
val reversedPages = pages.asReversed() val reversedPages = pages.asReversed()
viewLifecycleScope.launch {
val items = async { val items = async {
pagerAdapter?.setItems(reversedPages) requireAdapter().setItems(reversedPages)
yield()
} }
if (pendingState != null) { if (pendingState != null) {
val position = reversedPages.indexOfLast { val position = reversedPages.indexOfLast {
it.chapterId == pendingState.chapterId && it.index == pendingState.page it.chapterId == pendingState.chapterId && it.index == pendingState.page
} }
items.await() ?: return@launch items.await()
if (position != -1) { if (position != -1) {
requireViewBinding().pager.setCurrentItem(position, false) requireViewBinding().pager.setCurrentItem(position, false)
notifyPageChanged(position) notifyPageChanged(position)
} else {
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
.show()
} }
} else { } else {
items.await() items.await()
} }
} }
}
override fun getCurrentState(): ReaderState? = viewBinding?.run { override fun getCurrentState(): ReaderState? = viewBinding?.run {
val adapter = pager.adapter as? BaseReaderAdapter<*> val adapter = pager.adapter as? BaseReaderAdapter<*>
@ -123,6 +125,6 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
} }
private fun reversed(position: Int): Int { private fun reversed(position: Int): Int {
return ((pagerAdapter?.itemCount ?: 0) - position - 1).coerceAtLeast(0) return ((readerAdapter?.itemCount ?: 0) - position - 1).coerceAtLeast(0)
} }
} }

@ -4,16 +4,18 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.children import androidx.core.view.children
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.yield
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.recyclerView import org.koitharu.kotatsu.core.util.ext.recyclerView
import org.koitharu.kotatsu.core.util.ext.resetTransformations import org.koitharu.kotatsu.core.util.ext.resetTransformations
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
@ -32,8 +34,6 @@ class PagerReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>()
@Inject @Inject
lateinit var pageLoader: PageLoader lateinit var pageLoader: PageLoader
private var pagesAdapter: PagesAdapter? = null
override fun onCreateViewBinding( override fun onCreateViewBinding(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -41,15 +41,8 @@ class PagerReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>()
override fun onViewBindingCreated(binding: FragmentReaderStandardBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentReaderStandardBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
pagesAdapter = PagesAdapter(
lifecycleOwner = viewLifecycleOwner,
loader = pageLoader,
settings = viewModel.readerSettings,
networkState = networkState,
exceptionResolver = exceptionResolver,
)
with(binding.pager) { with(binding.pager) {
adapter = pagesAdapter adapter = readerAdapter
offscreenPageLimit = 2 offscreenPageLimit = 2
doOnPageChanged(::notifyPageChanged) doOnPageChanged(::notifyPageChanged)
} }
@ -66,30 +59,39 @@ class PagerReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>()
} }
override fun onDestroyView() { override fun onDestroyView() {
pagesAdapter = null
requireViewBinding().pager.adapter = null requireViewBinding().pager.adapter = null
super.onDestroyView() super.onDestroyView()
} }
override fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) { override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
viewLifecycleScope.launch {
val items = async { val items = async {
pagesAdapter?.setItems(pages) requireAdapter().setItems(pages)
yield()
} }
if (pendingState != null) { if (pendingState != null) {
val position = pages.indexOfFirst { val position = pages.indexOfFirst {
it.chapterId == pendingState.chapterId && it.index == pendingState.page it.chapterId == pendingState.chapterId && it.index == pendingState.page
} }
items.await() ?: return@launch items.await()
if (position != -1) { if (position != -1) {
requireViewBinding().pager.setCurrentItem(position, false) requireViewBinding().pager.setCurrentItem(position, false)
notifyPageChanged(position) notifyPageChanged(position)
} else {
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
.show()
} }
} else { } else {
items.await() items.await()
} }
} }
}
override fun onCreateAdapter() = PagesAdapter(
lifecycleOwner = viewLifecycleOwner,
loader = pageLoader,
settings = viewModel.readerSettings,
networkState = networkState,
exceptionResolver = exceptionResolver,
)
override fun switchPageBy(delta: Int) { override fun switchPageBy(delta: Int) {
with(requireViewBinding().pager) { with(requireViewBinding().pager) {

@ -4,15 +4,17 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.yield
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.util.ext.findCenterViewPosition import org.koitharu.kotatsu.core.util.ext.findCenterViewPosition
import org.koitharu.kotatsu.core.util.ext.firstVisibleItemPosition import org.koitharu.kotatsu.core.util.ext.firstVisibleItemPosition
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding
import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
@ -31,7 +33,6 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
lateinit var pageLoader: PageLoader lateinit var pageLoader: PageLoader
private val scrollInterpolator = AccelerateDecelerateInterpolator() private val scrollInterpolator = AccelerateDecelerateInterpolator()
private var webtoonAdapter: WebtoonAdapter? = null
override fun onCreateViewBinding( override fun onCreateViewBinding(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -40,16 +41,9 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
override fun onViewBindingCreated(binding: FragmentReaderWebtoonBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentReaderWebtoonBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
webtoonAdapter = WebtoonAdapter(
lifecycleOwner = viewLifecycleOwner,
loader = pageLoader,
settings = viewModel.readerSettings,
networkState = networkState,
exceptionResolver = exceptionResolver,
)
with(binding.recyclerView) { with(binding.recyclerView) {
setHasFixedSize(true) setHasFixedSize(true)
adapter = webtoonAdapter adapter = readerAdapter
addOnPageScrollListener(PageScrollListener()) addOnPageScrollListener(PageScrollListener())
} }
@ -59,19 +53,28 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
} }
override fun onDestroyView() { override fun onDestroyView() {
webtoonAdapter = null
requireViewBinding().recyclerView.adapter = null requireViewBinding().recyclerView.adapter = null
super.onDestroyView() super.onDestroyView()
} }
override fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) { override fun onCreateAdapter() = WebtoonAdapter(
viewLifecycleScope.launch { lifecycleOwner = viewLifecycleOwner,
val setItems = async { webtoonAdapter?.setItems(pages) } loader = pageLoader,
settings = viewModel.readerSettings,
networkState = networkState,
exceptionResolver = exceptionResolver,
)
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
val setItems = async {
requireAdapter().setItems(pages)
yield()
}
if (pendingState != null) { if (pendingState != null) {
val position = pages.indexOfFirst { val position = pages.indexOfFirst {
it.chapterId == pendingState.chapterId && it.index == pendingState.page it.chapterId == pendingState.chapterId && it.index == pendingState.page
} }
setItems.await() ?: return@launch setItems.await()
if (position != -1) { if (position != -1) {
with(requireViewBinding().recyclerView) { with(requireViewBinding().recyclerView) {
firstVisibleItemPosition = position firstVisibleItemPosition = position
@ -81,12 +84,14 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
} }
} }
notifyPageChanged(position) notifyPageChanged(position)
} else {
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
.show()
} }
} else { } else {
setItems.await() setItems.await()
} }
} }
}
override fun getCurrentState(): ReaderState? = viewBinding?.run { override fun getCurrentState(): ReaderState? = viewBinding?.run {
val currentItem = recyclerView.findCenterViewPosition() val currentItem = recyclerView.findCenterViewPosition()

@ -30,7 +30,7 @@ import org.koitharu.kotatsu.databinding.SheetPagesBinding
import org.koitharu.kotatsu.list.ui.MangaListSpanResolver import org.koitharu.kotatsu.list.ui.MangaListSpanResolver
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter import org.koitharu.kotatsu.reader.ui.thumbnails.adapter.PageThumbnailAdapter
import javax.inject.Inject import javax.inject.Inject
@ -101,8 +101,8 @@ class PagesThumbnailsSheet :
listener.onPageSelected(item.page) listener.onPageSelected(item.page)
} else { } else {
val state = ReaderState(item.page.chapterId, item.page.index, 0) val state = ReaderState(item.page.chapterId, item.page.index, 0)
val intent = ReaderActivity.newIntent(view.context, viewModel.manga, state) val intent = IntentBuilder(view.context).manga(viewModel.manga).state(state).build()
startActivity(intent, scaleUpActivityOptionsOf(view).toBundle()) startActivity(intent, scaleUpActivityOptionsOf(view))
} }
dismiss() dismiss()
} }

@ -34,7 +34,7 @@ import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.model.ListHeader
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.search.ui.SearchActivity import org.koitharu.kotatsu.search.ui.SearchActivity
import org.koitharu.kotatsu.search.ui.multi.adapter.MultiSearchAdapter import org.koitharu.kotatsu.search.ui.multi.adapter.MultiSearchAdapter
@ -117,8 +117,8 @@ class MultiSearchActivity :
override fun onReadClick(manga: Manga, view: View) { override fun onReadClick(manga: Manga, view: View) {
if (!selectionController.onItemClick(manga.id)) { if (!selectionController.onItemClick(manga.id)) {
val intent = ReaderActivity.newIntent(this, manga) val intent = IntentBuilder(this).manga(manga).build()
startActivity(intent, scaleUpActivityOptionsOf(view).toBundle()) startActivity(intent, scaleUpActivityOptionsOf(view))
} }
} }

@ -41,6 +41,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.almostEquals import org.koitharu.kotatsu.core.util.ext.almostEquals
import org.koitharu.kotatsu.core.util.ext.asArrayList import org.koitharu.kotatsu.core.util.ext.asArrayList
import org.koitharu.kotatsu.core.util.ext.flatten import org.koitharu.kotatsu.core.util.ext.flatten
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.sanitize import org.koitharu.kotatsu.core.util.ext.sanitize
import org.koitharu.kotatsu.core.util.ext.takeMostFrequent import org.koitharu.kotatsu.core.util.ext.takeMostFrequent
import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull
@ -53,11 +54,10 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.suggestions.domain.MangaSuggestion import org.koitharu.kotatsu.suggestions.domain.MangaSuggestion
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
import org.koitharu.kotatsu.suggestions.domain.TagsBlacklist import org.koitharu.kotatsu.suggestions.domain.TagsBlacklist
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.pow import kotlin.math.pow
import kotlin.random.Random import kotlin.random.Random
@ -263,7 +263,7 @@ class SuggestionsWorker @AssistedInject constructor(
PendingIntentCompat.getActivity( PendingIntentCompat.getActivity(
applicationContext, applicationContext,
id + 2, id + 2,
ReaderActivity.newIntent(applicationContext, manga), IntentBuilder(applicationContext).manga(manga).build(),
0, 0,
false, false,
), ),

Loading…
Cancel
Save