feat(novel-reader): Add safe coroutine scope for EbookReaderView (#536)

This commit is contained in:
Vipul Tyagi 2024-12-01 19:55:19 +05:30 committed by GitHub
parent 0d22c92e3e
commit ce332d7ae5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,6 +2,7 @@ package ani.dantotsu.media.novel.novelreader
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
@ -45,12 +46,17 @@ import ani.dantotsu.tryWith
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import com.vipulog.ebookreader.Book import com.vipulog.ebookreader.Book
import com.vipulog.ebookreader.EbookReaderEventListener import com.vipulog.ebookreader.EbookReaderEventListener
import com.vipulog.ebookreader.EbookReaderView
import com.vipulog.ebookreader.ReaderError import com.vipulog.ebookreader.ReaderError
import com.vipulog.ebookreader.ReaderFlow import com.vipulog.ebookreader.ReaderFlow
import com.vipulog.ebookreader.ReaderTheme import com.vipulog.ebookreader.ReaderTheme
import com.vipulog.ebookreader.RelocationInfo import com.vipulog.ebookreader.RelocationInfo
import com.vipulog.ebookreader.TocItem import com.vipulog.ebookreader.TocItem
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -190,6 +196,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private fun setupViews() { private fun setupViews() {
binding.bookReader.useSafeScope(this)
scope.launch { binding.bookReader.openBook(intent.data!!) } scope.launch { binding.bookReader.openBook(intent.data!!) }
binding.bookReader.setEbookReaderListener(this) binding.bookReader.setEbookReaderListener(this)
@ -540,4 +548,42 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
hideSystemBars() hideSystemBars()
} }
} }
} }
/**
* TEMPORARY HOTFIX
*
* This is a hacky workaround to handle crashes in the deprecated ebookreader library.
*
* Current implementation:
* - Uses reflection to access the private `scope` field in `EbookReaderView`.
* - Replaces the existing `CoroutineScope` with a new one that includes a
* `CoroutineExceptionHandler`.
* - Ensures that uncaught exceptions in coroutines are handled gracefully by showing a snackbar
* with error details.
*
* TODO:
* - This is NOT a long-term solution
* - The underlying library is archived and unmaintained
* - Schedule migration to an actively maintained library
* - Consider alternatives like https://github.com/readium/kotlin-toolkit
*/
fun EbookReaderView.useSafeScope(activity: Activity) {
runCatching {
val scopeField = javaClass.getDeclaredField("scope").apply { isAccessible = true }
val currentScope = scopeField.get(this) as CoroutineScope
val safeScope = CoroutineScope(
SupervisorJob() +
currentScope.coroutineContext.minusKey(Job) +
scopeExceptionHandler(activity)
)
scopeField.set(this, safeScope)
}.onFailure { e ->
snackString(e.localizedMessage, activity, e.stackTraceToString())
}
}
private fun scopeExceptionHandler(activity: Activity) = CoroutineExceptionHandler { _, e ->
snackString(e.localizedMessage, activity, e.stackTraceToString())
}