diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index 746bc38..bb552e8 100644
Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ
diff --git a/apng_library/src/main/java/oupson/apng/ExperimentalApngDecoder.kt b/apng_library/src/main/java/oupson/apng/ExperimentalApngDecoder.kt
index a53aa1f..6757e25 100644
--- a/apng_library/src/main/java/oupson/apng/ExperimentalApngDecoder.kt
+++ b/apng_library/src/main/java/oupson/apng/ExperimentalApngDecoder.kt
@@ -5,8 +5,11 @@ import android.graphics.*
import android.graphics.drawable.Drawable
import android.net.Uri
import android.util.Log
+import android.widget.ImageView
import androidx.annotation.RawRes
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import oupson.apng.chunks.IHDR
import oupson.apng.chunks.fcTL
@@ -23,6 +26,11 @@ import java.util.zip.CRC32
// TODO DOC CODE
class ExperimentalApngDecoder {
+ interface Callback {
+ fun onSuccess(drawable : Drawable)
+ fun onError(error : java.lang.Exception)
+ }
+
companion object {
// TODO Change TAG
private const val TAG = "ExperimentalApngDecoder"
@@ -365,6 +373,159 @@ class ExperimentalApngDecoder {
decodeApng(FileInputStream(Loader.load(context, url)), speed)
}
+ /**
+ * Load Apng into an imageView, asynchronously
+ * @param file File to decode.
+ * @param imageView Image View.
+ * @param speed Optional parameter.
+ * @param callback [ExperimentalApngDecoder.Callback] to handle success and error
+ */
+ @Suppress("unused")
+ @JvmStatic
+ @JvmOverloads
+ fun decodeApngAsyncInto(file : File, imageView : ImageView, speed: Float = 1f, callback: Callback? = null) {
+ GlobalScope.launch(Dispatchers.IO) {
+ try {
+ val drawable = decodeApng(FileInputStream(file), speed)
+ withContext(Dispatchers.Main) {
+ imageView.setImageDrawable(drawable)
+ (drawable as? CustomAnimationDrawable)?.start()
+ callback?.onSuccess(drawable)
+ }
+ } catch (e : java.lang.Exception) {
+ withContext(Dispatchers.Main) {
+ callback?.onError(e)
+ }
+ }
+ }
+ }
+
+ /**
+ * Load Apng into an imageView, asynchronously
+ * @param context Context
+ * @param uri Uri to load
+ * @param imageView Image View.
+ * @param speed Optional parameter.
+ * @param callback [ExperimentalApngDecoder.Callback] to handle success and error
+ */
+ @Suppress("unused")
+ @JvmStatic
+ @JvmOverloads
+ fun decodeApngAsyncInto(context : Context, uri : Uri, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) {
+ val inputStream = context.contentResolver.openInputStream(uri) ?: throw Exception("Failed to open InputStream, InputStream is null")
+ GlobalScope.launch(Dispatchers.IO) {
+ try {
+ val drawable = decodeApng(inputStream, speed)
+ withContext(Dispatchers.Main) {
+ imageView.setImageDrawable(drawable)
+ (drawable as? CustomAnimationDrawable)?.start()
+ callback?.onSuccess(drawable)
+ }
+ } catch (e : java.lang.Exception) {
+ withContext(Dispatchers.Main) {
+ callback?.onError(e)
+ }
+ }
+ }
+ }
+
+ /**
+ * Load Apng into an imageView, asynchronously
+ * @param context Context
+ * @param res Raw resource to load
+ * @param imageView Image View.
+ * @param speed Optional parameter.
+ * @param callback [ExperimentalApngDecoder.Callback] to handle success and error
+ */
+ @Suppress("unused")
+ @JvmStatic
+ @JvmOverloads
+ fun decodeApngAsyncInto(context : Context, @RawRes res : Int, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) {
+ GlobalScope.launch(Dispatchers.IO) {
+ try {
+ val drawable = decodeApng(context.resources.openRawResource(res), speed)
+ withContext(Dispatchers.Main) {
+ imageView.setImageDrawable(drawable)
+ (drawable as? CustomAnimationDrawable)?.start()
+ callback?.onSuccess(drawable)
+ }
+ } catch (e : java.lang.Exception) {
+ withContext(Dispatchers.Main) {
+ callback?.onError(e)
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Load Apng into an imageView, asynchronously
+ * @param context Context
+ * @param url URL to load
+ * @param imageView Image View.
+ * @param speed Optional parameter.
+ * @param callback [ExperimentalApngDecoder.Callback] to handle success and error
+ */
+ @Suppress("unused")
+ @JvmStatic
+ @JvmOverloads
+ fun decodeApngAsyncInto(context : Context, url : URL, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) {
+ GlobalScope.launch(Dispatchers.IO) {
+ try {
+ val drawable = decodeApng(FileInputStream(Loader.load(context, url)), speed)
+ withContext(Dispatchers.Main) {
+ imageView.setImageDrawable(drawable)
+ (drawable as? CustomAnimationDrawable)?.start()
+ callback?.onSuccess(drawable)
+ }
+ } catch (e : java.lang.Exception) {
+ withContext(Dispatchers.Main) {
+ callback?.onError(e)
+ }
+ }
+ }
+ }
+
+ /**
+ * Load Apng into an imageView, asynchronously
+ * @param context Context
+ * @param string URL to load
+ * @param imageView Image View.
+ * @param speed Optional parameter.
+ * @param callback [ExperimentalApngDecoder.Callback] to handle success and error
+ */
+ @Suppress("unused")
+ @JvmStatic
+ @JvmOverloads
+ fun decodeApngAsyncInto(context : Context, string: String, imageView: ImageView, speed: Float = 1f, callback: Callback? = null) {
+ GlobalScope.launch(Dispatchers.IO) {
+ try {
+ if (string.startsWith("http://") || string.startsWith("https://")) {
+ decodeApngAsyncInto(context, URL(string), imageView, speed, callback)
+ } else if(File(string).exists()) {
+ var pathToLoad = if (string.startsWith("content://")) string else "file://$string"
+ pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
+ decodeApngAsyncInto(context, Uri.parse(pathToLoad), imageView, speed, callback)
+ } else if (string.startsWith("file://android_asset/")) {
+ val drawable = decodeApng(context.assets.open(string.replace("file:///android_asset/", "")), speed)
+ withContext(Dispatchers.Main) {
+ imageView.setImageDrawable(drawable)
+ (drawable as? CustomAnimationDrawable)?.start()
+ callback?.onSuccess(drawable)
+ }
+ } else {
+ withContext(Dispatchers.Main) {
+ callback?.onError(java.lang.Exception("Cannot open string"))
+ }
+ }
+ } catch (e : java.lang.Exception) {
+ withContext(Dispatchers.Main) {
+ callback?.onError(e)
+ }
+ }
+ }
+ }
+
/**
* Generate a correct IHDR from the IHDR chunk of the APNG
* @param ihdrOfApng The IHDR of the APNG
diff --git a/apng_library/src/main/java/oupson/apng/Loader.kt b/apng_library/src/main/java/oupson/apng/Loader.kt
index be2e3f6..7b46062 100644
--- a/apng_library/src/main/java/oupson/apng/Loader.kt
+++ b/apng_library/src/main/java/oupson/apng/Loader.kt
@@ -5,7 +5,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.BufferedInputStream
import java.io.File
-import java.io.IOException
import java.net.URL
class Loader {
@@ -16,22 +15,23 @@ class Loader {
* @param url Url of the file to download
* @return [ByteArray] of the file
*/
- @Throws(IOException::class)
- suspend fun load(context: Context, url: URL): File = withContext(Dispatchers.IO) {
- val currentDir = context.filesDir
- val fileTXT = File(currentDir, "apngLoader.txt")
- val filePNG = File(currentDir, "apngLoader.png")
- if (fileTXT.exists() && url.toString() == fileTXT.readText()) {
- filePNG
- } else {
- val connection = url.openConnection()
- connection.connect()
- val input = BufferedInputStream(connection.getInputStream())
- val bytes = input.readBytes()
- input.close()
- fileTXT.writeText(url.toString())
- filePNG.apply { writeBytes(bytes) }
+ // @Throws(IOException::class, java.io.FileNotFoundException::class, java.lang.Exception::class)
+ suspend fun load(context: Context, url: URL) =
+ withContext(Dispatchers.IO) {
+ val currentDir = context.filesDir
+ val fileTXT = File(currentDir, "apngLoader.txt")
+ val filePNG = File(currentDir, "apngLoader.png")
+ if (fileTXT.exists() && url.toString() == fileTXT.readText()) {
+ filePNG
+ } else {
+ val connection = url.openConnection()
+ connection.connect()
+ val input = BufferedInputStream(connection.getInputStream())
+ val bytes = input.readBytes()
+ input.close()
+ fileTXT.writeText(url.toString())
+ filePNG.apply { writeBytes(bytes) }
+ }
}
- }
}
}
\ No newline at end of file
diff --git a/app-test/src/main/java/oupson/apngcreator/ApngDecoderFragment.kt b/app-test/src/main/java/oupson/apngcreator/ApngDecoderFragment.kt
new file mode 100644
index 0000000..c2e1979
--- /dev/null
+++ b/app-test/src/main/java/oupson/apngcreator/ApngDecoderFragment.kt
@@ -0,0 +1,54 @@
+package oupson.apngcreator
+
+
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.fragment.app.Fragment
+import oupson.apng.ExperimentalApngDecoder
+import java.net.URL
+
+class ApngDecoderFragment : Fragment() {
+ companion object {
+ private const val TAG = "ApngDecoderFragment"
+ @JvmStatic
+ fun newInstance() =
+ ApngDecoderFragment()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ val view = inflater.inflate(R.layout.fragment_apng_decoder, container, false)
+
+ val imageView : ImageView = view.findViewById(R.id.apngDecoderImageView) ?: return view
+
+ if (context != null) {
+ ExperimentalApngDecoder.decodeApngAsyncInto(
+ this.context!!,
+ URL("http://littlesvr.ca/apng/images/GenevaDrive.png"),
+ imageView,
+ callback = object : ExperimentalApngDecoder.Callback {
+ override fun onSuccess(drawable: Drawable) {
+ if (BuildConfig.DEBUG)
+ Log.i(TAG, "onSuccess()")
+ }
+
+ override fun onError(error: Exception) {
+ if (BuildConfig.DEBUG)
+ Log.e(TAG, "onError", error)
+ }
+ })
+ }
+
+ return view
+ }
+
+
+}
diff --git a/app-test/src/main/java/oupson/apngcreator/JavaFragment.java b/app-test/src/main/java/oupson/apngcreator/JavaFragment.java
index 76fe2d3..5eb91a7 100644
--- a/app-test/src/main/java/oupson/apngcreator/JavaFragment.java
+++ b/app-test/src/main/java/oupson/apngcreator/JavaFragment.java
@@ -1,6 +1,8 @@
package oupson.apngcreator;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -10,9 +12,9 @@ import android.widget.ImageView;
import androidx.fragment.app.Fragment;
-import kotlin.Unit;
-import oupson.apng.ApngAnimator;
-import oupson.apng.ApngAnimatorKt;
+import org.jetbrains.annotations.NotNull;
+
+import oupson.apng.ExperimentalApngDecoder;
public class JavaFragment extends Fragment {
@@ -22,10 +24,16 @@ public class JavaFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i(TAG, "onCreateView()");
+
View v = inflater.inflate(R.layout.fragment_java, container, false);
+
String imageUrl = "https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png";
ImageView imageView = v.findViewById(R.id.javaImageView);
- if (imageView != null) {
+
+ Context context = this.getContext();
+
+ if (imageView != null && context != null) {
+ /*
ApngAnimator a = ApngAnimatorKt.loadApng(imageView, imageUrl);
a.onLoaded((animator) -> {
animator.setOnFrameChangeLister((index) -> {
@@ -36,6 +44,19 @@ public class JavaFragment extends Fragment {
});
return Unit.INSTANCE;
});
+ */
+ ExperimentalApngDecoder.decodeApngAsyncInto(context, imageUrl, imageView, 1f, new ExperimentalApngDecoder.Callback() {
+ @Override
+ public void onSuccess(@NotNull Drawable drawable) {
+ Log.i(TAG, "Success");
+ }
+
+ @Override
+ public void onError(@NotNull Exception error) {
+ Log.e(TAG, "Error", error);
+ }
+ });
+
}
return v;
}
diff --git a/app-test/src/main/java/oupson/apngcreator/MainActivity.kt b/app-test/src/main/java/oupson/apngcreator/MainActivity.kt
index 71bb185..c8301fe 100644
--- a/app-test/src/main/java/oupson/apngcreator/MainActivity.kt
+++ b/app-test/src/main/java/oupson/apngcreator/MainActivity.kt
@@ -16,6 +16,9 @@ import org.jetbrains.anko.startActivity
class MainActivity : AppCompatActivity() {
+ companion object {
+ private const val TAG = "MainActivity"
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -39,7 +42,7 @@ class MainActivity : AppCompatActivity() {
navigationView.setNavigationItemSelectedListener { menuItem : MenuItem ->
when(menuItem.itemId) {
- R.id.menu_kotlin_activity -> {
+ R.id.menu_kotlin_fragment -> {
if (selected != 0) {
supportFragmentManager.beginTransaction().apply {
replace(R.id.fragment_container, KotlinFragment.newInstance())
@@ -48,7 +51,7 @@ class MainActivity : AppCompatActivity() {
selected = 0
}
}
- R.id.menu_java_activity -> {
+ R.id.menu_java_fragment -> {
if (selected != 1) {
supportFragmentManager.beginTransaction().apply {
replace(R.id.fragment_container, JavaFragment())
@@ -57,6 +60,15 @@ class MainActivity : AppCompatActivity() {
selected = 1
}
}
+ R.id.menu_apng_decoder_fragment -> {
+ if (selected != 2) {
+ supportFragmentManager.beginTransaction().apply {
+ replace(R.id.fragment_container, ApngDecoderFragment.newInstance())
+ addToBackStack(null)
+ }.commit()
+ selected = 2
+ }
+ }
}
drawer_layout.closeDrawer(GravityCompat.START)
@@ -64,12 +76,36 @@ class MainActivity : AppCompatActivity() {
return@setNavigationItemSelectedListener true
}
- supportFragmentManager.beginTransaction().apply {
- add(R.id.fragment_container, KotlinFragment.newInstance(), "KotlinFragment")
- }.commit()
-
-
- navigationView.setCheckedItem(R.id.menu_kotlin_activity)
+ if (intent.hasExtra("fragment")) {
+ when(intent.getStringExtra("fragment")) {
+ "kotlin" -> {
+ supportFragmentManager.beginTransaction().apply {
+ add(R.id.fragment_container, KotlinFragment.newInstance(), "KotlinFragment")
+ }.commit()
+ navigationView.setCheckedItem(R.id.menu_kotlin_fragment)
+ selected = 0
+ }
+ "java" -> {
+ supportFragmentManager.beginTransaction().apply {
+ add(R.id.fragment_container, JavaFragment())
+ }.commit()
+ navigationView.setCheckedItem(R.id.menu_java_fragment)
+ selected = 1
+ }
+ "apng_decoder" -> {
+ supportFragmentManager.beginTransaction().apply {
+ add(R.id.fragment_container, ApngDecoderFragment.newInstance())
+ }.commit()
+ navigationView.setCheckedItem(R.id.menu_apng_decoder_fragment)
+ selected = 2
+ }
+ }
+ } else {
+ supportFragmentManager.beginTransaction().apply {
+ add(R.id.fragment_container, KotlinFragment.newInstance(), "KotlinFragment")
+ }.commit()
+ navigationView.setCheckedItem(R.id.menu_kotlin_fragment)
+ }
}
private fun setUpBottomAppBarShapeAppearance() {
diff --git a/app-test/src/main/res/layout/activity_creator.xml b/app-test/src/main/res/layout/activity_creator.xml
new file mode 100644
index 0000000..b1a8bea
--- /dev/null
+++ b/app-test/src/main/res/layout/activity_creator.xml
@@ -0,0 +1,6 @@
+
+