diff --git a/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt b/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt index bf08a56..d56fa6a 100644 --- a/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt +++ b/apng_library/src/main/java/oupson/apng/decoder/ApngDecoder.kt @@ -3,13 +3,11 @@ package oupson.apng.decoder import android.content.Context import android.graphics.* import android.graphics.drawable.AnimatedImageDrawable -import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build import android.util.Log -import android.widget.ImageView import androidx.annotation.RawRes import kotlinx.coroutines.* import oupson.apng.BuildConfig @@ -30,20 +28,6 @@ import java.util.zip.CRC32 * Call [decodeApng] */ class ApngDecoder { - interface Callback { - /** - * Function called when the file was successfully decoded. - * @param drawable Can be an [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif. If it is not an animated image, it is a [Drawable]. - */ - fun onSuccess(drawable: Drawable) - - /** - * Function called when something gone wrong. - * @param error The problem. - */ - fun onError(error: Exception) - } - class Config( internal var speed: Float = 1f, internal var bitmapConfig: Bitmap.Config = Bitmap.Config.ARGB_8888, @@ -645,252 +629,7 @@ class ApngDecoder { ) } - /** - * Load Apng into an imageView, asynchronously. - * @param context Context needed for animation drawable. - * @param file File to decode. - * @param imageView Image View. - * @param callback [ApngDecoder.Callback] to handle success and error. - * @param config Decoder configuration - */ - @Suppress("unused", "BlockingMethodInNonBlockingContext") - @JvmStatic - @JvmOverloads - fun decodeApngAsyncInto( - context: Context, - file: File, - imageView: ImageView, - callback: Callback? = null, - config: Config = Config(), - scope: CoroutineScope = GlobalScope - ) { - scope.launch(Dispatchers.Default) { - try { - val drawable = - decodeApng( - context, - withContext(Dispatchers.IO) { - FileInputStream(file) - }, - config - ) - withContext(Dispatchers.Main) { - imageView.setImageDrawable(drawable) - (drawable as? AnimationDrawable)?.start() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - (drawable as? AnimatedImageDrawable)?.start() - } - callback?.onSuccess(drawable) - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - callback?.onError(e) - } - } - } - } - /** - * Load Apng into an imageView, asynchronously. - * @param context Context needed for animation drawable and content resolver. - * @param uri Uri to load. - * @param imageView Image View. - * @param callback [ApngDecoder.Callback] to handle success and error. - * @param config Decoder configuration - */ - @Suppress("unused") - @JvmStatic - @JvmOverloads - fun decodeApngAsyncInto( - context: Context, - uri: Uri, - imageView: ImageView, - callback: Callback? = null, - config: Config = Config(), - scope: CoroutineScope = GlobalScope - ) { - val inputStream = context.contentResolver.openInputStream(uri)!! - scope.launch(Dispatchers.Default) { - try { - val drawable = - decodeApng( - context, - inputStream, - config - ) - withContext(Dispatchers.Main) { - imageView.setImageDrawable(drawable) - (drawable as? AnimationDrawable)?.start() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - (drawable as? AnimatedImageDrawable)?.start() - } - callback?.onSuccess(drawable) - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - callback?.onError(e) - } - } - } - } - - /** - * Load Apng into an imageView, asynchronously. - * @param context Context needed to decode the resource and for the animation drawable. - * @param res Raw resource to load. - * @param imageView Image View. - * @param callback [ApngDecoder.Callback] to handle success and error. - * @param config Decoder configuration - */ - @Suppress("unused") - @JvmStatic - @JvmOverloads - fun decodeApngAsyncInto( - context: Context, @RawRes res: Int, - imageView: ImageView, - callback: Callback? = null, - config: Config = Config(), - scope: CoroutineScope = GlobalScope - ) { - scope.launch(Dispatchers.Default) { - try { - val drawable = - decodeApng( - context, - context.resources.openRawResource(res), - config - ) - withContext(Dispatchers.Main) { - imageView.setImageDrawable(drawable) - (drawable as? AnimationDrawable)?.start() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - (drawable as? AnimatedImageDrawable)?.start() - } - callback?.onSuccess(drawable) - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - callback?.onError(e) - } - } - } - - } - - /** - * Load Apng into an imageView, asynchronously. - * @param context Context needed for the animation drawable. - * @param url URL to load. - * @param imageView Image View. - * @param callback [ApngDecoder.Callback] to handle success and error. - * @param config Decoder configuration - */ - @Suppress("unused", "BlockingMethodInNonBlockingContext") - @JvmStatic - @JvmOverloads - fun decodeApngAsyncInto( - context: Context, - url: URL, - imageView: ImageView, - callback: Callback? = null, - config: Config = Config(), - scope: CoroutineScope = GlobalScope - ) { - scope.launch(Dispatchers.Default) { - try { - val drawable = decodeApng( - context, - ByteArrayInputStream( - Loader.load( - url - ) - ), - config - ) - withContext(Dispatchers.Main) { - imageView.setImageDrawable(drawable) - (drawable as? AnimationDrawable)?.start() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - (drawable as? AnimatedImageDrawable)?.start() - } - callback?.onSuccess(drawable) - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - callback?.onError(e) - } - } - } - } - - /** - * Load Apng into an imageView, asynchronously. - * @param context Context needed for decoding the image and creating the animation drawable. - * @param string URL to load - * @param imageView Image View. - * @param callback [ApngDecoder.Callback] to handle success and error. - * @param config Decoder configuration - */ - @Suppress("unused") - @JvmStatic - @JvmOverloads - fun decodeApngAsyncInto( - context: Context, - string: String, - imageView: ImageView, - callback: Callback? = null, - config: Config = Config(), - scope: CoroutineScope = GlobalScope - ) { - scope.launch(Dispatchers.Default) { - try { - if (string.startsWith("http://") || string.startsWith("https://")) { - decodeApngAsyncInto( - context, - URL(string), - imageView, - callback, - config - ) - } 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, - callback, - config - ) - } else if (string.startsWith("file://android_asset/")) { - val drawable = - decodeApng( - context, - context.assets.open(string.replace("file:///android_asset/", "")), - - config - ) - withContext(Dispatchers.Main) { - imageView.setImageDrawable(drawable) - (drawable as? AnimationDrawable)?.start() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - (drawable as? AnimatedImageDrawable)?.start() - } - callback?.onSuccess(drawable) - } - } else { - withContext(Dispatchers.Main) { - callback?.onError(Exception("Cannot open string")) - } - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - callback?.onError(e) - } - } - } - } /** * Generate a correct IHDR from the IHDR chunk of the APNG. diff --git a/apng_library/src/main/java/oupson/apng/decoder/ApngLoader.kt b/apng_library/src/main/java/oupson/apng/decoder/ApngLoader.kt new file mode 100644 index 0000000..bd05ac7 --- /dev/null +++ b/apng_library/src/main/java/oupson/apng/decoder/ApngLoader.kt @@ -0,0 +1,353 @@ +package oupson.apng.decoder + +import android.content.Context +import android.graphics.drawable.AnimatedImageDrawable +import android.graphics.drawable.AnimationDrawable +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Build +import android.widget.ImageView +import androidx.annotation.RawRes +import kotlinx.coroutines.* +import oupson.apng.drawable.ApngDrawable +import oupson.apng.utils.Loader +import java.io.ByteArrayInputStream +import java.io.File +import java.io.FileInputStream +import java.io.FileNotFoundException +import java.net.URL + +class ApngLoader(parent: Job? = null) { + interface Callback { + /** + * Function called when the file was successfully decoded. + * @param drawable Can be an [ApngDrawable] if successful and an [AnimatedImageDrawable] if the image decoded is not an APNG but a gif. If it is not an animated image, it is a [Drawable]. + */ + fun onSuccess(drawable: Drawable) + + /** + * Function called when something gone wrong. + * @param error The problem. + */ + fun onError(error: Exception) + } + + private val job = SupervisorJob(parent) + private val coroutineScope: CoroutineScope = CoroutineScope(job) + + fun cancelAll() { + coroutineScope.cancel(CancellationException("Loading was canceled")) + } + + + /** + * Load Apng into an imageView. + * @param context Context needed for animation drawable. + * @param file File to decode. + * @param imageView Image View. + * @param config Decoder configuration + */ + suspend fun decodeApngInto( + context: Context, + file: File, + imageView: ImageView, + config: ApngDecoder.Config = ApngDecoder.Config() + ): Drawable { + val drawable = + ApngDecoder.decodeApng( + context, + withContext(Dispatchers.IO) { + FileInputStream(file) + }, + config + ) + withContext(Dispatchers.Main) { + imageView.setImageDrawable(drawable) + (drawable as? AnimationDrawable)?.start() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + (drawable as? AnimatedImageDrawable)?.start() + } + } + return drawable + } + + /** + * Load Apng into an imageView. + * @param context Context needed for animation drawable and content resolver. + * @param uri Uri to load. + * @param imageView Image View. + * @param config Decoder configuration + */ + suspend fun decodeApngInto( + context: Context, + uri: Uri, + imageView: ImageView, + config: ApngDecoder.Config = ApngDecoder.Config() + ): Drawable { + val inputStream = + withContext(Dispatchers.IO) { context.contentResolver.openInputStream(uri) } + ?: throw FileNotFoundException("Failed to load $uri") // TODO Better err ? + val drawable = + ApngDecoder.decodeApng( + context, + inputStream, + config + ) + withContext(Dispatchers.Main) { + imageView.setImageDrawable(drawable) + (drawable as? AnimationDrawable)?.start() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + (drawable as? AnimatedImageDrawable)?.start() + } + } + + return drawable + } + + /** + * Load Apng into an imageView. + * @param context Context needed to decode the resource and for the animation drawable. + * @param res Raw resource to load. + * @param imageView Image View. + * @param config Decoder configuration + */ + suspend fun decodeApngInto( + context: Context, @RawRes res: Int, + imageView: ImageView, + config: ApngDecoder.Config = ApngDecoder.Config() + ): Drawable { + val drawable = + ApngDecoder.decodeApng( + context, + context.resources.openRawResource(res), + config + ) + withContext(Dispatchers.Main) { + imageView.setImageDrawable(drawable) + (drawable as? AnimationDrawable)?.start() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + (drawable as? AnimatedImageDrawable)?.start() + } + } + return drawable + } + + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed for the animation drawable. + * @param url URL to load. + * @param imageView Image View. + * @param config Decoder configuration + */ + suspend fun decodeApngInto( + context: Context, + url: URL, + imageView: ImageView, + config: ApngDecoder.Config = ApngDecoder.Config() + ): Drawable { + + val drawable = ApngDecoder.decodeApng( + context, + ByteArrayInputStream( + Loader.load( + url + ) + ), + config + ) + withContext(Dispatchers.Main) { + imageView.setImageDrawable(drawable) + (drawable as? AnimationDrawable)?.start() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + (drawable as? AnimatedImageDrawable)?.start() + } + } + + return drawable + } + + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed for decoding the image and creating the animation drawable. + * @param string URL to load + * @param imageView Image View. + * @param config Decoder configuration + */ + @Suppress("unused") + suspend fun decodeApngInto( + context: Context, + string: String, + imageView: ImageView, + config: ApngDecoder.Config = ApngDecoder.Config() + ): Drawable { + return if (string.startsWith("http://") || string.startsWith("https://")) { + decodeApngInto( + context, + URL(string), + imageView, + config + ) + } else if (File(string).exists()) { + var pathToLoad = + if (string.startsWith("content://")) string else "file://$string" + pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23") + decodeApngInto( + context, + Uri.parse(pathToLoad), + imageView, + config + ) + } else if (string.startsWith("file://android_asset/")) { + val drawable = + ApngDecoder.decodeApng( + context, + context.assets.open(string.replace("file:///android_asset/", "")), + + config + ) + withContext(Dispatchers.Main) { + imageView.setImageDrawable(drawable) + (drawable as? AnimationDrawable)?.start() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + (drawable as? AnimatedImageDrawable)?.start() + } + } + drawable + } else { + throw Exception("Cannot open string") + } + } + + +// region with callback + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed for animation drawable. + * @param file File to decode. + * @param imageView Image View. + * @param callback [ApngLoader.Callback] to handle success and error. + * @param config Decoder configuration + */ + @Suppress("unused", "BlockingMethodInNonBlockingContext") + @JvmOverloads + fun decodeApngAsyncInto( + context: Context, + file: File, + imageView: ImageView, + callback: Callback? = null, + config: ApngDecoder.Config = ApngDecoder.Config() + ) = + coroutineScope.launch(Dispatchers.Default) { + try { + val drawable = decodeApngInto(context, file, imageView, config) + callback?.onSuccess(drawable) + } catch (e: Exception) { + callback?.onError(e) + } + } + + + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed for animation drawable and content resolver. + * @param uri Uri to load. + * @param imageView Image View. + * @param callback [ApngLoader.Callback] to handle success and error. + * @param config Decoder configuration + */ + @Suppress("unused") + @JvmOverloads + fun decodeApngAsyncInto( + context: Context, + uri: Uri, + imageView: ImageView, + callback: Callback? = null, + config: ApngDecoder.Config = ApngDecoder.Config() + ) = coroutineScope.launch(Dispatchers.Default) { + try { + val drawable = decodeApngInto(context, uri, imageView, config) + callback?.onSuccess(drawable) + } catch (e: Exception) { + callback?.onError(e) + } + } + + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed to decode the resource and for the animation drawable. + * @param res Raw resource to load. + * @param imageView Image View. + * @param callback [ApngLoader.Callback] to handle success and error. + * @param config Decoder configuration + */ + @Suppress("unused") + @JvmOverloads + fun decodeApngAsyncInto( + context: Context, @RawRes res: Int, + imageView: ImageView, + callback: Callback? = null, + config: ApngDecoder.Config = ApngDecoder.Config() + ) = coroutineScope.launch(Dispatchers.Default) { + try { + val drawable = decodeApngInto(context, res, imageView, config) + callback?.onSuccess(drawable) + } catch (e: Exception) { + callback?.onError(e) + } + } + + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed for the animation drawable. + * @param url URL to load. + * @param imageView Image View. + * @param callback [ApngLoader.Callback] to handle success and error. + * @param config Decoder configuration + */ + @Suppress("unused", "BlockingMethodInNonBlockingContext") + @JvmOverloads + fun decodeApngAsyncInto( + context: Context, + url: URL, + imageView: ImageView, + callback: Callback? = null, + config: ApngDecoder.Config = ApngDecoder.Config() + ) = coroutineScope.launch(Dispatchers.Default) { + try { + val drawable = decodeApngInto(context, url, imageView, config) + callback?.onSuccess(drawable) + } catch (e: Exception) { + callback?.onError(e) + } + } + + /** + * Load Apng into an imageView, asynchronously. + * @param context Context needed for decoding the image and creating the animation drawable. + * @param string URL to load + * @param imageView Image View. + * @param callback [ApngLoader.Callback] to handle success and error. + * @param config Decoder configuration + */ + @Suppress("unused") + @JvmOverloads + fun decodeApngAsyncInto( + context: Context, + string: String, + imageView: ImageView, + callback: Callback? = null, + config: ApngDecoder.Config = ApngDecoder.Config() + ) = + coroutineScope.launch(Dispatchers.Default) { + try { + val drawable = decodeApngInto(context, string, imageView, config) + callback?.onSuccess(drawable) + } catch (e: Exception) { + withContext(Dispatchers.Main) { + callback?.onError(e) + } + } + } + +// endregion with callback +} \ No newline at end of file diff --git a/app-test/src/main/java/oupson/apngcreator/activities/ViewerActivity.kt b/app-test/src/main/java/oupson/apngcreator/activities/ViewerActivity.kt index 36a9cc7..e7ba8d1 100644 --- a/app-test/src/main/java/oupson/apngcreator/activities/ViewerActivity.kt +++ b/app-test/src/main/java/oupson/apngcreator/activities/ViewerActivity.kt @@ -11,13 +11,17 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import kotlinx.android.synthetic.main.activity_viewer.* import oupson.apng.decoder.ApngDecoder +import oupson.apng.decoder.ApngLoader import oupson.apngcreator.R class ViewerActivity : AppCompatActivity() { + private var apngLoader: ApngLoader? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_viewer) + this.apngLoader = ApngLoader() + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN @@ -50,16 +54,24 @@ class ViewerActivity : AppCompatActivity() { private fun load() { val uri = intent.data ?: return - ApngDecoder.decodeApngAsyncInto(this, uri, viewerImageView, callback = object : ApngDecoder.Callback { - override fun onSuccess(drawable: Drawable) {} - override fun onError(error: Exception) { - Log.e("ViewerActivity", "Error when loading file", error) - } - }, ApngDecoder.Config(decodeCoverFrame = false)) + apngLoader?.decodeApngAsyncInto( + this, + uri, + viewerImageView, + callback = object : ApngLoader.Callback { + override fun onSuccess(drawable: Drawable) {} + override fun onError(error: Exception) { + Log.e("ViewerActivity", "Error when loading file", error) + } + }, + ApngDecoder.Config(decodeCoverFrame = false) + ) } - override fun onRequestPermissionsResult(requestCode: Int, - permissions: Array, grantResults: IntArray) { + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, grantResults: IntArray + ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { 2 -> { diff --git a/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt b/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt index c6543fc..4edb6e8 100644 --- a/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt +++ b/app-test/src/main/java/oupson/apngcreator/fragments/ApngDecoderFragment.kt @@ -11,6 +11,7 @@ import android.view.ViewGroup import android.widget.ImageView import androidx.fragment.app.Fragment import oupson.apng.decoder.ApngDecoder +import oupson.apng.decoder.ApngLoader import oupson.apng.drawable.ApngDrawable import oupson.apngcreator.BuildConfig import oupson.apngcreator.R @@ -19,29 +20,40 @@ import java.net.URL class ApngDecoderFragment : Fragment() { companion object { private const val TAG = "ApngDecoderFragment" + @JvmStatic fun newInstance() = ApngDecoderFragment() } + private var apngLoader: ApngLoader? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_apng_decoder, container, false) - val imageView : ImageView = view.findViewById(R.id.apngDecoderImageView) ?: return view + val imageView: ImageView = view.findViewById(R.id.apngDecoderImageView) ?: return view + + apngLoader = ApngLoader() if (context != null) { - ApngDecoder.decodeApngAsyncInto( + apngLoader?.decodeApngAsyncInto( this.requireContext(), URL("https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png"), imageView, - config = ApngDecoder.Config(bitmapConfig = Bitmap.Config.RGB_565, decodeCoverFrame = true), - callback = object : ApngDecoder.Callback { + config = ApngDecoder.Config( + bitmapConfig = Bitmap.Config.RGB_565, + decodeCoverFrame = true + ), + callback = object : ApngLoader.Callback { override fun onSuccess(drawable: Drawable) { if (BuildConfig.DEBUG) - Log.i(TAG, "onSuccess(), has cover frame : ${(drawable as? ApngDrawable)?.coverFrame != null}") + Log.i( + TAG, + "onSuccess(), has cover frame : ${(drawable as? ApngDrawable)?.coverFrame != null}" + ) } override fun onError(error: Exception) { diff --git a/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java b/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java index 74dd6c1..49db007 100644 --- a/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java +++ b/app-test/src/main/java/oupson/apngcreator/fragments/JavaFragment.java @@ -16,6 +16,7 @@ import androidx.fragment.app.Fragment; import org.jetbrains.annotations.NotNull; import oupson.apng.decoder.ApngDecoder; +import oupson.apng.decoder.ApngLoader; import oupson.apngcreator.BuildConfig; import oupson.apngcreator.R; @@ -23,6 +24,8 @@ import oupson.apngcreator.R; public class JavaFragment extends Fragment { private static final String TAG = "JavaActivity"; + private ApngLoader apngLoader = null; + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -36,10 +39,12 @@ public class JavaFragment extends Fragment { Context context = this.getContext(); + this.apngLoader = new ApngLoader(); + if (imageView != null && context != null) { if (BuildConfig.DEBUG) Log.v(TAG, "Loading " + imageUrl); - ApngDecoder.decodeApngAsyncInto(context, imageUrl, imageView, new ApngDecoder.Callback() { + this.apngLoader.decodeApngAsyncInto(context, imageUrl, imageView, new ApngLoader.Callback() { @Override public void onSuccess(@NotNull Drawable drawable) { if (BuildConfig.DEBUG) @@ -56,4 +61,11 @@ public class JavaFragment extends Fragment { return v; } + + @Override + public void onStop() { + super.onStop(); + + apngLoader.cancelAll(); + } } diff --git a/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt b/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt index 7c05231..a23f926 100644 --- a/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt +++ b/app-test/src/main/java/oupson/apngcreator/fragments/KotlinFragment.kt @@ -13,7 +13,7 @@ import android.widget.SeekBar import androidx.fragment.app.Fragment import com.squareup.picasso.Picasso import kotlinx.android.synthetic.main.activity_creator.* -import oupson.apng.decoder.ApngDecoder +import oupson.apng.decoder.ApngLoader import oupson.apng.drawable.ApngDrawable import oupson.apngcreator.BuildConfig import oupson.apngcreator.R @@ -22,22 +22,23 @@ import oupson.apngcreator.R class KotlinFragment : Fragment() { companion object { private const val TAG = "KotlinFragment" + @JvmStatic fun newInstance() = KotlinFragment() } - private var apngImageView : ImageView? = null - private var normalImageView : ImageView? = null + private var apngImageView: ImageView? = null + private var normalImageView: ImageView? = null - private var pauseButton : Button? = null - private var playButton : Button? = null + private var pauseButton: Button? = null + private var playButton: Button? = null - private var speedSeekBar : SeekBar? = null + private var speedSeekBar: SeekBar? = null //private var animator : ApngAnimator? = null - private var animation : ApngDrawable? = null - private var durations : IntArray? = null + private var animation: ApngDrawable? = null + private var durations: IntArray? = null private var frameIndex = 0 @@ -51,6 +52,8 @@ class KotlinFragment : Fragment() { ) private val selected = 4 + private var apngLoader: ApngLoader? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -60,6 +63,8 @@ class KotlinFragment : Fragment() { val view = inflater.inflate(R.layout.fragment_kotlin, container, false) + apngLoader = ApngLoader() + apngImageView = view.findViewById(R.id.ApngImageView) normalImageView = view.findViewById(R.id.NormalImageView) @@ -126,7 +131,10 @@ class KotlinFragment : Fragment() { res.coverFrame = animation.coverFrame for (i in 0 until animation.numberOfFrames) { - res.addFrame(animation.getFrame(i), (durations!![i].toFloat() / speed).toInt()) + res.addFrame( + animation.getFrame(i), + (durations!![i].toFloat() / speed).toInt() + ) } apngImageView?.setImageDrawable(res) @@ -138,11 +146,11 @@ class KotlinFragment : Fragment() { }) if (animation == null) { - ApngDecoder.decodeApngAsyncInto( + apngLoader?.decodeApngAsyncInto( requireContext(), imageUrls[selected], apngImageView!!, - callback = object : ApngDecoder.Callback { + callback = object : ApngLoader.Callback { override fun onSuccess(drawable: Drawable) { animation = (drawable as? ApngDrawable) durations = IntArray(animation?.numberOfFrames ?: 0) { i ->