mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
replace subscription with job in PagerPageHolder
also more fixes to the auto shifting
This commit is contained in:
parent
d93f9d6a45
commit
6872616db5
4 changed files with 109 additions and 115 deletions
|
@ -208,6 +208,8 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
private var indexChapterToShift: Long? = null
|
||||
|
||||
private var lastCropRes = 0
|
||||
var manuallyShiftedPages = false
|
||||
private set
|
||||
|
||||
val isSplitScreen: Boolean
|
||||
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isInMultiWindowMode
|
||||
|
@ -593,6 +595,7 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
when (item.itemId) {
|
||||
R.id.action_shift_double_page -> {
|
||||
shiftDoublePages()
|
||||
manuallyShiftedPages = true
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
@ -600,12 +603,13 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
}
|
||||
|
||||
fun shiftDoublePages(forceShift: Boolean? = null, page: ReaderPage? = null) {
|
||||
(viewer as? PagerViewer)?.config?.let { config ->
|
||||
if (forceShift == config.shiftDoublePage) return
|
||||
config.shiftDoublePage = !config.shiftDoublePage
|
||||
(viewer as? PagerViewer)?.let { pViewer ->
|
||||
if (forceShift == pViewer.config.shiftDoublePage) return
|
||||
if (page != null && pViewer.getShiftedPage() == page) return
|
||||
pViewer.config.shiftDoublePage = !pViewer.config.shiftDoublePage
|
||||
viewModel.state.value.viewerChapters?.let {
|
||||
(viewer as? PagerViewer)?.updateShifting(page)
|
||||
(viewer as? PagerViewer)?.setChaptersDoubleShift(it)
|
||||
pViewer.updateShifting(page)
|
||||
pViewer.setChaptersDoubleShift(it)
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
|
@ -821,6 +825,7 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
|
||||
binding.chaptersSheet.shiftPageButton.setOnClickListener {
|
||||
shiftDoublePages()
|
||||
manuallyShiftedPages = true
|
||||
}
|
||||
|
||||
binding.readerNav.leftChapter.setOnClickListener { loadAdjacentChapter(false) }
|
||||
|
@ -1303,6 +1308,9 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
binding.chaptersSheet.root.sheetBehavior.isCollapsed() -> View.VISIBLE
|
||||
else -> View.INVISIBLE
|
||||
}
|
||||
if (lastShiftDoubleState == null) {
|
||||
manuallyShiftedPages = false
|
||||
}
|
||||
lastShiftDoubleState = null
|
||||
viewer?.setChapters(viewerChapters)
|
||||
intentPageNumber?.let { moveToPageIndex(it) }
|
||||
|
|
|
@ -33,22 +33,21 @@ import eu.kanade.tachiyomi.util.system.bottomCutoutInset
|
|||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.isInNightMode
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.topCutoutInset
|
||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||
import eu.kanade.tachiyomi.util.view.backgroundColor
|
||||
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
|
||||
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Dispatchers.Default
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.InputStream
|
||||
|
@ -117,10 +116,10 @@ class PagerPageHolder(
|
|||
private var extraProgressJob: Job? = null
|
||||
|
||||
/**
|
||||
* Subscription used to read the header of the image. This is needed in order to instantiate
|
||||
* the appropiate image view depending if the image is animated (GIF).
|
||||
* Job used to read the header of the image. This is needed in order to instantiate
|
||||
* the appropriate image view depending if the image is animated (GIF).
|
||||
*/
|
||||
private var readImageHeaderSubscription: Subscription? = null
|
||||
private var readImageHeaderJob: Job? = null
|
||||
|
||||
private var status = Page.State.READY
|
||||
private var extraStatus = Page.State.READY
|
||||
|
@ -197,7 +196,7 @@ class PagerPageHolder(
|
|||
cancelLoadJob(1)
|
||||
cancelProgressJob(2)
|
||||
cancelLoadJob(2)
|
||||
unsubscribeReadImageHeader()
|
||||
cancelReadImageHeader()
|
||||
(pageView as? SubsamplingScaleImageView)?.setOnImageEventListener(null)
|
||||
}
|
||||
|
||||
|
@ -442,9 +441,9 @@ class PagerPageHolder(
|
|||
/**
|
||||
* Unsubscribes from the read image header subscription.
|
||||
*/
|
||||
private fun unsubscribeReadImageHeader() {
|
||||
readImageHeaderSubscription?.unsubscribe()
|
||||
readImageHeaderSubscription = null
|
||||
private fun cancelReadImageHeader() {
|
||||
readImageHeaderJob?.cancel()
|
||||
readImageHeaderJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -487,40 +486,35 @@ class PagerPageHolder(
|
|||
retryButton?.isVisible = false
|
||||
decodeErrorLayout?.isVisible = false
|
||||
|
||||
unsubscribeReadImageHeader()
|
||||
cancelReadImageHeader()
|
||||
val streamFn = page.stream ?: return
|
||||
val streamFn2 = extraPage?.stream
|
||||
|
||||
var openStream: InputStream? = null
|
||||
|
||||
readImageHeaderSubscription = Observable
|
||||
.fromCallable {
|
||||
readImageHeaderJob = scope.launchIO {
|
||||
try {
|
||||
val stream = streamFn().buffered(16)
|
||||
|
||||
val stream2 = streamFn2?.invoke()?.buffered(16)
|
||||
openStream = this@PagerPageHolder.mergeOrSplitPages(stream, stream2)
|
||||
ImageUtil.isAnimatedAndSupported(stream) ||
|
||||
if (stream2 != null) ImageUtil.isAnimatedAndSupported(stream2) else false
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { isAnimated ->
|
||||
if (!isAnimated) {
|
||||
if (viewer.config.readerTheme >= 2) {
|
||||
if (page.bg != null &&
|
||||
page.bgType == getBGType(viewer.config.readerTheme, context) + item.hashCode()
|
||||
) {
|
||||
setImage(openStream!!, false, imageConfig)
|
||||
pageView?.background = page.bg
|
||||
}
|
||||
// if the user switches to automatic when pages are already cached, the bg needs to be loaded
|
||||
else {
|
||||
val bytesArray = openStream!!.readBytes()
|
||||
val bytesStream = bytesArray.inputStream()
|
||||
setImage(bytesStream, false, imageConfig)
|
||||
bytesStream.close()
|
||||
val isAnimated = ImageUtil.isAnimatedAndSupported(stream) ||
|
||||
(stream2?.let { ImageUtil.isAnimatedAndSupported(stream2) } ?: false)
|
||||
withUIContext {
|
||||
if (!isAnimated) {
|
||||
if (viewer.config.readerTheme >= 2) {
|
||||
val bgType = getBGType(viewer.config.readerTheme, context)
|
||||
if (page.bg != null && page.bgType == bgType) {
|
||||
setImage(openStream!!, false, imageConfig)
|
||||
pageView?.background = page.bg
|
||||
}
|
||||
// if the user switches to automatic when pages are already cached, the bg needs to be loaded
|
||||
else {
|
||||
val bytesArray = openStream!!.readBytes()
|
||||
val bytesStream = bytesArray.inputStream()
|
||||
setImage(bytesStream, false, imageConfig)
|
||||
closeStreams(bytesStream)
|
||||
|
||||
scope.launchUI {
|
||||
try {
|
||||
pageView?.background = setBG(bytesArray)
|
||||
} catch (e: Exception) {
|
||||
|
@ -528,36 +522,26 @@ class PagerPageHolder(
|
|||
pageView?.background = ColorDrawable(Color.WHITE)
|
||||
} finally {
|
||||
page.bg = pageView?.background
|
||||
page.bgType = getBGType(
|
||||
viewer.config.readerTheme,
|
||||
context,
|
||||
) + item.hashCode()
|
||||
page.bgType = bgType
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setImage(openStream!!, false, imageConfig)
|
||||
}
|
||||
} else {
|
||||
setImage(openStream!!, false, imageConfig)
|
||||
}
|
||||
} else {
|
||||
setImage(openStream!!, true, imageConfig)
|
||||
if (viewer.config.readerTheme >= 2 && page.bg != null) {
|
||||
pageView?.background = page.bg
|
||||
setImage(openStream!!, true, imageConfig)
|
||||
if (viewer.config.readerTheme >= 2 && page.bg != null) {
|
||||
pageView?.background = page.bg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Keep the Rx stream alive to close the input stream only when unsubscribed
|
||||
.flatMap { Observable.never<Unit>() }
|
||||
.doOnUnsubscribe {
|
||||
} catch (_: Exception) {
|
||||
try {
|
||||
openStream?.close()
|
||||
} catch (_: Exception) {}
|
||||
openStream?.let { closeStreams(it) }
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
.doOnError {
|
||||
try {
|
||||
openStream?.close()
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
.subscribe({}, {})
|
||||
}
|
||||
}
|
||||
|
||||
private val imageConfig: Config
|
||||
|
@ -716,18 +700,16 @@ class PagerPageHolder(
|
|||
return decodeLayout
|
||||
}
|
||||
|
||||
private fun mergeOrSplitPages(imageStream: InputStream, imageStream2: InputStream?): InputStream {
|
||||
private suspend fun mergeOrSplitPages(imageStream: InputStream, imageStream2: InputStream?): InputStream {
|
||||
if (ImageUtil.isAnimatedAndSupported(imageStream)) {
|
||||
imageStream.reset()
|
||||
withContext(Dispatchers.IO) { imageStream.reset() }
|
||||
if (page.longPage == null) {
|
||||
page.longPage = true
|
||||
if (viewer.config.splitPages || imageStream2 != null) {
|
||||
splitDoublePages()
|
||||
}
|
||||
}
|
||||
scope.launchUI {
|
||||
progressBar.completeAndFadeOut()
|
||||
}
|
||||
withUIContext { progressBar.completeAndFadeOut() }
|
||||
return imageStream
|
||||
}
|
||||
if (page.longPage == true && viewer.config.splitPages) {
|
||||
|
@ -735,7 +717,7 @@ class PagerPageHolder(
|
|||
val imageBitmap = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||
} catch (e: Exception) {
|
||||
imageStream.close()
|
||||
closeStreams(imageStream)
|
||||
Timber.e("Cannot split page ${e.message}")
|
||||
return imageBytes.inputStream()
|
||||
}
|
||||
|
@ -756,7 +738,7 @@ class PagerPageHolder(
|
|||
val imageBitmap = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||
} catch (e: Exception) {
|
||||
imageStream.close()
|
||||
closeStreams(imageStream)
|
||||
page.longPage = true
|
||||
splitDoublePages()
|
||||
Timber.e("Cannot split page ${e.message}")
|
||||
|
@ -765,7 +747,7 @@ class PagerPageHolder(
|
|||
val height = imageBitmap.height
|
||||
val width = imageBitmap.width
|
||||
return if (height < width) {
|
||||
imageStream.close()
|
||||
closeStreams(imageStream)
|
||||
page.longPage = true
|
||||
splitDoublePages()
|
||||
val isLTR = (viewer !is R2LPagerViewer).xor(viewer.config.invertDoublePages)
|
||||
|
@ -790,8 +772,7 @@ class PagerPageHolder(
|
|||
val imageBitmap = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||
} catch (e: Exception) {
|
||||
imageStream2.close()
|
||||
imageStream.close()
|
||||
closeStreams(imageStream, imageStream2)
|
||||
page.fullPage = true
|
||||
splitDoublePages()
|
||||
Timber.e("Cannot combine pages ${e.message}")
|
||||
|
@ -802,8 +783,7 @@ class PagerPageHolder(
|
|||
val width = imageBitmap.width
|
||||
|
||||
if (height < width) {
|
||||
imageStream2.close()
|
||||
imageStream.close()
|
||||
closeStreams(imageStream, imageStream2)
|
||||
val oldValue = page.fullPage
|
||||
page.fullPage = true
|
||||
delayPageUpdate {
|
||||
|
@ -827,32 +807,37 @@ class PagerPageHolder(
|
|||
shiftDoublePages(false)
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
} else if ((page.isEndPage == true) &&
|
||||
(if (page.index == 2) !viewer.activity.isFirstPageFull() else true)
|
||||
(if (page.index == 2) !viewer.activity.isFirstPageFull() else true) &&
|
||||
extraPage?.isEndPage != true
|
||||
) {
|
||||
shiftDoublePages(true)
|
||||
extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
} else if (!viewer.activity.manuallyShiftedPages && page.index == 0 && page.isEndPage == true) {
|
||||
// if for some reason the first page should be by itself but its not, fix that
|
||||
shiftDoublePages(true)
|
||||
extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
|
||||
val imageBytes2 = imageStream2.readBytes()
|
||||
val imageBitmap2 = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes2, 0, imageBytes2.size)
|
||||
} catch (e: Exception) {
|
||||
imageStream2.close()
|
||||
imageStream.close()
|
||||
closeStreams(imageStream, imageStream2)
|
||||
extraPage?.fullPage = true
|
||||
page.isolatedPage = true
|
||||
splitDoublePages()
|
||||
Timber.e("Cannot combine pages ${e.message}")
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
scope.launchUI { progressBar.setProgress(97) }
|
||||
withUIContext { progressBar.setProgress(97) }
|
||||
val height2 = imageBitmap2.height
|
||||
val width2 = imageBitmap2.width
|
||||
|
||||
if (height2 < width2) {
|
||||
imageStream2.close()
|
||||
imageStream.close()
|
||||
closeStreams(imageStream, imageStream2)
|
||||
extraPage?.fullPage = true
|
||||
page.isolatedPage = true
|
||||
splitDoublePages()
|
||||
|
@ -864,13 +849,12 @@ class PagerPageHolder(
|
|||
Color.BLACK
|
||||
}
|
||||
|
||||
imageStream.close()
|
||||
imageStream2.close()
|
||||
|
||||
closeStreams(imageStream, imageStream2)
|
||||
if (extraPage?.index == 1 && extraPage?.isStartPage == null && extraPage?.fullPage == null) {
|
||||
extraPage?.isStartPage = ImageUtil.isPagePadded(imageBitmap, rightSide = isLTR)
|
||||
if (extraPage?.isStartPage == true) {
|
||||
shiftDoublePages(true)
|
||||
extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
}
|
||||
|
@ -885,13 +869,13 @@ class PagerPageHolder(
|
|||
}
|
||||
}
|
||||
|
||||
private fun supportHingeIfThere(imageStream: InputStream): InputStream {
|
||||
private suspend fun supportHingeIfThere(imageStream: InputStream): InputStream {
|
||||
if (viewer.config.hingeGapSize > 0 && !ImageUtil.isAnimatedAndSupported(imageStream)) {
|
||||
val imageBytes = imageStream.readBytes()
|
||||
val imageBitmap = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||
} catch (e: Exception) {
|
||||
imageStream.close()
|
||||
closeStreams(imageStream)
|
||||
val wasNotFullPage = page.fullPage != true
|
||||
page.fullPage = true
|
||||
if (wasNotFullPage) {
|
||||
|
@ -917,17 +901,23 @@ class PagerPageHolder(
|
|||
return imageStream
|
||||
}
|
||||
|
||||
private fun shiftDoublePages(shift: Boolean) {
|
||||
private suspend fun closeStreams(stream1: InputStream?, stream2: InputStream? = null) {
|
||||
withContext(Dispatchers.IO) {
|
||||
stream1?.close()
|
||||
stream2?.close()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun shiftDoublePages(shift: Boolean) {
|
||||
delayPageUpdate { viewer.activity.shiftDoublePages(shift) }
|
||||
}
|
||||
|
||||
private fun splitDoublePages() {
|
||||
private suspend fun splitDoublePages() {
|
||||
delayPageUpdate { viewer.splitDoublePages(page) }
|
||||
}
|
||||
|
||||
private fun delayPageUpdate(callback: () -> Unit) {
|
||||
scope.launchUI {
|
||||
delay(100)
|
||||
private suspend fun delayPageUpdate(callback: () -> Unit) {
|
||||
withUIContext {
|
||||
callback()
|
||||
if (extraPage?.fullPage == true || page.fullPage == true) {
|
||||
extraPage = null
|
||||
|
@ -935,13 +925,11 @@ class PagerPageHolder(
|
|||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getBGType(readerTheme: Int, context: Context): Int {
|
||||
return if (readerTheme == 3) {
|
||||
if (context.isInNightMode()) 2 else 1
|
||||
} else {
|
||||
0 + (context.resources.configuration?.orientation ?: 0) * 10
|
||||
}
|
||||
}
|
||||
private fun getBGType(readerTheme: Int, context: Context): Int {
|
||||
return if (readerTheme == 3) {
|
||||
if (context.isInNightMode()) 2 else 1
|
||||
} else {
|
||||
0 + (context.resources.configuration?.orientation ?: 0) * 10
|
||||
} + item.hashCode()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,12 +167,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
|
|||
(oldCurrent?.first as? ReaderPage)?.firstHalf == false
|
||||
} else {
|
||||
oldCurrent?.second == current ||
|
||||
(current.index + 1) < (
|
||||
(
|
||||
oldCurrent?.second
|
||||
?: oldCurrent?.first
|
||||
) as? ReaderPage
|
||||
)?.index ?: 0
|
||||
(current.index + 1) <
|
||||
(((oldCurrent?.second ?: oldCurrent?.first) as? ReaderPage)?.index ?: 0)
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -647,27 +647,29 @@ object ImageUtil {
|
|||
val right = width - left
|
||||
val paddedSide = if (rightSide) right else left
|
||||
val unPaddedSide = if (!rightSide) right else left
|
||||
return (1 until 30).all {
|
||||
return (1 until 30).count {
|
||||
// if all of a side is padded (the left page usually has a white padding on the right when scanned)
|
||||
getPixel(paddedSide, (height * (it / 30f)).roundToInt()).isWhiteOrDark(checkWhite)
|
||||
} && !(1 until 50).all {
|
||||
} >= 27 && !(1 until 50).all {
|
||||
// and if all of the other side isn't padded
|
||||
getPixel(unPaddedSide, (height * (it / 50f)).roundToInt()).isWhiteOrDark(checkWhite)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Bitmap.isOneSideMorePadded(rightSide: Boolean, checkWhite: Boolean): Boolean {
|
||||
val middle = height / 2
|
||||
val paddedSide: (Int) -> Int = { if (rightSide) width - it * 2 else it * 2 }
|
||||
val unPaddedSide: (Int) -> Int = { if (!rightSide) width - it * 2 else it * 2 }
|
||||
// val pixels = IntArray(100)
|
||||
// getPixels(pixels, 0, 2, paddedSide(0), 0)
|
||||
val middle = (height * 0.475).roundToInt()
|
||||
val middle2 = (height * 0.525).roundToInt()
|
||||
val widthFactor = max(1, (width / 400f).roundToInt())
|
||||
val paddedSide: (Int) -> Int = { if (!rightSide) width - it * widthFactor else it * widthFactor }
|
||||
val unPaddedSide: (Int) -> Int = { if (rightSide) width - it * widthFactor else it * widthFactor }
|
||||
return run stop@{
|
||||
(1 until 100).any {
|
||||
(1 until 37).any {
|
||||
if (!getPixel(paddedSide(it), middle).isWhiteOrDark(checkWhite)) return@stop false
|
||||
!getPixel(unPaddedSide(it), middle).isWhiteOrDark(checkWhite)
|
||||
if (!getPixel(paddedSide(it), middle2).isWhiteOrDark(checkWhite)) return@stop false
|
||||
!getPixel(unPaddedSide(it), middle).isWhiteOrDark(checkWhite) ||
|
||||
!getPixel(unPaddedSide(it), middle2).isWhiteOrDark(checkWhite)
|
||||
}
|
||||
} // && getPixels()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.isWhiteOrDark(checkWhite: Boolean): Boolean =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue