同步上游更改: @lhjgege

新漫画增加墨水屏模式 (#5181)
* 新漫画增加墨水屏模式
* 增加水平滚动动画开关
This commit is contained in:
lhjgege
2025-07-11 13:40:10 +08:00
committed by HapeLee
parent 329cdac774
commit 06dd2ac2c1
16 changed files with 463 additions and 29 deletions
@@ -104,6 +104,11 @@ object PreferKey {
const val enableMangaHorizontalScroll = "enableMangaHorizontalScroll"
const val hideMangaTitle = "hideMangaTitle"
const val mangaColorFilter = "mangaColorFilter"
const val enableMangaEInk = "enableMangaEInk"
const val mangaEInkThreshold = "mangaEInkThreshold"
const val disableHorizontalAnimator = "disableHorizontalAnimator"
const val enableMangaGray = "enableMangaGray"
const val autoRefresh = "auto_refresh"
const val defaultToRead = "defaultToRead"
const val exportCharset = "exportCharset"
@@ -496,7 +496,7 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val defaultHomePage get() = appCtx.getPrefString(PreferKey.defaultHomePage, "bookshelf")
val updateToVariant get() = appCtx.getPrefString(PreferKey.updateToVariant, "default_version")
val updateToVariant get() = appCtx.getPrefString(PreferKey.updateToVariant, "official_version")
val streamReadAloudAudio get() = appCtx.getPrefBoolean(PreferKey.streamReadAloudAudio, false)
@@ -676,5 +676,30 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
set(value) {
appCtx.putPrefBoolean(PreferKey.hideMangaTitle, value)
}
//开启墨水屏模式
var enableMangaEInk
get() = appCtx.getPrefBoolean(PreferKey.enableMangaEInk, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.enableMangaEInk, value)
}
var mangaEInkThreshold
get() = appCtx.getPrefInt(PreferKey.mangaEInkThreshold, 150)
set(value) {
appCtx.putPrefInt(PreferKey.mangaEInkThreshold, value)
}
var disableHorizontalAnimator
get() = appCtx.getPrefBoolean(PreferKey.disableHorizontalAnimator, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.disableHorizontalAnimator, value)
}
var enableMangaGray
get() = appCtx.getPrefBoolean(PreferKey.enableMangaGray, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.enableMangaGray, value)
}
}
@@ -2,12 +2,14 @@ package io.legato.kazusa.model
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.BitmapDrawable
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import androidx.annotation.Keep
import androidx.core.graphics.drawable.toDrawable
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.Transformation
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.CenterCrop
@@ -74,7 +76,7 @@ object BookCover {
return
}
defaultDrawable = kotlin.runCatching {
BitmapDrawable(appCtx.resources, BitmapUtils.decodeBitmap(path, 600, 900))
BitmapUtils.decodeBitmap(path, 600, 900).toDrawable(appCtx.resources)
}.getOrDefault(appCtx.resources.getDrawable(R.drawable.image_cover_default, null))
}
@@ -87,7 +89,7 @@ object BookCover {
path: String?,
loadOnlyWifi: Boolean = false,
sourceOrigin: String? = null,
onLoadFinish: (() -> Unit)? = null
onLoadFinish: (() -> Unit)? = null,
): RequestBuilder<Drawable> {
if (AppConfig.useDefaultCover) {
return ImageLoader.load(context, defaultDrawable)
@@ -105,7 +107,7 @@ object BookCover {
e: GlideException?,
model: Any?,
target: Target<Drawable?>,
isFirstResource: Boolean
isFirstResource: Boolean,
): Boolean {
onLoadFinish.invoke()
return false
@@ -116,7 +118,7 @@ object BookCover {
model: Any,
target: Target<Drawable?>?,
dataSource: DataSource,
isFirstResource: Boolean
isFirstResource: Boolean,
): Boolean {
onLoadFinish.invoke()
return false
@@ -133,11 +135,13 @@ object BookCover {
/**
* 加载漫画图片
*/
@SuppressLint("CheckResult")
fun loadManga(
context: Context,
path: String?,
loadOnlyWifi: Boolean = false,
sourceOrigin: String? = null,
transformation: Transformation<Bitmap>? = null,
): RequestBuilder<Drawable> {
var options = RequestOptions().set(OkHttpModelLoader.loadOnlyWifiOption, loadOnlyWifi)
.set(OkHttpModelLoader.mangaOption, true)
@@ -148,7 +152,12 @@ object BookCover {
.apply(options)
.override(context.resources.displayMetrics.widthPixels, SIZE_ORIGINAL)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(true)
.skipMemoryCache(true).let {
if (transformation != null) {
it.transform(transformation)
}
it
}
}
fun preloadManga(
@@ -6,8 +6,11 @@ import android.view.View
import io.legato.kazusa.BuildConfig
import io.legato.kazusa.R
import io.legato.kazusa.base.BaseBottomSheetDialogFragment
import io.legato.kazusa.constant.AppConst
import io.legato.kazusa.databinding.DialogUpdateBinding
import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.help.update.AppUpdate
import io.legato.kazusa.help.update.AppVariant
import io.legato.kazusa.model.Download
import io.legato.kazusa.utils.toastOnUi
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
@@ -27,6 +30,14 @@ class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
}
}
private val checkVariant: AppVariant
get() = when (AppConfig.updateToVariant) {
"official_version" -> AppVariant.OFFICIAL
"beta_release_version" -> AppVariant.BETA_RELEASE
"beta_releaseA_version" -> AppVariant.BETA_RELEASEA
else -> AppConst.appInfo.appVariant
}
val binding by viewBinding(DialogUpdateBinding::bind)
override fun onStart() {
@@ -41,6 +52,7 @@ class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
binding.tvAbi.text = Build.SUPPORTED_ABIS.firstOrNull() ?: "unknown"
binding.tvUrl.text = arguments?.getString("url")
binding.tvVariable.text = checkVariant.toString()
val updateBody = arguments?.getString("updateBody")
if (updateBody == null) {
@@ -5,7 +5,6 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.os.PersistableBundle
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
@@ -44,6 +43,7 @@ import io.legato.kazusa.ui.book.changesource.ChangeBookSourceDialog
import io.legato.kazusa.ui.book.info.BookInfoActivity
import io.legato.kazusa.ui.book.manga.config.MangaColorFilterConfig
import io.legato.kazusa.ui.book.manga.config.MangaColorFilterDialog
import io.legato.kazusa.ui.book.manga.config.MangaEpaperDialog
import io.legato.kazusa.ui.book.manga.config.MangaFooterConfig
import io.legato.kazusa.ui.book.manga.config.MangaFooterSettingDialog
import io.legato.kazusa.ui.book.manga.entities.BaseMangaPage
@@ -63,7 +63,6 @@ import io.legato.kazusa.utils.canScroll
import io.legato.kazusa.utils.fastBinarySearch
import io.legato.kazusa.utils.findCenterViewPosition
import io.legato.kazusa.utils.fromJsonObject
import io.legato.kazusa.utils.getCompatColor
import io.legato.kazusa.utils.gone
import io.legato.kazusa.utils.observeEvent
import io.legato.kazusa.utils.showDialogFragment
@@ -79,7 +78,7 @@ import kotlin.math.ceil
class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewModel>(),
ReadManga.Callback, ChangeBookSourceDialog.CallBack, MangaMenu.CallBack,
MangaColorFilterDialog.Callback, ScrollTimer.ScrollCallback {
MangaColorFilterDialog.Callback, ScrollTimer.ScrollCallback, MangaEpaperDialog.Callback {
private val mLayoutManager by lazy {
MangaLayoutManager(this)
@@ -191,6 +190,8 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
?: MangaColorFilterConfig()
mAdapter.run {
setMangaImageColorFilter(mangaColorFilter)
enableMangaEInk(AppConfig.enableMangaEInk, AppConfig.mangaEInkThreshold)
enableGray(AppConfig.enableMangaGray)
}
setHorizontalScroll(AppConfig.enableMangaHorizontalScroll)
binding.recyclerView.run {
@@ -541,6 +542,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
R.id.menu_enable_horizontal_scroll -> {
item.isChecked = !item.isChecked
AppConfig.enableMangaHorizontalScroll = item.isChecked
mMenu?.findItem(R.id.menu_disable_horizontal_animation)?.isVisible = item.isChecked
setHorizontalScroll(item.isChecked)
mAdapter.notifyDataSetChanged()
}
@@ -570,6 +572,38 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
AppConfig.hideMangaTitle = item.isChecked
ReadManga.loadContent()
}
R.id.menu_epaper_manga -> {
item.isChecked = !item.isChecked
AppConfig.enableMangaEInk = item.isChecked
mMenu?.findItem(R.id.menu_gray_manga)?.isChecked = false
AppConfig.enableMangaGray = false
mMenu?.findItem(R.id.menu_epaper_manga_setting)?.isVisible = item.isChecked
mAdapter.enableMangaEInk(item.isChecked, AppConfig.mangaEInkThreshold)
}
R.id.menu_epaper_manga_setting -> {
showDialogFragment(MangaEpaperDialog())
}
R.id.menu_disable_horizontal_animation -> {
item.isChecked = !item.isChecked
AppConfig.disableHorizontalAnimator = item.isChecked
if (item.isChecked) {
mPagerSnapHelper.attachToRecyclerView(null)
} else {
mPagerSnapHelper.attachToRecyclerView(binding.recyclerView)
}
}
R.id.menu_gray_manga -> {
item.isChecked = !item.isChecked
AppConfig.enableMangaGray = item.isChecked
mMenu?.findItem(R.id.menu_epaper_manga)?.isChecked = false
AppConfig.enableMangaEInk = false
mMenu?.findItem(R.id.menu_epaper_manga_setting)?.isVisible = false
mAdapter.enableGray(item.isChecked)
}
}
return super.onCompatOptionsItemSelected(item)
}
@@ -626,6 +660,11 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
if (enable) {
if (!enableAutoScroll) {
mPagerSnapHelper.attachToRecyclerView(binding.recyclerView)
if (AppConfig.disableHorizontalAnimator) {
mPagerSnapHelper.attachToRecyclerView(null)
} else {
mPagerSnapHelper.attachToRecyclerView(binding.recyclerView)
}
}
mLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
} else {
@@ -646,6 +685,14 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
menu.findItem(R.id.menu_enable_horizontal_scroll).isChecked =
AppConfig.enableMangaHorizontalScroll
menu.findItem(R.id.menu_hide_manga_title).isChecked = AppConfig.hideMangaTitle
menu.findItem(R.id.menu_epaper_manga).isChecked = AppConfig.enableMangaEInk
menu.findItem(R.id.menu_epaper_manga_setting).isVisible = AppConfig.enableMangaEInk
menu.findItem(R.id.menu_disable_horizontal_animation).run {
isVisible =
AppConfig.enableMangaHorizontalScroll
isChecked = AppConfig.disableHorizontalAnimator
}
menu.findItem(R.id.menu_gray_manga).isChecked = AppConfig.enableMangaGray
}
private fun setDisableMangaScale(disable: Boolean) {
@@ -784,4 +831,8 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
}
return super.onKeyDown(keyCode, event)
}
override fun updateEepaper(value: Int) {
mAdapter.updateThreshold(value)
}
}
@@ -0,0 +1,49 @@
package io.legato.kazusa.ui.book.manga.config
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import io.legato.kazusa.R
import io.legato.kazusa.base.BaseBottomSheetDialogFragment
import io.legato.kazusa.databinding.DialogMangaEpaperBinding
import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
class MangaEpaperDialog : BaseBottomSheetDialogFragment(R.layout.dialog_manga_epaper) {
private val binding by viewBinding(DialogMangaEpaperBinding::bind)
private val callback get() = activity as? Callback
private var mMangaEInkThreshold = 150
// override fun onStart() {
// super.onStart()
// dialog?.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
// setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
// }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
initData()
initView()
}
private fun initData() {
binding.dsbEpaper.progress = AppConfig.mangaEInkThreshold
}
private fun initView() {
binding.dsbEpaper.onChanged = {
mMangaEInkThreshold = it
callback?.updateEepaper(it)
}
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
AppConfig.mangaEInkThreshold = mMangaEInkThreshold
}
interface Callback {
fun updateEepaper(value: Int)
}
}
@@ -0,0 +1,80 @@
package io.legato.kazusa.ui.book.manga.entities
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import androidx.annotation.IntRange
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import java.security.MessageDigest
/**
* 墨水屏图片转换器。
* 将彩色图片转换为灰度图,并可选择进行简单的二值化处理,以提高墨水屏显示效果。
*
* @param threshold 二值化的阈值(0-255)。低于此值的像素变为黑色,高于此值的像素变为白色。
* 仅当applyBinarization为true时有效。
*/
class EpaperTransformation(
@IntRange(0, 255) private val threshold: Int = 128,
) : BitmapTransformation() {
private val ID =
"io.legado.app.model.EpaperTransformation.${threshold}"
private val ID_BYTES = ID.toByteArray(CHARSET)
override fun transform(
pool: BitmapPool,
toTransform: Bitmap,
outWidth: Int,
outHeight: Int,
): Bitmap {
val resultBitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(resultBitmap)
val paint = Paint()
val colorMatrix = ColorMatrix()
colorMatrix.setSaturation(0f)
val filter = ColorMatrixColorFilter(colorMatrix)
paint.colorFilter = filter
canvas.drawBitmap(toTransform, 0f, 0f, paint)
val pixels = IntArray(outWidth * outHeight)
resultBitmap.getPixels(pixels, 0, outWidth, 0, 0, outWidth, outHeight)
for (i in pixels.indices) {
val pixel = pixels[i]
val gray = Color.red(pixel)
pixels[i] =
if (gray < threshold) Color.BLACK else Color.WHITE
}
resultBitmap.setPixels(pixels, 0, outWidth, 0, 0, outWidth, outHeight)
return resultBitmap
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(ID_BYTES)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as EpaperTransformation
if (threshold != other.threshold) return false
if (ID != other.ID) return false
return true
}
override fun hashCode(): Int {
var result = 31 + threshold
result = 31 * result + ID.hashCode()
return result
}
}
@@ -0,0 +1,57 @@
package io.legato.kazusa.ui.book.manga.entities
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
class GrayscaleTransformation : BitmapTransformation() {
private val ID = "io.legado.app.model.GrayscaleTransformation"
private val ID_BYTES = ID.toByteArray(StandardCharsets.UTF_8)
override fun transform(
pool: BitmapPool,
toTransform: Bitmap,
outWidth: Int,
outHeight: Int,
): Bitmap {
val resultBitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(resultBitmap)
val paint = Paint()
val matrix = ColorMatrix(
floatArrayOf(
0.299f, 0.587f, 0.114f, 0f, 0f,
0.299f, 0.587f, 0.114f, 0f, 0f,
0.299f, 0.587f, 0.114f, 0f, 0f,
0f, 0f, 0f, 1f, 0f
)
)
val filter = ColorMatrixColorFilter(matrix)
paint.colorFilter = filter
canvas.drawBitmap(toTransform, 0f, 0f, paint)
return resultBitmap
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(ID_BYTES)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as GrayscaleTransformation
return ID == other.ID
}
override fun hashCode(): Int {
return ID.hashCode()
}
}
@@ -8,6 +8,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import androidx.annotation.IntRange
import androidx.core.util.size
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
@@ -16,6 +17,7 @@ import androidx.viewbinding.ViewBinding
import com.bumptech.glide.Glide
import com.bumptech.glide.ListPreloader.PreloadModelProvider
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import io.legato.kazusa.base.adapter.ItemViewHolder
import io.legato.kazusa.base.adapter.RecyclerAdapter.Companion.TYPE_FOOTER_VIEW
import io.legato.kazusa.databinding.ItemBookMangaEdgeBinding
@@ -24,6 +26,8 @@ import io.legato.kazusa.help.glide.progress.ProgressManager
import io.legato.kazusa.model.BookCover
import io.legato.kazusa.model.ReadManga
import io.legato.kazusa.ui.book.manga.config.MangaColorFilterConfig
import io.legato.kazusa.ui.book.manga.entities.EpaperTransformation
import io.legato.kazusa.ui.book.manga.entities.GrayscaleTransformation
import io.legato.kazusa.ui.book.manga.entities.MangaPage
import io.legato.kazusa.ui.book.manga.entities.ReaderLoading
import io.legato.kazusa.utils.dpToPx
@@ -34,6 +38,8 @@ class MangaAdapter(private val context: Context) :
private val inflater: LayoutInflater = LayoutInflater.from(context)
private lateinit var mConfig: MangaColorFilterConfig
private var mTransformation: BitmapTransformation? = null
private var currentMangaEInkThreshold = 0
companion object {
private const val LOADING_VIEW = 0
@@ -91,7 +97,7 @@ class MangaAdapter(private val context: Context) :
if (item is MangaPage) {
val isLastImage = item.imageCount > 0 && item.index == item.imageCount - 1
loadImageWithRetry(
item.mImageUrl, isHorizontal, isLastImage
item.mImageUrl, isHorizontal, isLastImage, mTransformation
)
}
}
@@ -100,7 +106,7 @@ class MangaAdapter(private val context: Context) :
fun onBind(item: MangaPage) {
setImageColorFilter()
val isLastImage = item.imageCount > 0 && item.index == item.imageCount - 1
loadImageWithRetry(item.mImageUrl, isHorizontal, isLastImage)
loadImageWithRetry(item.mImageUrl, isHorizontal, isLastImage, mTransformation)
}
fun setImageColorFilter() {
@@ -167,7 +173,7 @@ class MangaAdapter(private val context: Context) :
}
}
fun getFooterCount() = footerItems.size()
fun getFooterCount() = footerItems.size
private fun isFooter(position: Int) = position >= getActualItemCount()
@@ -199,8 +205,8 @@ class MangaAdapter(private val context: Context) :
@Synchronized
fun addFooterView(footer: ((parent: ViewGroup) -> ViewBinding)) {
kotlin.runCatching {
val index = getActualItemCount() + footerItems.size()
footerItems.put(TYPE_FOOTER_VIEW + footerItems.size(), footer)
val index = getActualItemCount() + footerItems.size
footerItems.put(TYPE_FOOTER_VIEW + footerItems.size, footer)
notifyItemInserted(index)
}
}
@@ -244,4 +250,32 @@ class MangaAdapter(private val context: Context) :
notifyItemRangeChanged(0, itemCount)
}
fun enableMangaEInk(enable: Boolean, value: Int) {
if (enable) {
currentMangaEInkThreshold = value
mTransformation = EpaperTransformation(currentMangaEInkThreshold)
} else {
mTransformation = null
}
notifyItemRangeChanged(0, itemCount)
}
fun updateThreshold(mangaEInkThreshold: Int) {
if (currentMangaEInkThreshold != mangaEInkThreshold) {
currentMangaEInkThreshold = mangaEInkThreshold
mTransformation = EpaperTransformation(currentMangaEInkThreshold)
notifyItemRangeChanged(0, itemCount)
}
}
//开启灰色图片
fun enableGray(enable: Boolean) {
mTransformation = if (enable) {
GrayscaleTransformation()
} else {
null
}
notifyItemRangeChanged(0, itemCount)
}
}
@@ -2,6 +2,7 @@ package io.legato.kazusa.ui.book.manga.recyclerview
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.Gravity
import android.view.ViewGroup
@@ -17,6 +18,7 @@ import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.Transformation
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
@@ -51,7 +53,12 @@ open class MangaVH<VB : ViewBinding>(val binding: VB, private val context: Conte
}
@SuppressLint("CheckResult")
fun loadImageWithRetry(imageUrl: String, isHorizontal: Boolean, isLastImage: Boolean) {
fun loadImageWithRetry(
imageUrl: String,
isHorizontal: Boolean,
isLastImage: Boolean,
transformation: Transformation<Bitmap>?
) {
mFlProgress.isVisible = true
mLoading.isVisible = true
mRetry?.isGone = true
@@ -67,6 +74,7 @@ open class MangaVH<VB : ViewBinding>(val binding: VB, private val context: Conte
context,
imageUrl,
sourceOrigin = ReadManga.book?.origin,
transformation = transformation
).addListener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,