replace subscription with job in PagerPageHolder

also more fixes to the auto shifting
This commit is contained in:
Jays2Kings 2023-02-28 02:27:46 -05:00
parent d93f9d6a45
commit 6872616db5
4 changed files with 109 additions and 115 deletions

View file

@ -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) }

View file

@ -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()
}
}

View file

@ -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)
},
)

View file

@ -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 =