Work on result

This commit is contained in:
Oupson 2021-06-25 13:50:27 +02:00
parent 4ef644632a
commit 31c7529d45
2 changed files with 454 additions and 451 deletions

View File

@ -53,7 +53,7 @@ class ApngDecoder(input: InputStream, val config: Config) {
} }
} }
private val inputStream: InputStream? = input private var inputStream: InputStream? = input
private var result: Result<Drawable>? = null private var result: Result<Drawable>? = null
/** /**
@ -65,7 +65,9 @@ class ApngDecoder(input: InputStream, val config: Config) {
*/ */
suspend fun decodeApng( suspend fun decodeApng(
context: Context context: Context
): Drawable = withContext(Dispatchers.Default) { ): Result<Drawable> =
kotlin.runCatching {
withContext(Dispatchers.Default) {
val inputStream = BufferedInputStream(inputStream) val inputStream = BufferedInputStream(inputStream)
val bytes = ByteArray(8) val bytes = ByteArray(8)
inputStream.mark(8) inputStream.mark(8)
@ -83,7 +85,8 @@ class ApngDecoder(input: InputStream, val config: Config) {
var tnrs: ByteArray? = null var tnrs: ByteArray? = null
var maxWidth = 0 var maxWidth = 0
var maxHeight = 0 var maxHeight = 0
var blendOp: Utils.Companion.BlendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE var blendOp: Utils.Companion.BlendOp =
Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE
var disposeOp: Utils.Companion.DisposeOp = var disposeOp: Utils.Companion.DisposeOp =
Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE
@ -494,13 +497,13 @@ class ApngDecoder(input: InputStream, val config: Config) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
inputStream.close() inputStream.close()
} }
return@withContext drawable drawable
} else { } else {
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
Log.i(TAG, "Decoding non APNG stream") Log.i(TAG, "Decoding non APNG stream")
inputStream.reset() inputStream.reset()
return@withContext if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val bytesRead: ByteArray val bytesRead: ByteArray
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
bytesRead = inputStream.readBytes() bytesRead = inputStream.readBytes()
@ -519,16 +522,17 @@ class ApngDecoder(input: InputStream, val config: Config) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
inputStream.close() inputStream.close()
} }
drawable drawable!!
}
} }
} }
} }
suspend fun getDecoded(context: Context): Result<Drawable> { suspend fun getDecoded(context: Context): Result<Drawable> {
if (result == null) { if (result == null) {
result = kotlin.runCatching { result =
decodeApng(context) decodeApng(context)
}
kotlin.runCatching { kotlin.runCatching {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -537,6 +541,8 @@ class ApngDecoder(input: InputStream, val config: Config) {
}.onFailure { }.onFailure {
return Result.failure(it) return Result.failure(it)
} }
inputStream = null
} }
return result ?: Result.failure(NullPointerException("result is null")) return result ?: Result.failure(NullPointerException("result is null"))
@ -639,12 +645,13 @@ class ApngDecoder(input: InputStream, val config: Config) {
* @param config Decoder configuration * @param config Decoder configuration
* @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif. * @return [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif.
*/ */
@Suppress("unused", "BlockingMethodInNonBlockingContext") @Suppress("unused")
@JvmStatic @JvmStatic
suspend fun constructFromUrl( suspend fun constructFromUrl(
url: URL, url: URL,
config: Config = Config() config: Config = Config()
) = ): Result<ApngDecoder> =
kotlin.runCatching {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
ApngDecoder( ApngDecoder(
ByteArrayInputStream(Loader.load(url)), ByteArrayInputStream(Loader.load(url)),
@ -652,4 +659,5 @@ class ApngDecoder(input: InputStream, val config: Config) {
) )
} }
} }
}
} }

View File

@ -153,7 +153,9 @@ class ApngLoader(parent: Job? = null) {
imageView: ImageView, imageView: ImageView,
config: ApngDecoder.Config = ApngDecoder.Config() config: ApngDecoder.Config = ApngDecoder.Config()
): Result<Drawable> { ): Result<Drawable> {
val result = ApngDecoder.constructFromUrl(url, config).getDecoded(context) val result =
ApngDecoder.constructFromUrl(url, config).getOrElse { return Result.failure(it) }
.getDecoded(context)
if (result.isSuccess) { if (result.isSuccess) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val drawable = result.getOrNull() val drawable = result.getOrNull()
@ -204,10 +206,10 @@ class ApngLoader(parent: Job? = null) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
context.assets.open(string.replace("file:///android_asset/", "")) context.assets.open(string.replace("file:///android_asset/", ""))
} }
}.onFailure { }.getOrElse {
return Result.failure(it) return Result.failure(it)
} }
val result = ApngDecoder(inputStream.getOrThrow(), config).getDecoded(context) val result = ApngDecoder(inputStream, config).getDecoded(context)
if (result.isSuccess) { if (result.isSuccess) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val drawable = result.getOrNull() val drawable = result.getOrNull()
@ -246,11 +248,9 @@ class ApngLoader(parent: Job? = null) {
coroutineScope.launch(Dispatchers.Default) { coroutineScope.launch(Dispatchers.Default) {
val drawable = decodeApngInto(context, file, imageView, config) val drawable = decodeApngInto(context, file, imageView, config)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (drawable.isSuccess) { drawable
callback?.onSuccess(drawable.getOrNull()!!) .onSuccess { callback?.onSuccess(it) }
} else { .onFailure { callback?.onError(it) }
callback?.onError(drawable.exceptionOrNull()!!)
}
} }
} }
@ -274,11 +274,9 @@ class ApngLoader(parent: Job? = null) {
) = coroutineScope.launch(Dispatchers.Default) { ) = coroutineScope.launch(Dispatchers.Default) {
val drawable = decodeApngInto(context, uri, imageView, config) val drawable = decodeApngInto(context, uri, imageView, config)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (drawable.isSuccess) { drawable
callback?.onSuccess(drawable.getOrNull()!!) .onSuccess { callback?.onSuccess(it) }
} else { .onFailure { callback?.onError(it) }
callback?.onError(drawable.exceptionOrNull()!!)
}
} }
} }
@ -300,11 +298,9 @@ class ApngLoader(parent: Job? = null) {
) = coroutineScope.launch(Dispatchers.Default) { ) = coroutineScope.launch(Dispatchers.Default) {
val drawable = decodeApngInto(context, res, imageView, config) val drawable = decodeApngInto(context, res, imageView, config)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (drawable.isSuccess) { drawable
callback?.onSuccess(drawable.getOrNull()!!) .onSuccess { callback?.onSuccess(it) }
} else { .onFailure { callback?.onError(it) }
callback?.onError(drawable.exceptionOrNull()!!)
}
} }
} }
@ -327,14 +323,13 @@ class ApngLoader(parent: Job? = null) {
) = coroutineScope.launch(Dispatchers.Default) { ) = coroutineScope.launch(Dispatchers.Default) {
val drawable = decodeApngInto(context, url, imageView, config) val drawable = decodeApngInto(context, url, imageView, config)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (drawable.isSuccess) { drawable
callback?.onSuccess(drawable.getOrNull()!!) .onSuccess { callback?.onSuccess(it) }
} else { .onFailure { callback?.onError(it) }
callback?.onError(drawable.exceptionOrNull()!!)
}
} }
} }
/** /**
* Load Apng into an imageView, asynchronously. * Load Apng into an imageView, asynchronously.
* @param context Context needed for decoding the image and creating the animation drawable. * @param context Context needed for decoding the image and creating the animation drawable.