|  |  |  | @ -6,15 +6,18 @@ import android.util.Size | 
			
		
	
		
			
				
					|  |  |  |  | import androidx.collection.LruCache | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.R | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.constant.AppLog.putDebug | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.constant.PageAnim | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.data.entities.Book | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.data.entities.BookSource | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.exception.NoStackTraceException | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.help.BookHelp | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.help.config.AppConfig | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.help.coroutine.Coroutine | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.model.ReadBook | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.model.localBook.EpubFile | 
			
		
	
		
			
				
					|  |  |  |  | import io.legado.app.utils.* | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.Dispatchers.IO | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.Dispatchers.Main | 
			
		
	
		
			
				
					|  |  |  |  | import kotlinx.coroutines.withContext | 
			
		
	
		
			
				
					|  |  |  |  | import splitties.init.appCtx | 
			
		
	
		
			
				
					|  |  |  |  | import java.io.File | 
			
		
	
	
		
			
				
					|  |  |  | @ -32,6 +35,7 @@ object ImageProvider { | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     private const val M = 1024 * 1024 | 
			
		
	
		
			
				
					|  |  |  |  |     val cacheSize get() = AppConfig.bitmapCacheSize * M | 
			
		
	
		
			
				
					|  |  |  |  |     var triggerRecycled = false | 
			
		
	
		
			
				
					|  |  |  |  |     val bitmapLruCache = object : LruCache<String, Bitmap>(cacheSize) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         override fun sizeOf(filePath: String, bitmap: Bitmap): Int { | 
			
		
	
	
		
			
				
					|  |  |  | @ -47,6 +51,7 @@ object ImageProvider { | 
			
		
	
		
			
				
					|  |  |  |  |             //错误图片不能释放,占位用,防止一直重复获取图片 | 
			
		
	
		
			
				
					|  |  |  |  |             if (oldBitmap != errorBitmap) { | 
			
		
	
		
			
				
					|  |  |  |  |                 oldBitmap.recycle() | 
			
		
	
		
			
				
					|  |  |  |  |                 triggerRecycled = true | 
			
		
	
		
			
				
					|  |  |  |  |                 putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath") | 
			
		
	
		
			
				
					|  |  |  |  |                 putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes") | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
	
		
			
				
					|  |  |  | @ -110,12 +115,13 @@ object ImageProvider { | 
			
		
	
		
			
				
					|  |  |  |  |         book: Book, | 
			
		
	
		
			
				
					|  |  |  |  |         src: String, | 
			
		
	
		
			
				
					|  |  |  |  |         width: Int, | 
			
		
	
		
			
				
					|  |  |  |  |         height: Int? = null | 
			
		
	
		
			
				
					|  |  |  |  |     ): Bitmap { | 
			
		
	
		
			
				
					|  |  |  |  |         height: Int? = null, | 
			
		
	
		
			
				
					|  |  |  |  |         block: (() -> Unit)? = null | 
			
		
	
		
			
				
					|  |  |  |  |     ): Bitmap? { | 
			
		
	
		
			
				
					|  |  |  |  |         //src为空白时 可能被净化替换掉了 或者规则失效 | 
			
		
	
		
			
				
					|  |  |  |  |         if (book.getUseReplaceRule() && src.isBlank()) { | 
			
		
	
		
			
				
					|  |  |  |  |            book.setUseReplaceRule(false) | 
			
		
	
		
			
				
					|  |  |  |  |            appCtx.toastOnUi(R.string.error_image_url_empty) | 
			
		
	
		
			
				
					|  |  |  |  |             book.setUseReplaceRule(false) | 
			
		
	
		
			
				
					|  |  |  |  |             appCtx.toastOnUi(R.string.error_image_url_empty) | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         val vFile = BookHelp.getImage(book, src) | 
			
		
	
		
			
				
					|  |  |  |  |         if (!vFile.exists()) return errorBitmap | 
			
		
	
	
		
			
				
					|  |  |  | @ -123,6 +129,30 @@ object ImageProvider { | 
			
		
	
		
			
				
					|  |  |  |  |         //bitmapLruCache的key同一改成缓存文件的路径 | 
			
		
	
		
			
				
					|  |  |  |  |         val cacheBitmap = bitmapLruCache.get(vFile.absolutePath) | 
			
		
	
		
			
				
					|  |  |  |  |         if (cacheBitmap != null) return cacheBitmap | 
			
		
	
		
			
				
					|  |  |  |  |         if (height != null && ReadBook.pageAnim() == PageAnim.scrollPageAnim) { | 
			
		
	
		
			
				
					|  |  |  |  |             Coroutine.async { | 
			
		
	
		
			
				
					|  |  |  |  |                 kotlin.runCatching { | 
			
		
	
		
			
				
					|  |  |  |  |                     val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height) | 
			
		
	
		
			
				
					|  |  |  |  |                         ?: throw NoStackTraceException(appCtx.getString(R.string.error_decode_bitmap)) | 
			
		
	
		
			
				
					|  |  |  |  |                     withContext(Main) { | 
			
		
	
		
			
				
					|  |  |  |  |                         bitmapLruCache.put(vFile.absolutePath, bitmap) | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                 }.onFailure { | 
			
		
	
		
			
				
					|  |  |  |  |                     //错误图片占位,防止重复获取 | 
			
		
	
		
			
				
					|  |  |  |  |                     withContext(Main) { | 
			
		
	
		
			
				
					|  |  |  |  |                         bitmapLruCache.put(vFile.absolutePath, errorBitmap) | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                     putDebug( | 
			
		
	
		
			
				
					|  |  |  |  |                         "ImageProvider: decode bitmap failed. path: ${vFile.absolutePath}\n$it", | 
			
		
	
		
			
				
					|  |  |  |  |                         it | 
			
		
	
		
			
				
					|  |  |  |  |                     ) | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |                 withContext(Main) { | 
			
		
	
		
			
				
					|  |  |  |  |                     block?.invoke() | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             return null | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         @Suppress("BlockingMethodInNonBlockingContext") | 
			
		
	
		
			
				
					|  |  |  |  |         return kotlin.runCatching { | 
			
		
	
		
			
				
					|  |  |  |  |             val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height) | 
			
		
	
	
		
			
				
					|  |  |  | @ -139,4 +169,17 @@ object ImageProvider { | 
			
		
	
		
			
				
					|  |  |  |  |         }.getOrDefault(errorBitmap) | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     fun isImageAlive(book: Book, src: String): Boolean { | 
			
		
	
		
			
				
					|  |  |  |  |         val vFile = BookHelp.getImage(book, src) | 
			
		
	
		
			
				
					|  |  |  |  |         if (!vFile.exists()) return true // 使用 errorBitmap | 
			
		
	
		
			
				
					|  |  |  |  |         val cacheBitmap = bitmapLruCache.get(vFile.absolutePath) | 
			
		
	
		
			
				
					|  |  |  |  |         return cacheBitmap != null | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     fun isTriggerRecycled(): Boolean { | 
			
		
	
		
			
				
					|  |  |  |  |         val tmp = triggerRecycled | 
			
		
	
		
			
				
					|  |  |  |  |         triggerRecycled = false | 
			
		
	
		
			
				
					|  |  |  |  |         return tmp | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |