Skip to content

Commit

Permalink
feat(novel-reader): Add safe coroutine scope for EbookReaderView (#536)
Browse files Browse the repository at this point in the history
  • Loading branch information
VipulOG authored Dec 1, 2024
1 parent 0d22c92 commit ce332d7
Showing 1 changed file with 47 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ani.dantotsu.media.novel.novelreader

import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
Expand Down Expand Up @@ -45,12 +46,17 @@ import ani.dantotsu.tryWith
import com.google.android.material.slider.Slider
import com.vipulog.ebookreader.Book
import com.vipulog.ebookreader.EbookReaderEventListener
import com.vipulog.ebookreader.EbookReaderView
import com.vipulog.ebookreader.ReaderError
import com.vipulog.ebookreader.ReaderFlow
import com.vipulog.ebookreader.ReaderTheme
import com.vipulog.ebookreader.RelocationInfo
import com.vipulog.ebookreader.TocItem
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
Expand Down Expand Up @@ -190,6 +196,8 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {

@SuppressLint("ClickableViewAccessibility")
private fun setupViews() {
binding.bookReader.useSafeScope(this)

scope.launch { binding.bookReader.openBook(intent.data!!) }
binding.bookReader.setEbookReaderListener(this)

Expand Down Expand Up @@ -540,4 +548,42 @@ class NovelReaderActivity : AppCompatActivity(), EbookReaderEventListener {
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())
}

0 comments on commit ce332d7

Please sign in to comment.