Merge branch 'gedoor:master' into master

pull/1786/head
Xwite 3 years ago committed by GitHub
commit 2b81ca8fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      app/src/main/java/io/legado/app/api/controller/BookController.kt
  2. 2
      app/src/main/java/io/legado/app/constant/AppLog.kt
  3. 9
      app/src/main/java/io/legado/app/data/entities/Book.kt
  4. 3
      app/src/main/java/io/legado/app/help/BookHelp.kt
  5. 44
      app/src/main/java/io/legado/app/help/http/cronet/AbsCallBack.kt
  6. 14
      app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt
  7. 1
      app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt
  8. 8
      app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt
  9. 53
      app/src/main/java/io/legado/app/help/http/cronet/NewCallBack.kt
  10. 39
      app/src/main/java/io/legado/app/help/http/cronet/OldCallback.kt
  11. 18
      app/src/main/java/io/legado/app/ui/book/read/PhotoDialog.kt
  12. 53
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  13. 13
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt
  14. 60
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ImageProvider.kt
  15. 11
      app/src/main/java/io/legado/app/utils/BitmapUtils.kt
  16. 4
      app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt

@ -21,8 +21,8 @@ import io.legado.app.model.localBook.EpubFile
import io.legado.app.model.localBook.LocalBook
import io.legado.app.model.localBook.UmdFile
import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.*
import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.utils.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import splitties.init.appCtx
@ -87,9 +87,11 @@ object BookController {
this.bookSource = appDb.bookSourceDao.getBookSource(book.origin)
}
this.bookUrl = bookUrl
return ImageProvider.getImage(book, src, bookSource, width, width)?.let {
returnData.setData(it)
} ?: returnData.setErrorMsg("图片加载失败或不存在")
val bitmap = runBlocking {
ImageProvider.cacheImage(book, src, bookSource)
ImageProvider.getImage(book, src, width, width)
}
return returnData.setData(bitmap)
}
/**

@ -31,7 +31,7 @@ object AppLog {
}
fun putDebug(message: String?, throwable: Throwable? = null) {
if (AppConfig.recordLog) {
if (AppConfig.recordLog || BuildConfig.DEBUG) {
put(message, throwable)
}
}

@ -193,7 +193,14 @@ data class Book(
}
fun getUseReplaceRule(): Boolean {
return config.useReplaceRule ?: AppConfig.replaceEnableDefault
val useReplaceRule = config.useReplaceRule
if (useReplaceRule != null) {
return useReplaceRule
}
if (type == BookType.image) {
return false
}
return AppConfig.replaceEnableDefault
}
fun setReSegment(reSegment: Boolean) {

@ -1,5 +1,6 @@
package io.legado.app.help
import io.legado.app.constant.AppLog
import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus
import io.legado.app.data.appDb
@ -126,7 +127,7 @@ object BookHelp {
).writeBytes(it)
}
} catch (e: Exception) {
e.printOnDebug()
AppLog.putDebug("${src}下载错误", e)
} finally {
downloadImages.remove(src)
}

@ -2,7 +2,7 @@ package io.legado.app.help.http.cronet
import io.legado.app.help.http.okHttpClient
import io.legado.app.utils.DebugLog
import io.legado.app.utils.rethrowAsIOException
import io.legado.app.utils.asIOException
import okhttp3.*
import okhttp3.EventListener
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -27,12 +27,24 @@ abstract class AbsCallBack(
var mResponse: Response
var mException: IOException? = null
private var followCount = 0
@Throws(IOException::class)
abstract fun waitForDone(urlRequest: UrlRequest): Response
/**
* 当发生错误时通知子类终止阻塞抛出错误
* @param error
*/
abstract fun onError(error: IOException)
/**
* 请求成功后通知子类结束阻塞返回response
* @param response
*/
abstract fun onSuccess(response: Response)
override fun onRedirectReceived(
request: UrlRequest,
@ -41,10 +53,13 @@ abstract class AbsCallBack(
) {
if (followCount > MAX_FOLLOW_COUNT) {
request.cancel()
mException = IOException("Too many redirect")
onError(IOException("Too many redirect"))
return
}
if (mCall.isCanceled()) {
mException = IOException("Request Canceled")
onError(IOException("Request Canceled"))
request.cancel()
return
}
followCount += 1
val client = okHttpClient
@ -55,7 +70,7 @@ abstract class AbsCallBack(
} else if (okHttpClient.followRedirects) {
request.followRedirect()
} else {
mException = IOException("Too many redirect")
onError(IOException("Too many redirect"))
request.cancel()
}
}
@ -73,7 +88,7 @@ abstract class AbsCallBack(
}
@Throws(Exception::class)
@Throws(IOException::class)
override fun onReadCompleted(
request: UrlRequest,
info: UrlResponseInfo,
@ -83,7 +98,7 @@ abstract class AbsCallBack(
if (mCall.isCanceled()) {
request.cancel()
mException = IOException("Request Canceled")
onError(IOException("Request Canceled"))
}
byteBuffer.flip()
@ -91,9 +106,9 @@ abstract class AbsCallBack(
try {
buffer.write(byteBuffer)
} catch (e: IOException) {
DebugLog.i(javaClass.name, "IOException during ByteBuffer read. Details: ", e)
mException = IOException("IOException during ByteBuffer read. Details:", e)
throw e
DebugLog.e(javaClass.name, "IOException during ByteBuffer read. Details: ", e)
onError(IOException("IOException during ByteBuffer read. Details:", e))
return
}
byteBuffer.clear()
request.read(byteBuffer)
@ -108,7 +123,8 @@ abstract class AbsCallBack(
buffer.asResponseBody(contentType)
val newRequest = originalRequest.newBuilder().url(info.url).build()
this.mResponse = this.mResponse.newBuilder().body(responseBody).request(newRequest).build()
DebugLog.i(javaClass.simpleName, "end[${info.negotiatedProtocol}]${info.url}")
onSuccess(this.mResponse)
//DebugLog.i(javaClass.simpleName, "end[${info.negotiatedProtocol}]${info.url}")
eventListener?.callEnd(mCall)
if (responseCallback != null) {
@ -123,8 +139,8 @@ abstract class AbsCallBack(
//UrlResponseInfo可能为null
override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
DebugLog.i(javaClass.name, error.message.toString())
mException = error.rethrowAsIOException()
DebugLog.e(javaClass.name, error.message.toString())
onError(error.asIOException())
this.eventListener?.callFailed(mCall, error)
responseCallback?.onFailure(mCall, error)
}
@ -132,7 +148,7 @@ abstract class AbsCallBack(
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
super.onCanceled(request, info)
this.eventListener?.callEnd(mCall)
mException = IOException("Cronet Request Canceled")
onError(IOException("Cronet Request Canceled"))
}

@ -2,6 +2,7 @@ package io.legado.app.help.http.cronet
import io.legado.app.constant.AppLog
import io.legado.app.help.config.AppConfig
import io.legado.app.help.http.okHttpClient
import io.legado.app.utils.DebugLog
import okhttp3.Headers
import okhttp3.MediaType
@ -13,11 +14,8 @@ import org.chromium.net.UploadDataProviders
import org.chromium.net.UrlRequest
import splitties.init.appCtx
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
val executor: ExecutorService by lazy { Executors.newCachedThreadPool() }
//val executor: ExecutorService by lazy { Executors.newCachedThreadPool() }
val cronetEngine: ExperimentalCronetEngine? by lazy {
if (!AppConfig.isGooglePlay) {
@ -48,7 +46,11 @@ fun buildRequest(request: Request, callback: UrlRequest.Callback): UrlRequest? {
val url = request.url.toString()
val headers: Headers = request.headers
val requestBody = request.body
return cronetEngine?.newUrlRequestBuilder(url, callback, executor)?.apply {
return cronetEngine?.newUrlRequestBuilder(
url,
callback,
okHttpClient.dispatcher.executorService
)?.apply {
setHttpMethod(request.method)//设置
allowDirectExecutor()
headers.forEachIndexed { index, _ ->
@ -65,7 +67,7 @@ fun buildRequest(request: Request, callback: UrlRequest.Callback): UrlRequest? {
requestBody.writeTo(buffer)
setUploadDataProvider(
UploadDataProviders.create(buffer.readByteArray()),
executor
okHttpClient.dispatcher.executorService
)
}

@ -49,7 +49,6 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
OldCallback(request, call)
}
buildRequest(request, callBack)?.let {
it.start()
return callBack.waitForDone(it)
}
return null

@ -268,7 +268,8 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
return
}
download = true
executor.execute {
Coroutine.async {
val result = downloadFileIfNotExist(url, downloadTempFile)
DebugLog.d(javaClass.simpleName, "download result:$result")
//文件md5再次校验
@ -279,7 +280,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
downloadTempFile.deleteOnExit()
}
download = false
return@execute
return@async
}
DebugLog.d(javaClass.simpleName, "download success, copy to $destSuccessFile")
//下载成功拷贝文件
@ -289,6 +290,9 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
@Suppress("SameParameterValue")
deleteHistoryFile(parentFile!!, null)
}
// executor.execute {
//
// }
}
/**

@ -6,11 +6,8 @@ import androidx.annotation.RequiresApi
import okhttp3.Call
import okhttp3.Request
import okhttp3.Response
import org.chromium.net.CronetException
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
import java.io.IOException
import java.nio.ByteBuffer
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
@ -21,45 +18,29 @@ class NewCallBack(originalRequest: Request, mCall: Call) : AbsCallBack(originalR
@Throws(IOException::class)
override fun waitForDone(urlRequest: UrlRequest): Response {
return responseFuture.get(mCall.timeout().timeoutNanos(), TimeUnit.NANOSECONDS)
urlRequest.start()
return if (mCall.timeout().timeoutNanos() > 0) {
responseFuture.get(mCall.timeout().timeoutNanos(), TimeUnit.NANOSECONDS)
} else {
return responseFuture.get()
}
override fun onRedirectReceived(
request: UrlRequest,
info: UrlResponseInfo,
newLocationUrl: String
) {
super.onRedirectReceived(request, info, newLocationUrl)
if (mException != null) {
responseFuture.completeExceptionally(mException)
}
}
override fun onReadCompleted(
request: UrlRequest,
info: UrlResponseInfo,
byteBuffer: ByteBuffer
) {
super.onReadCompleted(request, info, byteBuffer)
if (mException != null) {
responseFuture.completeExceptionally(mException)
}
}
override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
super.onSucceeded(request, info)
responseFuture.complete(mResponse)
}
override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
super.onFailed(request, info, error)
responseFuture.completeExceptionally(mException)
/**
* 当发生错误时通知子类终止阻塞抛出错误
* @param error
*/
override fun onError(error: IOException) {
responseFuture.completeExceptionally(error)
}
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
super.onCanceled(request, info)
responseFuture.completeExceptionally(mException)
/**
* 请求成功后通知子类结束阻塞返回response
* @param response
*/
override fun onSuccess(response: Response) {
responseFuture.complete(response)
}

@ -4,20 +4,19 @@ import android.os.ConditionVariable
import okhttp3.Call
import okhttp3.Request
import okhttp3.Response
import org.chromium.net.CronetException
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
import java.io.IOException
import java.nio.ByteBuffer
class OldCallback(originalRequest: Request, mCall: Call) : AbsCallBack(originalRequest, mCall) {
private val mResponseCondition = ConditionVariable()
private var mException: IOException? = null
@Throws(IOException::class)
override fun waitForDone(urlRequest: UrlRequest): Response {
//获取okhttp call的完整请求的超时时间
val timeOutMs: Long = mCall.timeout().timeoutNanos() / 1000000
urlRequest.start()
if (timeOutMs > 0) {
mResponseCondition.block(timeOutMs)
} else {
@ -32,35 +31,25 @@ class OldCallback(originalRequest: Request, mCall: Call) : AbsCallBack(originalR
if (mException != null) {
throw mException as IOException
}
return this.mResponse
return mResponse
}
override fun onReadCompleted(
request: UrlRequest,
info: UrlResponseInfo,
byteBuffer: ByteBuffer
) {
super.onReadCompleted(request, info, byteBuffer)
if (mException != null) {
/**
* 当发生错误时通知子类终止阻塞抛出错误
* @param error
*/
override fun onError(error: IOException) {
mException = error
mResponseCondition.open()
}
}
override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
super.onSucceeded(request, info)
/**
* 请求成功后通知子类结束阻塞返回response
* @param response
*/
override fun onSuccess(response: Response) {
mResponseCondition.open()
}
override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
super.onFailed(request, info, error)
mResponseCondition.open()
}
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
super.onCanceled(request, info)
mResponseCondition.open()
}
}

@ -5,9 +5,10 @@ import android.view.View
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.databinding.DialogPhotoViewBinding
import io.legado.app.help.BookHelp
import io.legado.app.help.glide.ImageLoader
import io.legado.app.model.BookCover
import io.legado.app.model.ReadBook
import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.utils.setLayout
import io.legado.app.utils.viewbindingdelegate.viewBinding
@ -40,17 +41,12 @@ class PhotoDialog() : BaseDialogFragment(R.layout.dialog_photo_view) {
arguments?.let {
val path = it.getString("path")
if (path.isNullOrEmpty()) {
val chapterIndex = it.getInt("chapterIndex")
val src = it.getString("src")
ReadBook.book?.let { book ->
src?.let {
execute {
ImageProvider.getImage(book, src, ReadBook.bookSource)
}.onSuccess { bitmap ->
if (bitmap != null) {
binding.photoView.setImageBitmap(bitmap)
}
}
it.getString("src")?.let { src ->
val file = BookHelp.getImage(book, src)
ImageLoader.load(requireContext(), file)
.error(R.drawable.image_loading_error)
.into(binding.photoView)
}
}
} else {

@ -82,43 +82,36 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
private fun drawPage(canvas: Canvas) {
var relativeOffset = relativeOffset(0)
textPage.textLines.forEach { textLine ->
draw(canvas, textLine, relativeOffset)
draw(canvas, textPage, textLine, relativeOffset)
}
if (!callBack.isScroll) return
//滚动翻页
if (!pageFactory.hasNext()) return
val nextPage = relativePage(1)
val textPage1 = relativePage(1)
relativeOffset = relativeOffset(1)
nextPage.textLines.forEach { textLine ->
draw(canvas, textLine, relativeOffset)
textPage1.textLines.forEach { textLine ->
draw(canvas, textPage1, textLine, relativeOffset)
}
if (!pageFactory.hasNextPlus()) return
relativeOffset = relativeOffset(2)
if (relativeOffset < ChapterProvider.visibleHeight) {
relativePage(2).textLines.forEach { textLine ->
draw(canvas, textLine, relativeOffset)
val textPage2 = relativePage(2)
textPage2.textLines.forEach { textLine ->
draw(canvas, textPage2, textLine, relativeOffset)
}
}
}
private fun draw(
canvas: Canvas,
textPage: TextPage,
textLine: TextLine,
relativeOffset: Float,
) {
val lineTop = textLine.lineTop + relativeOffset
val lineBase = textLine.lineBase + relativeOffset
val lineBottom = textLine.lineBottom + relativeOffset
drawChars(
canvas,
textLine.textChars,
lineTop,
lineBase,
lineBottom,
textLine.isTitle,
textLine.isReadAloud,
textLine.isImage
)
drawChars(canvas, textPage, textLine, lineTop, lineBase, lineBottom)
}
/**
@ -126,23 +119,21 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
*/
private fun drawChars(
canvas: Canvas,
textChars: List<TextChar>,
textPage: TextPage,
textLine: TextLine,
lineTop: Float,
lineBase: Float,
lineBottom: Float,
isTitle: Boolean,
isReadAloud: Boolean,
isImageLine: Boolean
) {
val textPaint = if (isTitle) {
val textPaint = if (textLine.isTitle) {
ChapterProvider.titlePaint
} else {
ChapterProvider.contentPaint
}
val textColor = if (isReadAloud) context.accentColor else ReadBookConfig.textColor
textChars.forEach {
val textColor = if (textLine.isReadAloud) context.accentColor else ReadBookConfig.textColor
textLine.textChars.forEach {
if (it.isImage) {
drawImage(canvas, it, lineTop, lineBottom, isImageLine)
drawImage(canvas, textPage, textLine, it, lineTop, lineBottom)
} else {
textPaint.color = textColor
if (it.isSearchResult) {
@ -159,22 +150,23 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
/**
* 绘制图片
*/
@Suppress("UNUSED_PARAMETER")
private fun drawImage(
canvas: Canvas,
textPage: TextPage,
textLine: TextLine,
textChar: TextChar,
lineTop: Float,
lineBottom: Float,
isImageLine: Boolean
lineBottom: Float
) {
val book = ReadBook.book ?: return
ImageProvider.getImage(
val bitmap = ImageProvider.getImage(
book,
textChar.charData,
ReadBook.bookSource,
(textChar.end - textChar.start).toInt(),
(lineBottom - lineTop).toInt()
)?.let { bitmap ->
val rectF = if (isImageLine) {
)
val rectF = if (textLine.isImage) {
RectF(textChar.start, lineTop, textChar.end, lineBottom)
} else {
/*以宽度为基准保持图片的原始比例叠加,当div为负数时,允许高度比字符更高*/
@ -188,7 +180,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
context.toastOnUi(e.localizedMessage)
}
}
}
/**
* 滚动事件

@ -208,22 +208,23 @@ object ChapterProvider {
imageStyle: String?,
): Float {
var durY = y
ImageProvider.getImageSize(book, src, ReadBook.bookSource).let {
val size = ImageProvider.getImageSize(book, src, ReadBook.bookSource)
if (size.width > 0 && size.height > 0) {
if (durY > visibleHeight) {
textPages.last().height = durY
textPages.add(TextPage())
durY = 0f
}
var height = it.height
var width = it.width
var height = size.height
var width = size.width
when (imageStyle?.toUpperCase(Locale.ROOT)) {
Book.imgStyleFull -> {
width = visibleWidth
height = it.height * visibleWidth / it.width
height = size.height * visibleWidth / size.width
}
else -> {
if (it.width > visibleWidth) {
height = it.height * visibleWidth / it.width
if (size.width > visibleWidth) {
height = size.height * visibleWidth / size.width
width = visibleWidth
}
if (height > visibleHeight) {

@ -9,16 +9,13 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource
import io.legado.app.help.BookHelp
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.glide.ImageLoader
import io.legado.app.model.localBook.EpubFile
import io.legado.app.utils.BitmapUtils
import io.legado.app.utils.FileUtils
import io.legado.app.utils.isXml
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.suspendCancellableCoroutine
import splitties.init.appCtx
import java.io.File
import java.io.FileOutputStream
import kotlin.coroutines.resume
object ImageProvider {
@ -26,7 +23,7 @@ object ImageProvider {
BitmapFactory.decodeResource(appCtx.resources, R.drawable.image_loading_error)
}
private suspend fun cacheImage(
suspend fun cacheImage(
book: Book,
src: String,
bookSource: BookSource?
@ -54,59 +51,26 @@ object ImageProvider {
bookSource: BookSource?
): Size {
val file = cacheImage(book, src, bookSource)
return suspendCancellableCoroutine { block ->
kotlin.runCatching {
ImageLoader.loadBitmap(appCtx, file.absolutePath).submit()
.getSize { width, height ->
block.resume(Size(width, height))
}
}.onFailure {
block.resume(Size(errorBitmap.width, errorBitmap.height))
}
}
val op = BitmapFactory.Options()
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
op.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, op)
return Size(op.outWidth, op.outHeight)
}
fun getImage(
book: Book,
src: String,
bookSource: BookSource?,
width: Int,
height: Int
): Bitmap? {
val vFile = runBlocking {
cacheImage(book, src, bookSource)
}
return try {
ImageLoader.loadBitmap(appCtx, vFile.absolutePath)
.submit(width, height)
.get()
} catch (e: Exception) {
Coroutine.async {
putDebug("${vFile.absolutePath} 解码失败\n${e.toString()}", e)
if (FileUtils.readText(vFile.absolutePath).isXml()) {
putDebug("${vFile.absolutePath}为xml,自动删除")
vFile.delete()
}
}
errorBitmap
}
}
fun getImage(
book: Book,
src: String,
bookSource: BookSource?
): Bitmap? {
val vFile = runBlocking {
cacheImage(book, src, bookSource)
}
): Bitmap {
val vFile = BookHelp.getImage(book, src)
@Suppress("BlockingMethodInNonBlockingContext")
return try {
ImageLoader.loadBitmap(appCtx, vFile.absolutePath)
.submit(ChapterProvider.visibleWidth, ChapterProvider.visibleHeight)
.get()
BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
} catch (e: Exception) {
Coroutine.async {
putDebug("${vFile.absolutePath} 解码失败\n${e.toString()}", e)
putDebug("${vFile.absolutePath} 解码失败\n$e", e)
if (FileUtils.readText(vFile.absolutePath).isXml()) {
putDebug("${vFile.absolutePath}为xml,自动删除")
vFile.delete()

@ -7,7 +7,6 @@ import android.graphics.*
import android.graphics.Bitmap.Config
import android.view.View
import com.google.android.renderscript.Toolkit
import java.io.FileInputStream
import java.io.IOException
import kotlin.math.*
@ -27,10 +26,9 @@ object BitmapUtils {
@Throws(IOException::class)
fun decodeBitmap(path: String, width: Int, height: Int): Bitmap {
val op = BitmapFactory.Options()
val ips = FileInputStream(path)
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
op.inJustDecodeBounds = true
BitmapFactory.decodeFileDescriptor(ips.fd, null, op)
BitmapFactory.decodeFile(path, op)
//获取比例大小
val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
@ -43,7 +41,7 @@ object BitmapUtils {
}
}
op.inJustDecodeBounds = false
return BitmapFactory.decodeFileDescriptor(ips.fd, null, op)
return BitmapFactory.decodeFile(path, op)
}
/** 从path中获取Bitmap图片
@ -53,12 +51,11 @@ object BitmapUtils {
@Throws(IOException::class)
fun decodeBitmap(path: String): Bitmap {
val opts = BitmapFactory.Options()
val ips = FileInputStream(path)
opts.inJustDecodeBounds = true
BitmapFactory.decodeFileDescriptor(ips.fd, null, opts)
BitmapFactory.decodeFile(path, opts)
opts.inSampleSize = computeSampleSize(opts, -1, 128 * 128)
opts.inJustDecodeBounds = false
return BitmapFactory.decodeFileDescriptor(ips.fd, null, opts)
return BitmapFactory.decodeFile(path, opts)
}
/**

@ -12,8 +12,8 @@ val Throwable.msg: String
}
}
fun Throwable.rethrowAsIOException(): IOException {
fun Throwable.asIOException(): IOException {
val newException = IOException(this.message)
newException.initCause(this)
throw newException
return newException
}
Loading…
Cancel
Save