parent
6d1f6af7c5
commit
ef95ec2e1f
Binary file not shown.
|
@ -2,6 +2,7 @@ package oupson.apng
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Environment
|
||||||
import oupson.apng.ImageUtils.BitmapDiffCalculator
|
import oupson.apng.ImageUtils.BitmapDiffCalculator
|
||||||
import oupson.apng.ImageUtils.PngEncoder
|
import oupson.apng.ImageUtils.PngEncoder
|
||||||
import oupson.apng.ImageUtils.PnnQuantizer
|
import oupson.apng.ImageUtils.PnnQuantizer
|
||||||
|
@ -13,6 +14,7 @@ import oupson.apng.utils.Utils.Companion.getDispose_op
|
||||||
import oupson.apng.utils.Utils.Companion.pngSignature
|
import oupson.apng.utils.Utils.Companion.pngSignature
|
||||||
import oupson.apng.utils.Utils.Companion.to2Bytes
|
import oupson.apng.utils.Utils.Companion.to2Bytes
|
||||||
import oupson.apng.utils.Utils.Companion.to4Bytes
|
import oupson.apng.utils.Utils.Companion.to4Bytes
|
||||||
|
import java.io.File
|
||||||
import java.util.zip.CRC32
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -517,6 +519,9 @@ class Apng {
|
||||||
it.maxWidth = maxWidth
|
it.maxWidth = maxWidth
|
||||||
it.maxHeight = maxHeight
|
it.maxHeight = maxHeight
|
||||||
}
|
}
|
||||||
|
for (i in 0 until frames.size){
|
||||||
|
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frameCreated$i.png").writeBytes(frames[i].byteArray)
|
||||||
|
}
|
||||||
val drawedFrame = ApngAnimator(null).draw(frames)
|
val drawedFrame = ApngAnimator(null).draw(frames)
|
||||||
for (i in 1 until frames.size) {
|
for (i in 1 until frames.size) {
|
||||||
val diffCalculator = BitmapDiffCalculator(drawedFrame[i - 1], drawedFrame[i])
|
val diffCalculator = BitmapDiffCalculator(drawedFrame[i - 1], drawedFrame[i])
|
||||||
|
|
|
@ -79,7 +79,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
* @throws NotApngException
|
* @throws NotApngException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
fun load(file: File, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) {
|
fun load(file: File, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
|
||||||
doAsync {
|
doAsync {
|
||||||
val bytes = file.readBytes()
|
val bytes = file.readBytes()
|
||||||
if (isApng(bytes)) {
|
if (isApng(bytes)) {
|
||||||
|
@ -103,6 +103,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,7 +114,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
* @param speed The speed
|
* @param speed The speed
|
||||||
* @throws NotApngException
|
* @throws NotApngException
|
||||||
*/
|
*/
|
||||||
fun load(uri : Uri, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) {
|
fun load(uri : Uri, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
|
||||||
doAsync {
|
doAsync {
|
||||||
context?.contentResolver?.openInputStream(uri)?.readBytes()?.let {
|
context?.contentResolver?.openInputStream(uri)?.readBytes()?.let {
|
||||||
if (isApng(it)) {
|
if (isApng(it)) {
|
||||||
|
@ -138,6 +139,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,7 +149,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
* @throws NotApngException
|
* @throws NotApngException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
fun loadUrl(url: URL, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) {
|
fun loadUrl(url: URL, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
|
||||||
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
|
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
|
||||||
this@ApngAnimator.speed = speed
|
this@ApngAnimator.speed = speed
|
||||||
// Download PNG
|
// Download PNG
|
||||||
|
@ -176,6 +178,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,7 +188,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
* @throws NotApngException
|
* @throws NotApngException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
fun load(byteArray: ByteArray, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) {
|
fun load(byteArray: ByteArray, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
|
||||||
doAsync {
|
doAsync {
|
||||||
this@ApngAnimator.speed = speed
|
this@ApngAnimator.speed = speed
|
||||||
if (isApng(byteArray)) {
|
if (isApng(byteArray)) {
|
||||||
|
@ -209,6 +212,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -217,7 +221,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
* @param speed The speed
|
* @param speed The speed
|
||||||
* @throws NotApngException
|
* @throws NotApngException
|
||||||
*/
|
*/
|
||||||
fun load(string: String, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) {
|
fun load(string: String, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
|
||||||
doAsync {
|
doAsync {
|
||||||
this@ApngAnimator.speed = speed
|
this@ApngAnimator.speed = speed
|
||||||
if (string.contains("http") || string.contains("https")) {
|
if (string.contains("http") || string.contains("https")) {
|
||||||
|
@ -240,6 +244,7 @@ class ApngAnimator(private val context: Context?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package oupson.apng
|
package oupson.apng
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import oupson.apng.chunks.IDAT
|
import oupson.apng.chunks.IDAT
|
||||||
import oupson.apng.chunks.IHDR
|
import oupson.apng.chunks.IHDR
|
||||||
import oupson.apng.exceptions.NotPngException
|
import oupson.apng.exceptions.NotPngException
|
||||||
import oupson.apng.utils.Utils
|
import oupson.apng.utils.Utils
|
||||||
|
import oupson.apng.utils.Utils.Companion.IDAT
|
||||||
|
import oupson.apng.utils.Utils.Companion.IHDR
|
||||||
import oupson.apng.utils.Utils.Companion.isPng
|
import oupson.apng.utils.Utils.Companion.isPng
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A frame for an animated png
|
* A frame for an animated png
|
||||||
|
@ -18,12 +22,12 @@ class Frame {
|
||||||
|
|
||||||
var byteArray : ByteArray
|
var byteArray : ByteArray
|
||||||
|
|
||||||
var width : Int
|
var width : Int = -1
|
||||||
var height : Int
|
var height : Int = -1
|
||||||
|
|
||||||
var ihdr : IHDR
|
lateinit var ihdr : IHDR
|
||||||
|
|
||||||
var idat : IDAT
|
lateinit var idat : IDAT
|
||||||
|
|
||||||
val delay : Float
|
val delay : Float
|
||||||
|
|
||||||
|
@ -39,21 +43,17 @@ class Frame {
|
||||||
constructor(byteArray: ByteArray) {
|
constructor(byteArray: ByteArray) {
|
||||||
if (isPng(byteArray)) {
|
if (isPng(byteArray)) {
|
||||||
this.byteArray = byteArray
|
this.byteArray = byteArray
|
||||||
|
Log.e("tag", byteArray.size.toString())
|
||||||
// Get width and height for image
|
// Get width and height for image
|
||||||
ihdr = IHDR()
|
|
||||||
ihdr.parse(byteArray)
|
|
||||||
|
|
||||||
width = ihdr.pngWidth
|
|
||||||
height = ihdr.pngHeight
|
|
||||||
|
|
||||||
// Get IDAT Bytes
|
|
||||||
idat = IDAT()
|
|
||||||
idat.parse(byteArray)
|
|
||||||
|
|
||||||
delay = 1000f
|
delay = 1000f
|
||||||
|
|
||||||
blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
|
blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
|
||||||
dispose_op = Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE
|
dispose_op = Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE
|
||||||
|
var cursor = 8
|
||||||
|
while (cursor < byteArray.size) {
|
||||||
|
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||||
|
parseChunk(chunk)
|
||||||
|
cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw NotPngException()
|
throw NotPngException()
|
||||||
}
|
}
|
||||||
|
@ -62,15 +62,12 @@ class Frame {
|
||||||
if (isPng(byteArray)) {
|
if (isPng(byteArray)) {
|
||||||
this.byteArray = byteArray
|
this.byteArray = byteArray
|
||||||
// Get width and height for image
|
// Get width and height for image
|
||||||
ihdr = IHDR()
|
var cursor = 8
|
||||||
ihdr.parse(byteArray)
|
while (cursor < byteArray.size) {
|
||||||
|
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||||
width = ihdr.pngWidth
|
parseChunk(chunk)
|
||||||
height = ihdr.pngHeight
|
cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||||
|
}
|
||||||
// Get IDAT Bytes
|
|
||||||
idat = IDAT()
|
|
||||||
idat.parse(byteArray)
|
|
||||||
|
|
||||||
this.delay = delay
|
this.delay = delay
|
||||||
blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
|
blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE
|
||||||
|
@ -84,15 +81,12 @@ class Frame {
|
||||||
if (isPng(byteArray)) {
|
if (isPng(byteArray)) {
|
||||||
this.byteArray = byteArray
|
this.byteArray = byteArray
|
||||||
// Get width and height for image
|
// Get width and height for image
|
||||||
ihdr = IHDR()
|
var cursor = 8
|
||||||
ihdr.parse(byteArray)
|
while (cursor < byteArray.size) {
|
||||||
|
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||||
width = ihdr.pngWidth
|
parseChunk(chunk)
|
||||||
height = ihdr.pngHeight
|
cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||||
|
}
|
||||||
// Get IDAT Bytes
|
|
||||||
idat = IDAT()
|
|
||||||
idat.parse(byteArray)
|
|
||||||
|
|
||||||
this.delay = delay
|
this.delay = delay
|
||||||
|
|
||||||
|
@ -110,15 +104,12 @@ class Frame {
|
||||||
if (isPng(byteArray)) {
|
if (isPng(byteArray)) {
|
||||||
this.byteArray = byteArray
|
this.byteArray = byteArray
|
||||||
// Get width and height for image
|
// Get width and height for image
|
||||||
ihdr = IHDR()
|
var cursor = 8
|
||||||
ihdr.parse(byteArray)
|
while (cursor < byteArray.size) {
|
||||||
|
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||||
width = ihdr.pngWidth
|
parseChunk(chunk)
|
||||||
height = ihdr.pngHeight
|
cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||||
|
}
|
||||||
// Get IDAT Bytes
|
|
||||||
idat = IDAT()
|
|
||||||
idat.parse(byteArray)
|
|
||||||
|
|
||||||
this.delay = delay
|
this.delay = delay
|
||||||
|
|
||||||
|
@ -138,15 +129,12 @@ class Frame {
|
||||||
if (isPng(byteArray)) {
|
if (isPng(byteArray)) {
|
||||||
this.byteArray = byteArray
|
this.byteArray = byteArray
|
||||||
// Get width and height for image
|
// Get width and height for image
|
||||||
ihdr = IHDR()
|
var cursor = 8
|
||||||
ihdr.parse(byteArray)
|
while (cursor < byteArray.size) {
|
||||||
|
val chunk = byteArray.copyOfRange(cursor, cursor + Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12)
|
||||||
width = ihdr.pngWidth
|
parseChunk(chunk)
|
||||||
height = ihdr.pngHeight
|
cursor += Utils.parseLength(byteArray.copyOfRange(cursor, cursor + 4)) + 12
|
||||||
|
}
|
||||||
// Get IDAT Bytes
|
|
||||||
idat = IDAT()
|
|
||||||
idat.parse(byteArray)
|
|
||||||
|
|
||||||
this.delay = delay
|
this.delay = delay
|
||||||
|
|
||||||
|
@ -161,4 +149,20 @@ class Frame {
|
||||||
throw NotPngException()
|
throw NotPngException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parseChunk(byteArray: ByteArray) {
|
||||||
|
when(Arrays.toString(byteArray.copyOfRange(4, 8))) {
|
||||||
|
IHDR -> {
|
||||||
|
ihdr = IHDR()
|
||||||
|
ihdr.parse(byteArray)
|
||||||
|
width = ihdr.pngWidth
|
||||||
|
height = ihdr.pngHeight
|
||||||
|
}
|
||||||
|
IDAT -> {
|
||||||
|
// Get IDAT Bytes
|
||||||
|
idat = IDAT()
|
||||||
|
idat.parse(byteArray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -80,22 +80,22 @@ class PngEncoder {
|
||||||
* @param compressionLevel ! Don't use it : It's buggy
|
* @param compressionLevel ! Don't use it : It's buggy
|
||||||
*/
|
*/
|
||||||
fun encode(image: Bitmap, encodeAlpha: Boolean = false, filter: Int = 0, compressionLevel: Int = 0): ByteArray {
|
fun encode(image: Bitmap, encodeAlpha: Boolean = false, filter: Int = 0, compressionLevel: Int = 0): ByteArray {
|
||||||
Companion.filter = FILTER_NONE
|
this.filter = FILTER_NONE
|
||||||
if (filter <= FILTER_LAST) {
|
if (filter <= FILTER_LAST) {
|
||||||
Companion.filter = filter
|
this.filter = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compressionLevel in 0..9) {
|
if (compressionLevel in 0..9) {
|
||||||
Companion.compressionLevel = compressionLevel
|
this.compressionLevel = compressionLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
Companion.encodeAlpha = encodeAlpha
|
this.encodeAlpha = encodeAlpha
|
||||||
|
|
||||||
|
|
||||||
val pngIdBytes = byteArrayOf(-119, 80, 78, 71, 13, 10, 26, 10)
|
val pngIdBytes = byteArrayOf(-119, 80, 78, 71, 13, 10, 26, 10)
|
||||||
width = image.width
|
width = image.width
|
||||||
height = image.height
|
height = image.height
|
||||||
Companion.image = image
|
this.image = image
|
||||||
/*
|
/*
|
||||||
* start with an array that is big enough to hold all the pixels
|
* start with an array that is big enough to hold all the pixels
|
||||||
* (plus filter bytes), and an extra 200 bytes for header info
|
* (plus filter bytes), and an extra 200 bytes for header info
|
||||||
|
@ -221,9 +221,8 @@ class PngEncoder {
|
||||||
* Write a PNG "IHDR" chunk into the pngBytes array.
|
* Write a PNG "IHDR" chunk into the pngBytes array.
|
||||||
*/
|
*/
|
||||||
private fun writeHeader() {
|
private fun writeHeader() {
|
||||||
val startPos: Int = bytePos
|
|
||||||
|
|
||||||
bytePos = writeInt4(13, bytePos)
|
bytePos = writeInt4(13, bytePos)
|
||||||
|
val startPos: Int = bytePos
|
||||||
bytePos = writeBytes(IHDR, bytePos)
|
bytePos = writeBytes(IHDR, bytePos)
|
||||||
width = image!!.width
|
width = image!!.width
|
||||||
height = image!!.height
|
height = image!!.height
|
||||||
|
|
|
@ -4,11 +4,9 @@ import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
import android.util.Log
|
|
||||||
import kotlinx.android.synthetic.main.activity_creator.*
|
import kotlinx.android.synthetic.main.activity_creator.*
|
||||||
import org.jetbrains.anko.alert
|
import org.jetbrains.anko.alert
|
||||||
import org.jetbrains.anko.customView
|
import org.jetbrains.anko.customView
|
||||||
|
@ -17,7 +15,6 @@ import org.jetbrains.anko.sdk27.coroutines.onClick
|
||||||
import oupson.apng.APNGDisassembler
|
import oupson.apng.APNGDisassembler
|
||||||
import oupson.apng.Apng
|
import oupson.apng.Apng
|
||||||
import oupson.apng.ApngAnimator
|
import oupson.apng.ApngAnimator
|
||||||
import oupson.apng.ImageUtils.PngEncoder
|
|
||||||
import oupson.apngcreator.adapter.frameListViewAdapter
|
import oupson.apngcreator.adapter.frameListViewAdapter
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@ -45,33 +42,30 @@ class CreatorActivity : AppCompatActivity() {
|
||||||
fab_create.onClick {
|
fab_create.onClick {
|
||||||
var apngCreated = Apng()
|
var apngCreated = Apng()
|
||||||
|
|
||||||
items.forEach {
|
items.forEach { bitmap ->
|
||||||
apngCreated.addFrames(it)
|
apngCreated.addFrames(bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apngCreated = APNGDisassembler.disassemble(apngCreated.toByteArray()).apply {
|
||||||
Log.e("tag", apngCreated.frames.size.toString())
|
optimiseFrame()
|
||||||
apngCreated = APNGDisassembler.disassemble(apngCreated.toByteArray())
|
}
|
||||||
apngCreated.optimiseFrame()
|
|
||||||
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "vtm").writeBytes(apngCreated.toByteArray())
|
|
||||||
val a = ApngAnimator(applicationContext)
|
val a = ApngAnimator(applicationContext)
|
||||||
|
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "apn.png").writeBytes(apngCreated.toByteArray())
|
||||||
|
|
||||||
a.load(apngCreated.toByteArray())
|
a.load(apngCreated.toByteArray())
|
||||||
a.onLoaded {
|
a.onLoaded { anim ->
|
||||||
alert {
|
alert {
|
||||||
customView {
|
customView {
|
||||||
imageView {
|
imageView {
|
||||||
Log.e("tag", "${it.anim?.numberOfFrames.toString()} : ${items.size}")
|
/**anim.anim?.let {cu ->
|
||||||
it.anim?.let {
|
for (i in 0 until cu.numberOfFrames) {
|
||||||
for (i in 0 until it.numberOfFrames) {
|
val vt = Bitmap.createBitmap(cu.getFrame(i).intrinsicWidth, cu.getFrame(i).intrinsicHeight, Bitmap.Config.ARGB_8888)
|
||||||
val vt = Bitmap.createBitmap(it.getFrame(i).intrinsicWidth, it.getFrame(i).intrinsicHeight, Bitmap.Config.ARGB_8888)
|
|
||||||
val canvas = Canvas(vt)
|
val canvas = Canvas(vt)
|
||||||
it.getFrame(i).draw(canvas)
|
cu.getFrame(i).draw(canvas)
|
||||||
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame$i.png").writeBytes(PngEncoder.encode(vt))
|
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frameCreated$i.png").writeBytes(PngEncoder.encode(vt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setImageDrawable(it.anim)
|
this.setImageDrawable(anim.anim)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.show()
|
}.show()
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:srcCompat="@drawable/ic_play_arrow_white_24dp" />
|
app:srcCompat="@drawable/ic_add_white_24dp" />
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/dragView"
|
android:id="@+id/dragView"
|
||||||
|
|
Loading…
Reference in New Issue