@@ -6,8 +6,8 @@ import android.graphics.drawable.Drawable
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import com.bumptech.glide.RequestBuilder
|
import com.bumptech.glide.RequestBuilder
|
||||||
import io.legado.app.constant.AppPattern.dataUriRegex
|
|
||||||
import io.legado.app.utils.isAbsUrl
|
import io.legado.app.utils.isAbsUrl
|
||||||
|
import io.legado.app.utils.isDataUrl
|
||||||
import io.legado.app.utils.isContentScheme
|
import io.legado.app.utils.isContentScheme
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ object ImageLoader {
|
|||||||
fun load(context: Context, path: String?): RequestBuilder<Drawable> {
|
fun load(context: Context, path: String?): RequestBuilder<Drawable> {
|
||||||
return when {
|
return when {
|
||||||
path.isNullOrEmpty() -> GlideApp.with(context).load(path)
|
path.isNullOrEmpty() -> GlideApp.with(context).load(path)
|
||||||
dataUriRegex.find(path) != null -> GlideApp.with(context).load(path)
|
path.isDataUrl() -> GlideApp.with(context).load(path)
|
||||||
path.isAbsUrl() -> GlideApp.with(context).load(path)
|
path.isAbsUrl() -> GlideApp.with(context).load(path)
|
||||||
path.isContentScheme() -> GlideApp.with(context).load(Uri.parse(path))
|
path.isContentScheme() -> GlideApp.with(context).load(Uri.parse(path))
|
||||||
else -> kotlin.runCatching {
|
else -> kotlin.runCatching {
|
||||||
@@ -34,6 +34,7 @@ object ImageLoader {
|
|||||||
fun loadBitmap(context: Context, path: String?): RequestBuilder<Bitmap> {
|
fun loadBitmap(context: Context, path: String?): RequestBuilder<Bitmap> {
|
||||||
return when {
|
return when {
|
||||||
path.isNullOrEmpty() -> GlideApp.with(context).asBitmap().load(path)
|
path.isNullOrEmpty() -> GlideApp.with(context).asBitmap().load(path)
|
||||||
|
path.isDataUrl() -> GlideApp.with(context).asBitmap().load(path)
|
||||||
path.isAbsUrl() -> GlideApp.with(context).asBitmap().load(path)
|
path.isAbsUrl() -> GlideApp.with(context).asBitmap().load(path)
|
||||||
path.isContentScheme() -> GlideApp.with(context).asBitmap().load(Uri.parse(path))
|
path.isContentScheme() -> GlideApp.with(context).asBitmap().load(Uri.parse(path))
|
||||||
else -> kotlin.runCatching {
|
else -> kotlin.runCatching {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.bumptech.glide.load.Options
|
|||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
import com.bumptech.glide.load.model.ModelLoader
|
import com.bumptech.glide.load.model.ModelLoader
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
|
import io.legado.app.utils.isAbsUrl
|
||||||
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
@@ -19,7 +20,11 @@ object OkHttpModelLoader : ModelLoader<GlideUrl?, InputStream?> {
|
|||||||
height: Int,
|
height: Int,
|
||||||
options: Options
|
options: Options
|
||||||
): ModelLoader.LoadData<InputStream?> {
|
): ModelLoader.LoadData<InputStream?> {
|
||||||
val modelWithHeader = AnalyzeUrl(model.toString()).getGlideUrl()
|
val cacheKey = model.toString()
|
||||||
|
var modelWithHeader = model
|
||||||
|
if (cacheKey.isAbsUrl()) {
|
||||||
|
modelWithHeader = AnalyzeUrl(cacheKey).getGlideUrl()
|
||||||
|
}
|
||||||
return ModelLoader.LoadData(modelWithHeader, OkHttpStreamFetcher(modelWithHeader, options))
|
return ModelLoader.LoadData(modelWithHeader, OkHttpStreamFetcher(modelWithHeader, options))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,16 +71,18 @@ class AnalyzeUrl(
|
|||||||
private var webJs: String? = null
|
private var webJs: String? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val urlMatcher = paramPattern.matcher(baseUrl)
|
if (!mUrl.isDataUrl()) {
|
||||||
if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start())
|
val urlMatcher = paramPattern.matcher(baseUrl)
|
||||||
(headerMapF ?: source?.getHeaderMap(true))?.let {
|
if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start())
|
||||||
headerMap.putAll(it)
|
(headerMapF ?: source?.getHeaderMap(true))?.let {
|
||||||
if (it.containsKey("proxy")) {
|
headerMap.putAll(it)
|
||||||
proxy = it["proxy"]
|
if (it.containsKey("proxy")) {
|
||||||
headerMap.remove("proxy")
|
proxy = it["proxy"]
|
||||||
|
headerMap.remove("proxy")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
initUrl()
|
||||||
}
|
}
|
||||||
initUrl()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -457,10 +459,9 @@ class AnalyzeUrl(
|
|||||||
* 访问网站,返回ByteArray
|
* 访问网站,返回ByteArray
|
||||||
*/
|
*/
|
||||||
suspend fun getByteArrayAwait(): ByteArray {
|
suspend fun getByteArrayAwait(): ByteArray {
|
||||||
|
val concurrentRecord = fetchStart()
|
||||||
@Suppress("RegExpRedundantEscape")
|
@Suppress("RegExpRedundantEscape")
|
||||||
val dataUriFindResult = dataUriRegex.find(urlNoQuery)
|
val dataUriFindResult = dataUriRegex.find(urlNoQuery)
|
||||||
val concurrentRecord = fetchStart()
|
|
||||||
setCookie(source?.getKey())
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
if (dataUriFindResult != null) {
|
if (dataUriFindResult != null) {
|
||||||
val dataUriBase64 = dataUriFindResult.groupValues[1]
|
val dataUriBase64 = dataUriFindResult.groupValues[1]
|
||||||
@@ -468,6 +469,7 @@ class AnalyzeUrl(
|
|||||||
fetchEnd(concurrentRecord)
|
fetchEnd(concurrentRecord)
|
||||||
return byteArray
|
return byteArray
|
||||||
} else {
|
} else {
|
||||||
|
setCookie(source?.getKey())
|
||||||
val byteArray = getProxyClient(proxy).newCallResponseBody(retry) {
|
val byteArray = getProxyClient(proxy).newCallResponseBody(retry) {
|
||||||
addHeaders(headerMap)
|
addHeaders(headerMap)
|
||||||
when (method) {
|
when (method) {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package io.legado.app.model.localBook
|
||||||
|
|
||||||
|
import io.legado.app.data.entities.Book
|
||||||
|
import io.legado.app.data.entities.BookChapter
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
*companion object interface
|
||||||
|
*see EpubFile.kt
|
||||||
|
*/
|
||||||
|
interface BaseLocalBookParse {
|
||||||
|
|
||||||
|
fun upBookInfo(book: Book)
|
||||||
|
|
||||||
|
fun getChapterList(book: Book): ArrayList<BookChapter>
|
||||||
|
|
||||||
|
fun getContent(book: Book, chapter: BookChapter): String?
|
||||||
|
|
||||||
|
fun getImage(book: Book, href: String): InputStream?
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ import java.util.zip.ZipFile
|
|||||||
|
|
||||||
class EpubFile(var book: Book) {
|
class EpubFile(var book: Book) {
|
||||||
|
|
||||||
companion object {
|
companion object : BaseLocalBookParse {
|
||||||
private var eFile: EpubFile? = null
|
private var eFile: EpubFile? = null
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -41,17 +41,17 @@ class EpubFile(var book: Book) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getChapterList(book: Book): ArrayList<BookChapter> {
|
override fun getChapterList(book: Book): ArrayList<BookChapter> {
|
||||||
return getEFile(book).getChapterList()
|
return getEFile(book).getChapterList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getContent(book: Book, chapter: BookChapter): String? {
|
override fun getContent(book: Book, chapter: BookChapter): String? {
|
||||||
return getEFile(book).getContent(chapter)
|
return getEFile(book).getContent(chapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getImage(
|
override fun getImage(
|
||||||
book: Book,
|
book: Book,
|
||||||
href: String
|
href: String
|
||||||
): InputStream? {
|
): InputStream? {
|
||||||
@@ -59,7 +59,7 @@ class EpubFile(var book: Book) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun upBookInfo(book: Book) {
|
override fun upBookInfo(book: Book) {
|
||||||
return getEFile(book).upBookInfo()
|
return getEFile(book).upBookInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# 本地书籍解析
|
||||||
|
|
||||||
|
* BaseLocalBookParse.kt 本地书籍解析接口
|
||||||
|
* LocalBook.kt 总入口
|
||||||
|
* TextFile.kt 解析txt
|
||||||
|
* EpubFile.kt 解析epub
|
||||||
|
* UmdFile.kt 解析umd
|
||||||
@@ -11,7 +11,7 @@ import java.io.File
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
class UmdFile(var book: Book) {
|
class UmdFile(var book: Book) {
|
||||||
companion object {
|
companion object : BaseLocalBookParse {
|
||||||
private var uFile: UmdFile? = null
|
private var uFile: UmdFile? = null
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -25,17 +25,17 @@ class UmdFile(var book: Book) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getChapterList(book: Book): ArrayList<BookChapter> {
|
override fun getChapterList(book: Book): ArrayList<BookChapter> {
|
||||||
return getUFile(book).getChapterList()
|
return getUFile(book).getChapterList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getContent(book: Book, chapter: BookChapter): String? {
|
override fun getContent(book: Book, chapter: BookChapter): String? {
|
||||||
return getUFile(book).getContent(chapter)
|
return getUFile(book).getContent(chapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getImage(
|
override fun getImage(
|
||||||
book: Book,
|
book: Book,
|
||||||
href: String
|
href: String
|
||||||
): InputStream? {
|
): InputStream? {
|
||||||
@@ -44,7 +44,7 @@ class UmdFile(var book: Book) {
|
|||||||
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun upBookInfo(book: Book) {
|
override fun upBookInfo(book: Book) {
|
||||||
return getUFile(book).upBookInfo()
|
return getUFile(book).upBookInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.legado.app.ui.book.read.page.provider
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
|
import androidx.collection.LruCache
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.AppLog.putDebug
|
import io.legado.app.constant.AppLog.putDebug
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
@@ -23,6 +24,33 @@ object ImageProvider {
|
|||||||
BitmapFactory.decodeResource(appCtx.resources, R.drawable.image_loading_error)
|
BitmapFactory.decodeResource(appCtx.resources, R.drawable.image_loading_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*缓存bitmap LruCache实现
|
||||||
|
*/
|
||||||
|
//private val maxMemory = Runtime.getRuntime().maxMemory()
|
||||||
|
//private val cacheMemorySize = (maxMemory / 8) as Int
|
||||||
|
private val cacheMemorySize: Int = 1024 * 1024 * 1024 //1G
|
||||||
|
private val bitmapLruCache = object : LruCache<String, Bitmap>(cacheMemorySize) {
|
||||||
|
override fun sizeOf(key: String, bitmap: Bitmap): Int {
|
||||||
|
return bitmap.getByteCount()
|
||||||
|
}
|
||||||
|
override fun entryRemoved(
|
||||||
|
evicted: Boolean,
|
||||||
|
key: String,
|
||||||
|
oldBitmap: Bitmap,
|
||||||
|
newBitmap: Bitmap?
|
||||||
|
) {
|
||||||
|
if (evicted) {
|
||||||
|
oldBitmap.recycle()
|
||||||
|
putDebug("自动回收Bitmap path: $key")
|
||||||
|
putDebug("bitmapLruCache : ${size()} / ${maxSize()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*缓存网络图片和epub图片
|
||||||
|
*/
|
||||||
suspend fun cacheImage(
|
suspend fun cacheImage(
|
||||||
book: Book,
|
book: Book,
|
||||||
src: String,
|
src: String,
|
||||||
@@ -45,6 +73,9 @@ object ImageProvider {
|
|||||||
return vFile
|
return vFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*获取图片宽度高度信息
|
||||||
|
*/
|
||||||
suspend fun getImageSize(
|
suspend fun getImageSize(
|
||||||
book: Book,
|
book: Book,
|
||||||
src: String,
|
src: String,
|
||||||
@@ -61,37 +92,23 @@ object ImageProvider {
|
|||||||
return Size(op.outWidth, op.outHeight)
|
return Size(op.outWidth, op.outHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*获取bitmap 使用LruCache缓存
|
||||||
|
*/
|
||||||
fun getImage(
|
fun getImage(
|
||||||
book: Book,
|
book: Book,
|
||||||
src: String,
|
src: String,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int
|
height: Int? = null
|
||||||
): Bitmap {
|
): Bitmap {
|
||||||
|
val cacheBitmap = bitmapLruCache.get(src)
|
||||||
|
if (cacheBitmap != null) return cacheBitmap
|
||||||
val vFile = BookHelp.getImage(book, src)
|
val vFile = BookHelp.getImage(book, src)
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
return try {
|
return try {
|
||||||
BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
|
val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
|
||||||
} catch (e: Exception) {
|
bitmapLruCache.put(src, bitmap)
|
||||||
Coroutine.async {
|
bitmap
|
||||||
putDebug("${vFile.absolutePath} 解码失败\n$e", e)
|
|
||||||
if (FileUtils.readText(vFile.absolutePath).isXml()) {
|
|
||||||
putDebug("${vFile.absolutePath}为xml,自动删除")
|
|
||||||
vFile.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errorBitmap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getImage(
|
|
||||||
book: Book,
|
|
||||||
src: String,
|
|
||||||
width: Int
|
|
||||||
): Bitmap {
|
|
||||||
val vFile = BookHelp.getImage(book, src)
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
return try {
|
|
||||||
BitmapUtils.decodeBitmap(vFile.absolutePath, width)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Coroutine.async {
|
Coroutine.async {
|
||||||
putDebug("${vFile.absolutePath} 解码失败\n$e", e)
|
putDebug("${vFile.absolutePath} 解码失败\n$e", e)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ object BitmapUtils {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun decodeBitmap(path: String, width: Int, height: Int): Bitmap {
|
fun decodeBitmap(path: String, width: Int, height: Int? = null): Bitmap {
|
||||||
|
|
||||||
val fis = FileInputStream(path)
|
val fis = FileInputStream(path)
|
||||||
return fis.use {
|
return fis.use {
|
||||||
@@ -35,44 +35,35 @@ object BitmapUtils {
|
|||||||
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
||||||
op.inJustDecodeBounds = true
|
op.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
|
BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
|
||||||
//获取比例大小
|
op.inSampleSize = calculateInSampleSize(op, width, height)
|
||||||
val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
|
|
||||||
val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
|
|
||||||
//如果超出指定大小,则缩小相应的比例
|
|
||||||
if (wRatio > 1 && hRatio > 1) {
|
|
||||||
if (wRatio > hRatio) {
|
|
||||||
op.inSampleSize = wRatio
|
|
||||||
} else {
|
|
||||||
op.inSampleSize = hRatio
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op.inJustDecodeBounds = false
|
op.inJustDecodeBounds = false
|
||||||
BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
|
BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
/**
|
||||||
fun decodeBitmap(path: String, width: Int): Bitmap {
|
*计算 InSampleSize。缺省返回1
|
||||||
|
* @param options BitmapFactory.Options,
|
||||||
val fis = FileInputStream(path)
|
* @param width 想要显示的图片的宽度
|
||||||
|
* @param height 想要显示的图片的高度
|
||||||
return fis.use {
|
* @return
|
||||||
val op = BitmapFactory.Options()
|
*/
|
||||||
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
private fun calculateInSampleSize(
|
||||||
op.inJustDecodeBounds = true
|
options: BitmapFactory.Options,
|
||||||
|
width: Int? = null,
|
||||||
BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
|
height: Int? = null
|
||||||
//获取比例大小
|
): Int {
|
||||||
val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
|
//获取比例大小
|
||||||
//如果超出指定大小,则缩小相应的比例
|
val wRatio = width?.let { ceil((options.outWidth / it).toDouble()).toInt() } ?: -1
|
||||||
if (wRatio > 1) {
|
val hRatio = height?.let { ceil((options.outHeight / it).toDouble()).toInt() } ?: -1
|
||||||
op.inSampleSize = wRatio
|
//如果超出指定大小,则缩小相应的比例
|
||||||
}
|
return when {
|
||||||
op.inJustDecodeBounds = false
|
wRatio > 1 && hRatio > 1 -> max(wRatio, hRatio)
|
||||||
BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
|
wRatio > 1 -> wRatio
|
||||||
|
hRatio > 1 -> hRatio
|
||||||
|
else -> 1
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 从path中获取Bitmap图片
|
/** 从path中获取Bitmap图片
|
||||||
@@ -118,17 +109,7 @@ object BitmapUtils {
|
|||||||
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
||||||
op.inJustDecodeBounds = true
|
op.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeResource(context.resources, resId, op) //获取尺寸信息
|
BitmapFactory.decodeResource(context.resources, resId, op) //获取尺寸信息
|
||||||
//获取比例大小
|
op.inSampleSize = calculateInSampleSize(op, width, height)
|
||||||
val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
|
|
||||||
val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
|
|
||||||
//如果超出指定大小,则缩小相应的比例
|
|
||||||
if (wRatio > 1 && hRatio > 1) {
|
|
||||||
if (wRatio > hRatio) {
|
|
||||||
op.inSampleSize = wRatio
|
|
||||||
} else {
|
|
||||||
op.inSampleSize = hRatio
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op.inJustDecodeBounds = false
|
op.inJustDecodeBounds = false
|
||||||
return BitmapFactory.decodeResource(context.resources, resId, op)
|
return BitmapFactory.decodeResource(context.resources, resId, op)
|
||||||
}
|
}
|
||||||
@@ -154,17 +135,7 @@ object BitmapUtils {
|
|||||||
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
|
||||||
op.inJustDecodeBounds = true
|
op.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeStream(inputStream, null, op) //获取尺寸信息
|
BitmapFactory.decodeStream(inputStream, null, op) //获取尺寸信息
|
||||||
//获取比例大小
|
op.inSampleSize = calculateInSampleSize(op, width, height)
|
||||||
val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
|
|
||||||
val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
|
|
||||||
//如果超出指定大小,则缩小相应的比例
|
|
||||||
if (wRatio > 1 && hRatio > 1) {
|
|
||||||
if (wRatio > hRatio) {
|
|
||||||
op.inSampleSize = wRatio
|
|
||||||
} else {
|
|
||||||
op.inSampleSize = hRatio
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inputStream = context.assets.open(fileNameInAssets)
|
inputStream = context.assets.open(fileNameInAssets)
|
||||||
op.inJustDecodeBounds = false
|
op.inJustDecodeBounds = false
|
||||||
BitmapFactory.decodeStream(inputStream, null, op)
|
BitmapFactory.decodeStream(inputStream, null, op)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
package io.legado.app.utils
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import io.legado.app.constant.AppPattern.dataUriRegex
|
||||||
import android.icu.text.Collator
|
import android.icu.text.Collator
|
||||||
import android.icu.util.ULocale
|
import android.icu.util.ULocale
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -25,6 +26,11 @@ fun String?.isAbsUrl() =
|
|||||||
it.startsWith("http://", true) || it.startsWith("https://", true)
|
it.startsWith("http://", true) || it.startsWith("https://", true)
|
||||||
} ?: false
|
} ?: false
|
||||||
|
|
||||||
|
fun String?.isDataUrl() =
|
||||||
|
this?.let {
|
||||||
|
dataUriRegex.matches(it)
|
||||||
|
} ?: false
|
||||||
|
|
||||||
fun String?.isJson(): Boolean =
|
fun String?.isJson(): Boolean =
|
||||||
this?.run {
|
this?.run {
|
||||||
val str = this.trim()
|
val str = this.trim()
|
||||||
|
|||||||
Reference in New Issue
Block a user