Better loader and add option to load not apng files

This commit is contained in:
oupson 2018-11-17 20:08:15 +01:00
parent dd7b36fb3f
commit 8c1e155695
5 changed files with 177 additions and 106 deletions

View File

@ -7,7 +7,7 @@ android {
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0.6"
versionName "1.0.7"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@ -1,10 +1,14 @@
package oupson.apng
import android.content.Context
import android.content.SharedPreferences
import android.graphics.*
import android.net.Uri
import android.widget.ImageView
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.runOnUiThread
import org.jetbrains.anko.uiThread
import oupson.apng.Utils.Companion.isApng
import oupson.apng.exceptions.NotApngException
import java.io.File
import java.net.URL
@ -17,16 +21,18 @@ class ApngAnimator(private val context: Context) {
private set(value) {
field = value
}
private var frames = ArrayList<Frame>()
private val generatedFrame = ArrayList<Bitmap>()
var speed: Float? = null
set(value) {
field = value
try {
pause()
play()
} catch ( e : Exception) { }
if (isApng) {
field = value
try {
pause()
play()
} catch (e: Exception) {
}
}
}
private var bitmapBuffer: Bitmap? = null
private var imageView: ImageView? = null
@ -35,6 +41,21 @@ class ApngAnimator(private val context: Context) {
private var doOnLoaded : (ApngAnimator) -> Unit = {}
private var AnimationLoopListener : () -> Unit = {}
private var duration : ArrayList<Float>? = null
var isApng = false
var loadNoApng = true
private val sharedPreferences : SharedPreferences = context.getSharedPreferences("apngAnimator", Context.MODE_PRIVATE)
init {
loadNoApng = sharedPreferences.getBoolean("loadNoApng", true)
}
fun loadNoApng(boolean: Boolean) {
val editor = sharedPreferences.edit()
editor.putBoolean("loadNoApng", boolean)
editor.apply()
}
/**
* Load into an imageview
* @param imageView Image view selected.
@ -51,12 +72,47 @@ class ApngAnimator(private val context: Context) {
*/
fun load(file: File, speed: Float? = null) {
doAsync {
this@ApngAnimator.speed = speed
// Download PNG
APNGDisassembler.disassemble(file.readBytes()).frames.apply {
draw(this)
val bytes = file.readBytes()
if (isApng(bytes)) {
isApng = true
this@ApngAnimator.speed = speed
// Download PNG
APNGDisassembler.disassemble(file.readBytes()).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
} else {
if (loadNoApng) {
context.runOnUiThread {
imageView?.setImageBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.size))
}
} else {
throw NotApngException()
}
}
}
}
fun load(uri : Uri, speed: Float? = null) {
doAsync {
val bytes = context.contentResolver.openInputStream(uri).readBytes()
if (isApng(bytes)) {
isApng = true
this@ApngAnimator.speed = speed
// Download PNG
APNGDisassembler.disassemble(bytes).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
} else {
if (loadNoApng) {
context.runOnUiThread {
imageView?.setImageBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.size))
}
} else {
throw NotApngException()
}
}
setupAnimationDrawableAndStart()
}
}
@ -70,10 +126,25 @@ class ApngAnimator(private val context: Context) {
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
this@ApngAnimator.speed = speed
// Download PNG
APNGDisassembler.disassemble(Loader.load(context, url)).frames.apply {
draw(this)
Loader.load(context, url).apply {
if (isApng(this)) {
isApng = true
APNGDisassembler.disassemble(this).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
} else {
if (loadNoApng) {
context.runOnUiThread {
imageView?.setImageBitmap(BitmapFactory.decodeByteArray(this@apply, 0, this@apply.size))
}
} else {
throw NotApngException()
}
}
}
setupAnimationDrawableAndStart()
}
}
@ -86,10 +157,55 @@ class ApngAnimator(private val context: Context) {
fun load(byteArray: ByteArray, speed: Float? = null) {
doAsync {
this@ApngAnimator.speed = speed
APNGDisassembler.disassemble(byteArray).frames.apply {
draw(this)
if (isApng(byteArray)) {
isApng = true
APNGDisassembler.disassemble(byteArray).frames.apply {
draw(this)
}
setupAnimationDrawableAndStart()
} else {
if (loadNoApng) {
context.runOnUiThread {
imageView?.setImageBitmap(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size))
}
} else {
throw NotApngException()
}
}
}
}
/**
* Load an APNG file
* @param context The current context.
* @param string Path of the file.
* @param animationListener The listener that will be invoked when there are specific animation events.
* @param frameDuration The duration to show each frame. If this is null then the duration specified
* in the APNG will be used instead.
* @throws NotApngException
*/
fun load(string: String, speed : Float? = null) {
doAsync {
this@ApngAnimator.speed = speed
if (string.contains("http") || string.contains("https")) {
val url = URL(string)
loadUrl(url, speed)
} else if (File(string).exists()) {
var pathToLoad = if (string.startsWith("content://")) string else "file://$string"
pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
val bytes = context.contentResolver.openInputStream(Uri.parse(pathToLoad)).readBytes()
if (isApng(bytes)) {
load(bytes, speed)
} else {
if (loadNoApng) {
context.runOnUiThread {
imageView?.setImageBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.size))
}
} else {
throw NotApngException()
}
}
}
setupAnimationDrawableAndStart()
}
}
@ -114,25 +230,6 @@ class ApngAnimator(private val context: Context) {
}
}
/**
* Load an APNG file
* @param context The current context.
* @param string Path of the file.
* @param animationListener The listener that will be invoked when there are specific animation events.
* @param frameDuration The duration to show each frame. If this is null then the duration specified
* in the APNG will be used instead.
* @throws NotApngException
*/
fun load(string: String, speed : Float? = null) {
this.speed = speed
if (string.contains("http") || string.contains("https")) {
val url = URL(string)
loadUrl(url, speed)
} else if (File(string).exists()) {
load(File(string), speed)
}
}
/**
* Draw frames
*/
@ -179,43 +276,49 @@ class ApngAnimator(private val context: Context) {
}
fun pause() {
isPlaying = false
val animResume = CustomAnimationDrawable()
activeAnimation?.stop()
val currentFrame = activeAnimation!!.current
val dura = ArrayList<Float>()
frameLoop@ for (i in 0 until anim?.numberOfFrames!!) {
val checkFrame = activeAnimation!!.getFrame(i)
if (checkFrame === currentFrame) {
val frameIndex = i
for (k in frameIndex until activeAnimation!!.numberOfFrames) {
val frame = activeAnimation!!.getFrame(k)
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
dura.add(duration!![k])
if (isApng) {
isPlaying = false
val animResume = CustomAnimationDrawable()
activeAnimation?.stop()
val currentFrame = activeAnimation!!.current
val dura = ArrayList<Float>()
frameLoop@ for (i in 0 until anim?.numberOfFrames!!) {
val checkFrame = activeAnimation!!.getFrame(i)
if (checkFrame === currentFrame) {
val frameIndex = i
for (k in frameIndex until activeAnimation!!.numberOfFrames) {
val frame = activeAnimation!!.getFrame(k)
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
dura.add(duration!![k])
}
for (k in 0 until frameIndex) {
val frame = activeAnimation!!.getFrame(k)
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
dura.add(duration!![k])
}
activeAnimation = animResume
imageView?.setImageDrawable(activeAnimation)
activeAnimation?.setOnAnimationLoopListener(AnimationLoopListener)
imageView?.invalidate()
duration = dura
break@frameLoop
}
for (k in 0 until frameIndex) {
val frame = activeAnimation!!.getFrame(k)
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
dura.add(duration!![k])
}
activeAnimation = animResume
imageView?.setImageDrawable(activeAnimation)
activeAnimation?.setOnAnimationLoopListener(AnimationLoopListener)
imageView?.invalidate()
duration = dura
break@frameLoop
}
}
}
fun play() {
isPlaying = true
activeAnimation?.start()
if (isApng) {
isPlaying = true
activeAnimation?.start()
}
}
fun setOnAnimationLoopListener(animationLoopListener : () -> Unit) {
AnimationLoopListener = animationLoopListener
anim?.setOnAnimationLoopListener(animationLoopListener)
if (isApng) {
AnimationLoopListener = animationLoopListener
anim?.setOnAnimationLoopListener(animationLoopListener)
}
}
fun onLoaded(f : (ApngAnimator) -> Unit) {
@ -227,11 +330,15 @@ class ApngAnimator(private val context: Context) {
* in the APNG will be used instead.
*/
private fun toAnimationDrawable( ): CustomAnimationDrawable {
return CustomAnimationDrawable().apply {
for (i in 0 until generatedFrame.size) {
addFrame(BitmapDrawable(generatedFrame[i]), ((frames[i].delay).toInt() / (speed ?: 1f)).toInt())
if (isApng) {
return CustomAnimationDrawable().apply {
for (i in 0 until generatedFrame.size) {
addFrame(BitmapDrawable(generatedFrame[i]), ((frames[i].delay).toInt() / (speed
?: 1f)).toInt())
}
}
} else {
throw NotApngException()
}
}
}

View File

@ -48,32 +48,7 @@ class Main2Activity : AppCompatActivity() {
fun load() {
val animator = ApngAnimator(applicationContext).loadInto(imageView3)
val uri = intent.data
if (uri.toString().contains("file:///")) {
try {
if (isApng(File(uri.path).readBytes())) {
animator.load(uri.path)
} else {
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
Snackbar.make(constraint, "Not an APNG, and verified !", Snackbar.LENGTH_LONG).show()
}
} catch (e: NotApngException) {
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
}
} else {
try {
val bytes = contentResolver.openInputStream(uri).readBytes()
if (isApng(bytes)) {
animator.load(bytes)
} else {
imageView3.setImageBitmap(BitmapFactory.decodeFile(getImageRealPath(contentResolver, uri, null)))
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
}
} catch (e: NotApngException) {
imageView3.setImageBitmap(BitmapFactory.decodeFile(getImageRealPath(contentResolver, uri, null)))
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
}
}
animator.load(uri)
imageView3.onClick {
try {
if (animator.isPlaying) {

View File

@ -34,17 +34,6 @@ class MainActivity : AppCompatActivity() {
}
}
doAsync {
Loader.load(applicationContext, URL(imageUrl)).apply {
val a = APNGDisassembler.disassemble(this)
a.reduceSize(100, false, 60)
File(File(Environment.getExternalStorageDirectory(), "Download"), "apng.png").writeBytes(a.toByteArray())
runOnUiThread {
toast("Converted ! ")
}
}
}
this.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progressValue: Int, fromUser: Boolean) {