Merge branch 'gedoor:master' into master

This commit is contained in:
huolibao 2024-02-26 12:31:28 +08:00 committed by GitHub
commit ce13ee9d1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 273 additions and 65 deletions

View File

@ -8,6 +8,7 @@ import io.legado.app.constant.AppLog
import io.legado.app.data.entities.rule.RowUi
import io.legado.app.help.CacheManager
import io.legado.app.help.JsExtensions
import io.legado.app.help.SymmetricCryptoAndroid
import io.legado.app.help.config.AppConfig
import io.legado.app.help.http.CookieStore
import io.legado.app.model.SharedJsScope
@ -106,6 +107,7 @@ interface BaseSource : JsExtensions {
it.lastIndexOf("<")
)
).toString()
else -> it
}
).getOrNull()?.let { map ->
@ -176,7 +178,7 @@ interface BaseSource : JsExtensions {
fun putLoginInfo(info: String): Boolean {
return try {
val key = (AppConst.androidId).encodeToByteArray(0, 16)
val encodeStr = AES(key).encryptBase64(info)
val encodeStr = SymmetricCryptoAndroid("AES", key).encryptBase64(info)
CacheManager.put("userInfo_${getKey()}", encodeStr)
true
} catch (e: Exception) {

View File

@ -42,7 +42,7 @@ interface JsEncodeUtils {
key: ByteArray?,
iv: ByteArray?
): SymmetricCrypto {
val symmetricCrypto = SymmetricCrypto(transformation, key)
val symmetricCrypto = SymmetricCryptoAndroid(transformation, key)
return if (iv != null && iv.isNotEmpty()) symmetricCrypto.setIv(iv) else symmetricCrypto
}

View File

@ -0,0 +1,17 @@
package io.legado.app.help
import android.graphics.Paint
import io.legado.app.utils.objectpool.BaseSafeObjectPool
object PaintPool : BaseSafeObjectPool<Paint>(8) {
private val emptyPaint = Paint()
override fun create(): Paint = Paint()
override fun recycle(target: Paint) {
target.set(emptyPaint)
super.recycle(target)
}
}

View File

@ -0,0 +1,33 @@
package io.legado.app.help
import cn.hutool.crypto.symmetric.SymmetricCrypto
import io.legado.app.utils.EncoderUtils
import java.io.InputStream
import java.nio.charset.Charset
class SymmetricCryptoAndroid(
algorithm: String,
key: ByteArray?,
) : SymmetricCrypto(algorithm, key) {
override fun encryptBase64(data: ByteArray): String {
return EncoderUtils.base64Encode(encrypt(data))
}
override fun encryptBase64(data: String, charset: String?): String {
return EncoderUtils.base64Encode(encrypt(data, charset))
}
override fun encryptBase64(data: String, charset: Charset?): String {
return EncoderUtils.base64Encode(encrypt(data, charset))
}
override fun encryptBase64(data: String): String {
return EncoderUtils.base64Encode(encrypt(data))
}
override fun encryptBase64(data: InputStream): String {
return EncoderUtils.base64Encode(encrypt(data))
}
}

View File

@ -55,7 +55,6 @@ object ThemeConfig {
initNightMode()
BookCover.upDefaultCover()
postEvent(EventBus.RECREATE, "")
postEvent(EventBus.UP_CONFIG, arrayOf(2, 9))
}
private fun initNightMode() {
@ -74,10 +73,12 @@ object ThemeConfig {
context.getPrefString(PreferKey.bgImage),
context.getPrefInt(PreferKey.bgImageBlurring, 0)
)
Theme.Dark -> Pair(
context.getPrefString(PreferKey.bgImageN),
context.getPrefInt(PreferKey.bgImageNBlurring, 0)
)
else -> null
} ?: return null
if (bgCfg.first.isNullOrBlank()) return null
@ -218,6 +219,7 @@ object ThemeConfig {
.bottomBackground(Color.WHITE)
.apply()
}
AppConfig.isNightTheme -> {
val primary =
getPrefInt(PreferKey.cNPrimary, getCompatColor(R.color.md_blue_grey_600))
@ -238,6 +240,7 @@ object ThemeConfig {
.bottomBackground(ColorUtils.withAlpha(bBackground, 1f))
.apply()
}
else -> {
val primary =
getPrefInt(PreferKey.cPrimary, getCompatColor(R.color.md_brown_500))

View File

@ -250,12 +250,12 @@ class DownloadService : BaseService() {
.setSubText(getString(R.string.action_download))
.setContentTitle(content)
.setContentIntent(
servicePendingIntent<DownloadService>(IntentAction.play) {
servicePendingIntent<DownloadService>(IntentAction.play, downloadId.toInt()) {
putExtra("downloadId", downloadId)
}
)
.setDeleteIntent(
servicePendingIntent<DownloadService>(IntentAction.stop) {
servicePendingIntent<DownloadService>(IntentAction.stop, downloadId.toInt()) {
putExtra("downloadId", downloadId)
}
)

View File

@ -422,7 +422,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
var curLine = curTextLines[lineIndex]
length = length - currentPage.text.length + curLine.text.length
if (curLine.isParagraphEnd) length++
while (length < contentPosition && lineIndex + 1 < curTextLines.size) {
while (length <= contentPosition && lineIndex + 1 < curTextLines.size) {
lineIndex += 1
curLine = curTextLines[lineIndex]
length += curLine.text.length

View File

@ -31,6 +31,7 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
import io.legado.app.ui.book.read.page.provider.TextPageFactory
import io.legado.app.utils.activity
import io.legado.app.utils.canvasrecorder.pools.BitmapPool
import io.legado.app.utils.invisible
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.throttle
@ -460,6 +461,8 @@ class ReadView(context: Context, attrs: AttributeSet) :
fun onDestroy() {
pageDelegate?.onDestroy()
curPage.cancelSelect()
invalidateTextPage()
BitmapPool.clear()
}
/**

View File

@ -1,14 +1,20 @@
package io.legado.app.ui.book.read.page.entities
import android.annotation.SuppressLint
import android.graphics.Canvas
import android.graphics.Paint.FontMetrics
import android.os.Build
import androidx.annotation.Keep
import androidx.core.graphics.withTranslation
import io.legado.app.help.PaintPool
import io.legado.app.help.book.isImage
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.model.ReadBook
import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.entities.TextPage.Companion.emptyTextPage
import io.legado.app.ui.book.read.page.entities.column.BaseColumn
import io.legado.app.ui.book.read.page.entities.column.TextColumn
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
@ -32,6 +38,13 @@ data class TextLine(
val isTitle: Boolean = false,
var isParagraphEnd: Boolean = false,
var isImage: Boolean = false,
var startX: Float = 0f,
var indentSize: Int = 0,
var extraLetterSpacing: Float = 0f,
var extraLetterSpacingOffsetX: Float = 0f,
var wordSpacing: Float = 0f,
var exceed: Boolean = false,
var onlyTextColumn: Boolean = true,
) {
val columns: List<BaseColumn> get() = textColumns
@ -41,6 +54,7 @@ data class TextLine(
val chapterIndices: IntRange get() = chapterPosition..chapterPosition + charSize
val height: Float inline get() = lineBottom - lineTop
val canvasRecorder = CanvasRecorderFactory.create()
var searchResultColumnCount = 0
var isReadAloud: Boolean = false
set(value) {
if (field != value) {
@ -52,6 +66,9 @@ data class TextLine(
var isLeftLine = true
fun addColumn(column: BaseColumn) {
if (column !is TextColumn) {
onlyTextColumn = false
}
column.textLine = this
textColumns.add(column)
}
@ -129,14 +146,57 @@ data class TextLine(
}
private fun drawTextLine(view: ContentTextView, canvas: Canvas) {
for (i in columns.indices) {
columns[i].draw(view, canvas)
if (checkFastDraw()) {
fastDrawTextLine(view, canvas)
} else {
for (i in columns.indices) {
columns[i].draw(view, canvas)
}
}
if (ReadBookConfig.underline && !isImage && ReadBook.book?.isImage != true) {
drawUnderline(canvas)
}
}
@SuppressLint("NewApi")
private fun fastDrawTextLine(view: ContentTextView, canvas: Canvas) {
val textPaint = if (isTitle) {
ChapterProvider.titlePaint
} else {
ChapterProvider.contentPaint
}
val textColor = if (isReadAloud) {
ThemeStore.accentColor
} else {
ReadBookConfig.textColor
}
val paint = PaintPool.obtain()
paint.set(textPaint)
if (extraLetterSpacing != 0f) {
paint.letterSpacing += extraLetterSpacing
}
if (wordSpacing != 0f) {
paint.wordSpacing = wordSpacing
}
if (paint.color != textColor) {
paint.color = textColor
}
if (extraLetterSpacingOffsetX != 0f) {
canvas.withTranslation(extraLetterSpacingOffsetX) {
canvas.drawText(text, indentSize, text.length, startX, lineBase - lineTop, paint)
}
} else {
canvas.drawText(text, indentSize, text.length, startX, lineBase - lineTop, paint)
}
PaintPool.recycle(paint)
for (i in columns.indices) {
val column = columns[i] as TextColumn
if (column.selected) {
canvas.drawRect(column.start, 0f, column.end, height, view.selectedPaint)
}
}
}
/**
* 绘制下划线
*/
@ -151,6 +211,16 @@ data class TextLine(
)
}
fun checkFastDraw(): Boolean {
if (exceed || !onlyTextColumn || textPage.isMsgPage) {
return false
}
if (!atLeastApi29 && wordSpacing != 0f) {
return false
}
return searchResultColumnCount == 0
}
fun invalidate() {
invalidateSelf()
textPage.invalidate()
@ -166,6 +236,7 @@ data class TextLine(
companion object {
val emptyTextLine = TextLine()
private val atLeastApi29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
}

View File

@ -1,11 +1,13 @@
package io.legado.app.ui.book.read.page.entities
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.StaticLayout
import androidx.annotation.Keep
import androidx.core.graphics.withTranslation
import io.legado.app.R
import io.legado.app.help.PaintPool
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
@ -13,6 +15,7 @@ import io.legado.app.ui.book.read.page.entities.column.TextColumn
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeeded
import io.legado.app.utils.dpToPx
import splitties.init.appCtx
import java.text.DecimalFormat
import kotlin.math.min
@ -45,7 +48,7 @@ data class TextPage(
var isMsgPage: Boolean = false
var canvasRecorder = CanvasRecorderFactory.create(true)
var doublePage = false
var paddingTop = 0
var paddingTop = ChapterProvider.paddingTop
var isCompleted = false
@JvmField
@ -277,6 +280,21 @@ data class TextPage(
}
}
private fun drawDebugInfo(canvas: Canvas) {
ChapterProvider.run {
val paint = PaintPool.obtain()
paint.style = Paint.Style.STROKE
canvas.drawRect(
paddingLeft.toFloat(),
0f,
(paddingLeft + visibleWidth).toFloat(),
height - 1.dpToPx(),
paint
)
PaintPool.recycle(paint)
}
}
private fun drawPage(view: ContentTextView, canvas: Canvas) {
for (i in lines.indices) {
val line = lines[i]

View File

@ -2,6 +2,7 @@ package io.legado.app.ui.book.read.page.entities.column
import android.graphics.Canvas
import androidx.annotation.Keep
import io.legado.app.help.PaintPool
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.ui.book.read.page.ContentTextView
@ -32,6 +33,11 @@ data class TextColumn(
set(value) {
if (field != value) {
textLine.invalidate()
if (value) {
textLine.searchResultColumnCount++
} else {
textLine.searchResultColumnCount--
}
}
field = value
}
@ -42,15 +48,18 @@ data class TextColumn(
} else {
ChapterProvider.contentPaint
}
if (textLine.isReadAloud || isSearchResult) {
synchronized(textPaint) {
textPaint.color = ThemeStore.accentColor
canvas.drawText(charData, start, textLine.lineBase - textLine.lineTop, textPaint)
textPaint.color = ReadBookConfig.textColor
}
val textColor = if (textLine.isReadAloud || isSearchResult) {
ThemeStore.accentColor
} else {
canvas.drawText(charData, start, textLine.lineBase - textLine.lineTop, textPaint)
ReadBookConfig.textColor
}
val paint = PaintPool.obtain()
paint.set(textPaint)
if (paint.color != textColor) {
paint.color = textColor
}
canvas.drawText(charData, start, textLine.lineBase - textLine.lineTop, paint)
PaintPool.recycle(paint)
if (selected) {
canvas.drawRect(start, 0f, end, textLine.height, view.selectedPaint)
}

View File

@ -879,6 +879,7 @@ object ChapterProvider {
tPaint.typeface = titleFont
tPaint.textSize = with(ReadBookConfig) { textSize + titleSize }.toFloat().spToPx()
tPaint.isAntiAlias = true
tPaint.isLinearText = true
//正文
val cPaint = TextPaint()
cPaint.color = ReadBookConfig.textColor
@ -886,6 +887,7 @@ object ChapterProvider {
cPaint.typeface = textFont
cPaint.textSize = ReadBookConfig.textSize.toFloat().spToPx()
cPaint.isAntiAlias = true
cPaint.isLinearText = true
return Pair(tPaint, cPaint)
}

View File

@ -31,6 +31,7 @@ import kotlinx.coroutines.launch
import java.util.LinkedList
import java.util.Locale
import kotlin.coroutines.coroutineContext
import kotlin.math.roundToInt
class TextChapterLayout(
scope: CoroutineScope,
@ -467,7 +468,7 @@ class TextChapterLayout(
//第一行 非标题
textLine.text = lineText
addCharsToLineFirst(
book, absStartX, textLine, words,
book, absStartX, textLine, words, textPaint,
desiredWidth, widths, srcList
)
}
@ -505,7 +506,7 @@ class TextChapterLayout(
//中间行
textLine.text = lineText
addCharsToLineMiddle(
book, absStartX, textLine, words,
book, absStartX, textLine, words, textPaint,
desiredWidth, 0f, widths, srcList
)
}
@ -556,6 +557,7 @@ class TextChapterLayout(
absStartX: Int,
textLine: TextLine,
words: List<String>,
textPaint: TextPaint,
/**自然排版长度**/
desiredWidth: Float,
textWidths: List<Float>,
@ -582,11 +584,12 @@ class TextChapterLayout(
x = x1
textLine.indentWidth = x
}
textLine.indentSize = bodyIndent.length
if (words.size > bodyIndent.length) {
val text1 = words.subList(bodyIndent.length, words.size)
val textWidths1 = textWidths.subList(bodyIndent.length, textWidths.size)
addCharsToLineMiddle(
book, absStartX, textLine, text1,
book, absStartX, textLine, text1, textPaint,
desiredWidth, x, textWidths1, srcList
)
}
@ -600,6 +603,7 @@ class TextChapterLayout(
absStartX: Int,
textLine: TextLine,
words: List<String>,
textPaint: TextPaint,
/**自然排版长度**/
desiredWidth: Float,
/**起始x坐标**/
@ -616,8 +620,10 @@ class TextChapterLayout(
}
val residualWidth = visibleWidth - desiredWidth
val spaceSize = words.count { it == " " }
textLine.startX = absStartX + startX
if (spaceSize > 1) {
val d = residualWidth / spaceSize
val d = residualWidth / (spaceSize - 1)
textLine.wordSpacing = d
var x = startX
for (index in words.indices) {
val char = words[index]
@ -636,6 +642,8 @@ class TextChapterLayout(
} else {
val gapCount: Int = words.lastIndex
val d = residualWidth / gapCount
textLine.extraLetterSpacingOffsetX = -d / 2
textLine.extraLetterSpacing = d / textPaint.textSize
var x = startX
for (index in words.indices) {
val char = words[index]
@ -666,6 +674,7 @@ class TextChapterLayout(
) {
val indentLength = ReadBookConfig.paragraphIndent.length
var x = startX
textLine.startX = absStartX + startX
for (index in words.indices) {
val char = words[index]
val cw = textWidths[index]
@ -727,8 +736,9 @@ class TextChapterLayout(
*/
private fun exceed(absStartX: Int, textLine: TextLine, words: List<String>) {
val visibleEnd = absStartX + visibleWidth
val endX = textLine.columns.lastOrNull()?.end ?: return
val endX = textLine.columns.lastOrNull()?.end?.roundToInt() ?: return
if (endX > visibleEnd) {
textLine.exceed = true
val cc = (endX - visibleEnd) / words.size
for (i in 0..words.lastIndex) {
textLine.getColumnReverseAt(i).let {

View File

@ -63,6 +63,7 @@ inline fun <reified T : Service> Context.stopService() {
@SuppressLint("UnspecifiedImmutableFlag")
inline fun <reified T : Service> Context.servicePendingIntent(
action: String,
requestCode: Int = 0,
configIntent: Intent.() -> Unit = {}
): PendingIntent? {
val intent = Intent(this, T::class.java)
@ -73,7 +74,7 @@ inline fun <reified T : Service> Context.servicePendingIntent(
} else {
FLAG_UPDATE_CURRENT
}
return getService(this, 0, intent, flags)
return getService(this, requestCode, intent, flags)
}
@SuppressLint("UnspecifiedImmutableFlag")

View File

@ -37,6 +37,11 @@ object EncoderUtils {
fun base64Encode(str: String, flags: Int = Base64.NO_WRAP): String? {
return Base64.encodeToString(str.toByteArray(), flags)
}
@JvmOverloads
fun base64Encode(bytes: ByteArray, flags: Int = Base64.NO_WRAP): String {
return Base64.encodeToString(bytes, flags)
}
@JvmOverloads
fun base64DecodeToByteArray(str: String, flags: Int = Base64.DEFAULT): ByteArray {

View File

@ -2,8 +2,8 @@ package io.legado.app.utils.canvasrecorder
import android.graphics.Canvas
import android.graphics.Picture
import io.legado.app.utils.canvasrecorder.objectpool.synchronized
import io.legado.app.utils.canvasrecorder.pools.PicturePool
import io.legado.app.utils.objectpool.synchronized
class CanvasRecorderApi23Impl : BaseCanvasRecorder() {

View File

@ -5,7 +5,7 @@ import android.graphics.Picture
import android.graphics.RenderNode
import android.os.Build
import androidx.annotation.RequiresApi
import io.legado.app.utils.canvasrecorder.objectpool.synchronized
import io.legado.app.utils.objectpool.synchronized
import io.legado.app.utils.canvasrecorder.pools.PicturePool
import io.legado.app.utils.canvasrecorder.pools.RenderNodePool

View File

@ -16,14 +16,14 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
private fun init(width: Int, height: Int) {
if (bitmap == null) {
bitmap = bitmapPool.obtain(width, height)
bitmap = BitmapPool.obtain(width, height)
}
if (bitmap!!.width != width || bitmap!!.height != height) {
if (canReconfigure(width, height)) {
bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888)
} else {
bitmapPool.recycle(bitmap!!)
bitmap = bitmapPool.obtain(width, height)
BitmapPool.recycle(bitmap!!)
bitmap = BitmapPool.obtain(width, height)
}
}
}
@ -54,13 +54,12 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
override fun recycle() {
super.recycle()
val bitmap = bitmap ?: return
bitmapPool.recycle(bitmap)
BitmapPool.recycle(bitmap)
this.bitmap = null
}
companion object {
private val canvasPool = CanvasPool(2)
private val bitmapPool = BitmapPool()
}
}

View File

@ -1,24 +0,0 @@
package io.legado.app.utils.canvasrecorder.objectpool
import androidx.annotation.CallSuper
import java.lang.ref.SoftReference
import java.util.LinkedList
abstract class BaseObjectPool<T> : ObjectPool<T> {
private val pool = LinkedList<SoftReference<T>>()
override fun obtain(): T {
while (true) {
if (pool.isEmpty()) break
return pool.poll()?.get() ?: continue
}
return create()
}
@CallSuper
override fun recycle(target: T) {
pool.add(SoftReference(target))
}
}

View File

@ -1,15 +1,46 @@
package io.legado.app.utils.canvasrecorder.pools
import android.graphics.Bitmap
import java.lang.ref.SoftReference
import io.legado.app.help.globalExecutor
import java.util.concurrent.ConcurrentHashMap
class BitmapPool {
object BitmapPool {
private val reusableBitmaps: MutableSet<SoftReference<Bitmap>> = ConcurrentHashMap.newKeySet()
private val reusableBitmaps: MutableSet<Bitmap> = ConcurrentHashMap.newKeySet()
fun recycle(bitmap: Bitmap) {
reusableBitmaps.add(SoftReference(bitmap))
reusableBitmaps.add(bitmap)
trimSize()
}
fun clear() {
if (reusableBitmaps.isEmpty()) {
return
}
globalExecutor.execute {
val iterator = reusableBitmaps.iterator()
while (iterator.hasNext()) {
val item = iterator.next()
iterator.remove()
item.recycle()
}
}
}
private fun trimSize() {
globalExecutor.execute {
var byteCount = 0
val iterator = reusableBitmaps.iterator()
while (iterator.hasNext()) {
val item = iterator.next()
if (byteCount > 64 * 1024 * 1024) {
iterator.remove()
item.recycle()
} else {
byteCount += item.byteCount
}
}
}
}
fun obtain(width: Int, height: Int): Bitmap {
@ -18,7 +49,7 @@ class BitmapPool {
}
val iterator = reusableBitmaps.iterator()
while (iterator.hasNext()) {
val item = iterator.next().get() ?: continue
val item = iterator.next()
if (item.isMutable) {
// Check to see it the item can be used for inBitmap.
if (canReconfigure(item, width, height)) {

View File

@ -1,9 +1,9 @@
package io.legado.app.utils.canvasrecorder.pools
import android.graphics.Picture
import io.legado.app.utils.canvasrecorder.objectpool.BaseObjectPool
import io.legado.app.utils.objectpool.BaseObjectPool
class PicturePool : BaseObjectPool<Picture>() {
class PicturePool : BaseObjectPool<Picture>(64) {
override fun create(): Picture = Picture()

View File

@ -3,10 +3,10 @@ package io.legado.app.utils.canvasrecorder.pools
import android.graphics.RenderNode
import android.os.Build
import androidx.annotation.RequiresApi
import io.legado.app.utils.canvasrecorder.objectpool.BaseObjectPool
import io.legado.app.utils.objectpool.BaseObjectPool
@RequiresApi(Build.VERSION_CODES.Q)
class RenderNodePool : BaseObjectPool<RenderNode>() {
class RenderNodePool : BaseObjectPool<RenderNode>(64) {
override fun recycle(target: RenderNode) {
target.discardDisplayList()

View File

@ -0,0 +1,19 @@
package io.legado.app.utils.objectpool
import androidx.annotation.CallSuper
import androidx.core.util.Pools
abstract class BaseObjectPool<T : Any>(size: Int) : ObjectPool<T> {
open val pool = Pools.SimplePool<T>(size)
override fun obtain(): T {
return pool.acquire() ?: create()
}
@CallSuper
override fun recycle(target: T) {
pool.release(target)
}
}

View File

@ -0,0 +1,9 @@
package io.legado.app.utils.objectpool
import androidx.core.util.Pools
abstract class BaseSafeObjectPool<T : Any>(size: Int): BaseObjectPool<T>(size) {
override val pool = Pools.SynchronizedPool<T>(size)
}

View File

@ -1,4 +1,4 @@
package io.legado.app.utils.canvasrecorder.objectpool
package io.legado.app.utils.objectpool
interface ObjectPool<T> {

View File

@ -1,3 +1,3 @@
package io.legado.app.utils.canvasrecorder.objectpool
package io.legado.app.utils.objectpool
fun <T> ObjectPool<T>.synchronized(): ObjectPool<T> = ObjectPoolLocked(this)

View File

@ -1,4 +1,4 @@
package io.legado.app.utils.canvasrecorder.objectpool
package io.legado.app.utils.objectpool
class ObjectPoolLocked<T>(private val delegate: ObjectPool<T>) : ObjectPool<T> by delegate {