Code Inspection

Change App Style
This commit is contained in:
oupson 2019-06-13 10:35:27 +02:00
parent 73ee647e89
commit de4073e6b8
18 changed files with 114 additions and 110 deletions

View File

@ -1,13 +1,13 @@
# Kapng-Android
An android library to create or display apng
Exemple of apng :
Example of apng :
![apng-example](https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png)
How to use this library :
To load animated png to an imageview :
To load animated png to an imageView :
```kotlin
val imageUrl = "https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png" // image url could be an url, or a file path. You could also load byteArray and file

View File

@ -1,4 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="oupson.apng">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

View File

@ -41,9 +41,10 @@ class APNGDisassembler {
if (isApng(byteArray)) {
var cursor = 8
while (cursor < byteArray.size) {
val chunk = byteArray.copyOfRange(cursor, cursor + parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
val length = parseLength(byteArray.copyOfRange(cursor, cursor + 4))
val chunk = byteArray.copyOfRange(cursor, cursor + length + 12)
parseChunk(chunk)
cursor += parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
cursor += length + 12
}
return apng
} else {

View File

@ -164,8 +164,7 @@ class ApngAnimator(private val context: Context?) {
* @return [ApngAnimator] The Animator
* @throws NotApngException
*/
@Suppress("unused")
@SuppressWarnings("WeakerAccess")
@JvmOverloads
fun load(file: File, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
GlobalScope.launch {
val bytes = file.readBytes()
@ -200,6 +199,7 @@ class ApngAnimator(private val context: Context?) {
* @return [ApngAnimator] The Animator
* @throws NotApngException
*/
@JvmOverloads
fun load(uri : Uri, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
GlobalScope.launch {
context?.contentResolver?.openInputStream(uri)?.readBytes()?.also {
@ -235,7 +235,7 @@ class ApngAnimator(private val context: Context?) {
* @return [ApngAnimator] The Animator
* @throws NotApngException
*/
@SuppressWarnings("WeakerAccess")
@JvmOverloads
fun loadUrl(url: URL, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
GlobalScope.launch {
this@ApngAnimator.speed = speed
@ -275,7 +275,7 @@ class ApngAnimator(private val context: Context?) {
* @return [ApngAnimator] The Animator
* @throws NotApngException
*/
@SuppressWarnings("WeakerAccess")
@JvmOverloads
fun load(byteArray: ByteArray, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
GlobalScope.launch {
this@ApngAnimator.speed = speed
@ -310,6 +310,7 @@ class ApngAnimator(private val context: Context?) {
* @return [ApngAnimator] The Animator
* @throws NotApngException
*/
@JvmOverloads
fun load(string: String, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
GlobalScope.launch {
this@ApngAnimator.speed = speed
@ -357,6 +358,7 @@ class ApngAnimator(private val context: Context?) {
* @return [ApngAnimator] The Animator
* @throws NotApngException
*/
@JvmOverloads
fun load(@RawRes res : Int, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
GlobalScope.launch {
val byteArray = context?.resources?.openRawResource(res)?.readBytes() ?: byteArrayOf()
@ -462,25 +464,25 @@ class ApngAnimator(private val context: Context?) {
val animResume = CustomAnimationDrawable()
activeAnimation?.stop()
val currentFrame = activeAnimation!!.current
val dura = ArrayList<Float>()
val durations = ArrayList<Float>()
frameLoop@ for (i in 0 until anim?.numberOfFrames!!) {
val checkFrame = activeAnimation!!.getFrame(i)
if (checkFrame === currentFrame) {
for (k in i until activeAnimation!!.numberOfFrames) {
val frame = activeAnimation!!.getFrame(k)
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
dura.add(duration!![k])
durations.add(duration!![k])
}
for (k in 0 until i) {
val frame = activeAnimation!!.getFrame(k)
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
dura.add(duration!![k])
durations.add(duration!![k])
}
activeAnimation = animResume
imageView?.setImageDrawable(activeAnimation)
activeAnimation?.setOnFrameChangeListener(frameChangeLister)
imageView?.invalidate()
duration = dura
duration = durations
break@frameLoop
}
}

View File

@ -1,3 +1,5 @@
@file:Suppress("unused")
package oupson.apng.exceptions
class NoFrameException : Exception()

View File

@ -11,6 +11,7 @@ class BitmapDiffCalculator(firstBitmap: Bitmap, secondBitmap : Bitmap) {
val res : Bitmap
var xOffset : Int = 0
var yOffset : Int = 0
@Suppress("unused")
var blendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_OVER
init {
val difBitmap = Bitmap.createBitmap(firstBitmap.width, firstBitmap.height, Bitmap.Config.ARGB_8888)

View File

@ -9,41 +9,45 @@ Copyright (c) 2018 Miller Cy Chan
https://github.com/mcychan/nQuant.android/blob/master/nQuant.master/src/main/java/com/android/nQuant/PnnQuantizer.java
*/
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
@SuppressWarnings("WeakerAccess")
public class PnnQuantizer {
private final short SHORT_MAX = Short.MAX_VALUE;
private final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
private boolean hasTransparency = false, hasSemiTransparency = false;
protected int width, height;
protected int pixels[] = null;
protected int[] pixels = null;
private Integer m_transparentColor;
private HashMap<Integer, short[]> closestMap = new HashMap();
@SuppressWarnings("unchecked")
@SuppressLint("UseSparseArrays")
private final HashMap<Integer, short[]> closestMap = new HashMap();
public PnnQuantizer(String fname) throws IOException {
@SuppressWarnings("unused")
public PnnQuantizer(String fname) {
fromBitmap(fname);
}
public PnnQuantizer(Bitmap bitmap) throws IOException{
public PnnQuantizer(Bitmap bitmap) {
fromBitmap(bitmap);
}
private void fromBitmap(Bitmap bitmap) throws IOException {
private void fromBitmap(Bitmap bitmap) {
width = bitmap.getWidth();
height = bitmap.getHeight();
pixels = new int [width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
}
private void fromBitmap(String fname) throws IOException {
private void fromBitmap(String fname) {
Bitmap bitmap = BitmapFactory.decodeFile(fname);
fromBitmap(bitmap);
}
@ -204,6 +208,7 @@ public class PnnQuantizer {
List<Integer> palette = new ArrayList<>();
short k = 0;
for (int i = 0;; ++k) {
assert bins[i] != null;
int alpha = (int) Math.rint(bins[i].ac);
palette.add(Color.argb(alpha, (int) Math.rint(bins[i].rc), (int) Math.rint(bins[i].gc), (int) Math.rint(bins[i].bc)));
if (hasTransparency && palette.get(k).equals(m_transparentColor)) {
@ -292,6 +297,7 @@ public class PnnQuantizer {
return k;
}
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "UnusedAssignment", "SameReturnValue"})
boolean quantize_image(final int[] pixels, final Integer[] palette, int[] qPixels, final boolean dither)
{
int nMaxColors = palette.length;
@ -301,6 +307,7 @@ public class PnnQuantizer {
sqr_tbl[i + BYTE_MAX] = i * i;
int[] squares3 = new int[sqr_tbl.length - BYTE_MAX];
//noinspection ManualArrayCopy
for (int i = 0; i < squares3.length; i++)
squares3[i] = sqr_tbl[i + BYTE_MAX];
@ -412,6 +419,7 @@ public class PnnQuantizer {
return true;
}
@SuppressWarnings("UnusedAssignment")
private Bitmap quantize_image(final int[] pixels, int[] qPixels)
{
int pixelIndex = 0;
@ -519,6 +527,7 @@ public class PnnQuantizer {
return Bitmap.createBitmap(qPixels, width, height, Bitmap.Config.RGB_565);
}
@SuppressWarnings("UnusedReturnValue")
public Bitmap convert(int nMaxColors, boolean dither) {
final int[] cPixels = new int[pixels.length];
for (int i =0; i<cPixels.length; ++i) {

View File

@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:fullBackupContent="false"
android:allowBackup="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher"

View File

@ -8,11 +8,11 @@ import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Bundle
import android.os.Environment
import android.text.Html
import android.view.View
import android.widget.CheckBox
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.jetbrains.anko.*
import org.jetbrains.anko.design.appBarLayout
@ -27,10 +27,10 @@ class CreatorActivity : AppCompatActivity() {
companion object {
private const val PICK_IMAGE = 999
}
var items : ArrayList<Bitmap> = ArrayList()
var bitmapAdapter : AnkoAdapter<Bitmap>? = null
private var items : ArrayList<Bitmap> = ArrayList()
private var bitmapAdapter : AnkoAdapter<Bitmap>? = null
var view = CreatorActivityLayout()
private var view = CreatorActivityLayout()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -66,16 +66,14 @@ class CreatorActivity : AppCompatActivity() {
a.onLoaded { anim ->
alert {
customView {
ctx.setTheme(R.style.AppTheme_DarkDialog)
imageView {
this.setImageDrawable(anim.anim)
}
}
}.show()
}
}
bitmapAdapter = AnkoAdapter({items}) {index, items, view ->
bitmapAdapter = AnkoAdapter({items}) { index, items, _ ->
with(items[index]) {
verticalLayout {
lparams {
@ -93,15 +91,18 @@ class CreatorActivity : AppCompatActivity() {
}
/* frameListViewAdapter(this, items) */
view.listView.adapter = bitmapAdapter
setSupportActionBar(view.toolbar)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) {
PICK_IMAGE -> {
if (resultCode == Activity.RESULT_OK) {
contentResolver.openInputStream(data?.data).readBytes().apply {
items.add(BitmapFactory.decodeByteArray(this, 0, this.size))
bitmapAdapter?.notifyDataSetChanged()
if (data?.data != null) {
contentResolver.openInputStream(data.data!!)?.readBytes()?.apply {
items.add(BitmapFactory.decodeByteArray(this, 0, this.size))
bitmapAdapter?.notifyDataSetChanged()
}
}
}
}
@ -114,18 +115,16 @@ class CreatorActivityLayout : AnkoComponent<CreatorActivity> {
lateinit var addFrameButton : FloatingActionButton
lateinit var createButton : FloatingActionButton
lateinit var optimiseCheckBox : CheckBox
lateinit var toolbar : Toolbar
override fun createView(ui: AnkoContext<CreatorActivity>) = with(ui) {
relativeLayout {
backgroundColor = Color.BLACK
backgroundColor = Color.WHITE
val bar = verticalLayout {
id = View.generateViewId()
backgroundColor = Color.DKGRAY
backgroundColor = Color.WHITE
appBarLayout {
backgroundColor = Color.BLACK
toolbar {
toolbar = xToolbar {
id = View.generateViewId()
title = Html.fromHtml("<font color='#ffffff'>Create APNG</font>")
}.lparams {
width = matchParent
height = wrapContent
@ -141,7 +140,6 @@ class CreatorActivityLayout : AnkoComponent<CreatorActivity> {
}
optimiseCheckBox = checkBox("Optimise APNG, WIP !") {
id = View.generateViewId()
this.textColor = Color.WHITE
}.lparams {
width = matchParent
below(bar)
@ -155,7 +153,8 @@ class CreatorActivityLayout : AnkoComponent<CreatorActivity> {
}
addFrameButton = floatingActionButton {
imageResource = R.drawable.ic_add_black_24dp
backgroundTintList = ColorStateList.valueOf(Color.WHITE)
imageTintList = ColorStateList.valueOf(Color.WHITE)
backgroundTintList = ColorStateList.valueOf(Color.BLACK)
isClickable = true
}.lparams {
width = wrapContent
@ -166,7 +165,8 @@ class CreatorActivityLayout : AnkoComponent<CreatorActivity> {
}
createButton = floatingActionButton {
imageResource = R.drawable.ic_play_arrow_black_24dp
backgroundTintList = ColorStateList.valueOf(Color.WHITE)
imageTintList = ColorStateList.valueOf(Color.WHITE)
backgroundTintList = ColorStateList.valueOf(Color.BLACK)
isClickable = true
}.lparams {
width = wrapContent

View File

@ -8,7 +8,7 @@ import kotlin.Unit;
import oupson.apng.ApngAnimator;
import oupson.apng.ApngAnimatorKt;
public class JavaActivity extends AppCompatActivity {
static String TAG = "JavaActivity";
private static final String TAG = "JavaActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -1,11 +1,11 @@
package oupson.apngcreator
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.text.Html
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toolbar
import android.view.ViewManager
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import com.squareup.picasso.Picasso
@ -14,16 +14,17 @@ import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder
import org.jetbrains.anko.constraint.layout.applyConstraintSet
import org.jetbrains.anko.constraint.layout.constraintLayout
import org.jetbrains.anko.constraint.layout.matchConstraint
import org.jetbrains.anko.custom.ankoView
import org.jetbrains.anko.design.appBarLayout
import org.jetbrains.anko.sdk27.coroutines.onClick
import org.jetbrains.anko.sdk27.coroutines.onMenuItemClick
import org.jetbrains.anko.sdk27.coroutines.onSeekBarChangeListener
import oupson.apng.ApngAnimator
import oupson.apng.loadApng
fun ViewManager.xToolbar(init : androidx.appcompat.widget.Toolbar.() -> Unit) = ankoView({androidx.appcompat.widget.Toolbar(it)}, 0, init)
class MainActivity : AppCompatActivity() {
private lateinit var animator: ApngAnimator
private lateinit var tool : Toolbar
private lateinit var tool : androidx.appcompat.widget.Toolbar
// val imageUrl = "http://oupson.oupsman.fr/apng/bigApng.png"
private val imageUrl = "https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png"
// val imageUrl = "http://orig06.deviantart.net/7812/f/2012/233/7/5/twilight_rapidash_shaded_and_animated_by_tamalesyatole-d5bz7hd.png"
@ -31,32 +32,11 @@ class MainActivity : AppCompatActivity() {
// val imageUrl = "file:///android_asset/image.png"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val buttonDrawable = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = dip(5).toFloat()
setStroke(2, Color.WHITE)
}
verticalLayout {
backgroundColor = Color.BLACK
verticalLayout {
backgroundColor = Color.DKGRAY
appBarLayout {
backgroundColor = Color.BLACK
tool = toolbar {
tool = xToolbar {
id = View.generateViewId()
title = Html.fromHtml("<font color='#ffffff'>MainActivity</font>", Html.FROM_HTML_MODE_LEGACY)
inflateMenu(R.menu.main_menu)
onMenuItemClick { item ->
when (item!!.itemId) {
R.id.action_open_create_activity -> {
startActivity<CreatorActivity>()
finish()
}
R.id.action_open_java_activity -> {
startActivity<JavaActivity>()
}
}
}
}.lparams {
width = matchParent
height = wrapContent
@ -72,9 +52,10 @@ class MainActivity : AppCompatActivity() {
}
constraintLayout {
backgroundColor = Color.WHITE
val pauseButton = button("pause") {
backgroundColor = Color.WHITE
id = View.generateViewId()
background = buttonDrawable
onClick {
animator.pause()
}
@ -83,7 +64,7 @@ class MainActivity : AppCompatActivity() {
height = wrapContent
)
val playButton = button("play") {
background = buttonDrawable
backgroundColor = Color.WHITE
id = View.generateViewId()
onClick {
animator.play()
@ -92,15 +73,15 @@ class MainActivity : AppCompatActivity() {
width = wrapContent,
height = wrapContent
)
val seekBar = themedSeekBar(R.style.AppTheme_SeekBar){
val seekBar = seekBar {
id = View.generateViewId()
max = 200
progress = 10
progress = 100
onSeekBarChangeListener {
onProgressChanged { _, _, _ -> }
onStartTrackingTouch { }
onStopTrackingTouch { seekBar ->
animator.speed = (seekBar?.progress?.toFloat() ?: 100f / 100f)
animator.speed = (seekBar?.progress?.toFloat() ?: 100f) / 100f
}
}
}.lparams(
@ -168,7 +149,21 @@ class MainActivity : AppCompatActivity() {
width = matchParent
height = matchParent
}
}
setSupportActionBar(tool)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.action_open_create_activity -> startActivity<CreatorActivity>()
R.id.action_open_java_activity -> startActivity<JavaActivity>()
}
return super.onOptionsItemSelected(item)
}
}

View File

@ -5,8 +5,10 @@ import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
class AnkoAdapter<T>(itemFactory: () -> List<T>, viewFactory: Context.(index: Int, items: List<T>, view: View?) -> View): BaseAdapter() {
val viewFactory = viewFactory
class AnkoAdapter<T>(itemFactory: () -> List<T>,
val viewFactory: Context.(index: Int, items: List<T>, view: View?) -> View
): BaseAdapter() {
@Suppress("MemberVisibilityCanBePrivate")
val items: List<T> by lazy { itemFactory() }
override fun getView(index: Int, view: View?, viewGroup: ViewGroup?): View {
@ -18,11 +20,10 @@ class AnkoAdapter<T>(itemFactory: () -> List<T>, viewFactory: Context.(index: In
}
override fun getItem(index: Int): T {
return items.get(index)
return items[index]
}
override fun getItemId(index: Int): Long {
return (items.get(index) as Any).hashCode().toLong() + (index.toLong() * Int.MAX_VALUE)
return (items[index] as Any).hashCode().toLong() + (index.toLong() * Int.MAX_VALUE)
}
}

View File

@ -8,6 +8,7 @@
tools:context=".JavaActivity">
<ImageView
android:contentDescription="@string/apng_imageView"
android:layout_width="0dp"
android:layout_height="0dp" app:srcCompat="@mipmap/ic_launcher" android:id="@+id/javaImageView"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -2,9 +2,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_open_create_activity"
android:title="Create apng"
android:title="@string/create_apng"
app:showAsAction="never" />
<item android:id="@+id/action_open_java_activity"
android:title="Java Activity"
android:title="@string/java_activity"
app:showAsAction="never" />
</menu>

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="colorPrimary">#ffffff</color>
<color name="colorPrimaryDark">#ffffff</color>
<color name="colorAccent">#b00020</color>
<color name="gray">#999999</color>
</resources>

View File

@ -1,3 +1,7 @@
<resources>
<string name="app_name">APNGCreator</string>
<string name="create_apng">Create APNG</string>
<string name="java_activity">Java Activity</string>
<string name="apng_imageView">An ImageView with an APNG</string>
</resources>

View File

@ -1,33 +1,18 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#000</item>
<item name="colorPrimaryDark">#000</item>
<item name="colorAccent">#fff</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:textColorPrimary">#ffffff</item>
<item name="actionMenuTextColor">#fff</item>
<item name="android:itemBackground">#000</item>
<item name="android:textColorSecondary">#fff</item>
<item name="android:colorEdgeEffect">#fff</item>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">#000</item>
<item name="android:textColorSecondary">#000</item>
<item name="android:windowLightStatusBar">true</item>
<item name="toolbarStyle">@style/AppTheme.ActionBar</item>
<item name="colorControlNormal">@color/gray</item>
</style>
<style name="AppTheme.SeekBar" parent="Widget.AppCompat.SeekBar">
<item name="android:progressBackgroundTint">#999999</item>
<item name="android:progressTint">#fff</item>
<item name="android:colorControlActivated">#fff</item>
<style name="AppTheme.ActionBar" parent="Widget.AppCompat.Toolbar">
<item name="titleTextColor">@color/gray</item>
<item name="subtitleTextColor">@color/gray</item>
</style>
<style name="AppTheme.DarkDialog" parent="Theme.AppCompat.Dialog.Alert">
<!-- Used for the buttons -->
<item name="colorAccent">#fff</item>
<!-- Used for the title and text -->
<item name="android:textColorPrimary">#FFFFFF</item>
<!-- Used for the background -->
<item name="android:background">#000</item>
</style>
</resources>