feat(novel-reader): Add safe coroutine scope for EbookReaderView (#536)
This commit is contained in:
parent
0d22c92e3e
commit
ce332d7ae5
1 changed files with 47 additions and 1 deletions
|
@ -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())
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue