Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package app.rive.runtime.kotlin.core

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import app.rive.runtime.kotlin.RiveAnimationView
import app.rive.runtime.kotlin.test.R
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith


@RunWith(AndroidJUnit4::class)
class RiveViewTest {

@Test
fun viewNoDefaults() {
UiThreadStatement.runOnUiThread {
val appContext = initTests()
val view = RiveAnimationView(appContext)

assertEquals(view.isPlaying, false)
}
}

@Test
fun viewDefaultsLoadResouce() {
UiThreadStatement.runOnUiThread {
val appContext = initTests()

val view = RiveAnimationView(appContext)
view.setRiveResource(R.raw.multipleartboards)

assertEquals(view.isPlaying, true)
assertEquals(view.file?.artboardNames, listOf("artboard2", "artboard1"))
assertEquals(
view.animations.map { it.animation.name }.toList(),
listOf("artboard2animation1", "artboard2animation2")
)
}
}

@Test
fun viewDefaultsChangeArtboard() {
UiThreadStatement.runOnUiThread {
val appContext = initTests()
val view = RiveAnimationView(appContext)
view.setRiveResource(R.raw.multipleartboards)
assertEquals(view.isPlaying, true)
view.artboardName = "artboard1"
assertEquals(
view.animations.map { it.animation.name }.toList(),
listOf("artboard1animation1")
)
}

}

@Test
fun viewDefaultsNoAutoplay() {
UiThreadStatement.runOnUiThread {
val appContext = initTests()

val view = RiveAnimationView(appContext)
view.autoplay = false
view.setRiveResource(R.raw.multipleartboards)

assertEquals(view.isPlaying, false)
view.artboardName = "artboard2"
assertEquals(
view.animations.map { it.animation.name }.toList(),
listOf<String>()
)
view.play(listOf("artboard2animation1", "artboard2animation2"))
assertEquals(
view.animations.map { it.animation.name }.toList(),
listOf("artboard2animation1", "artboard2animation2")
)
}
}

@Test
fun viewPause() {
UiThreadStatement.runOnUiThread {
val appContext = initTests()

val view = RiveAnimationView(appContext)
view.setRiveResource(R.raw.multipleartboards)
assertEquals(view.isPlaying, true)
assertEquals(view.animations.size, 2)
view.pause()
assertEquals(view.isPlaying, false)
}
}

@Test
fun viewPauseOneByOne() {
UiThreadStatement.runOnUiThread {
val appContext = initTests()

val view = RiveAnimationView(appContext)
view.setRiveResource(R.raw.multipleartboards)
assertEquals(view.isPlaying, true)
assertEquals(
view.animations.map { it.animation.name }.toList(),
listOf("artboard2animation1", "artboard2animation2")
)
view.pause("artboard2animation1")
assertEquals(view.isPlaying, true)
view.pause("artboard2animation2")
assertEquals(view.isPlaying, false)
}
}


}
86 changes: 52 additions & 34 deletions kotlin/src/main/java/app/rive/runtime/kotlin/RiveAnimationView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,8 @@ import android.view.View
import androidx.annotation.RawRes
import app.rive.runtime.kotlin.core.*

class RiveAnimationView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private var animationName: String? = null;
private var artboardName: String? = null;
private var autoplay: Boolean = true;
private var fit: Fit = Fit.CONTAIN
private var loop: Loop = Loop.LOOP
private var alignment: Alignment = Alignment.CENTER
private var drawable: RiveDrawable = RiveDrawable(fit, alignment, loop);
class RiveAnimationView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
private var drawable: RiveDrawable = RiveDrawable();
private var resourceId: Int? = null;

init {
Expand All @@ -24,20 +18,20 @@ class RiveAnimationView(context: Context, attrs: AttributeSet?) : View(context,
).apply {
try {
val alignmentIndex = getInteger(R.styleable.RiveAnimationView_riveAlignment, 4)
alignment = Alignment.values()[alignmentIndex]

val fitIndex = getInteger(R.styleable.RiveAnimationView_riveFit, 1)
fit = Fit.values()[fitIndex]

val loopIndex = getInteger(R.styleable.RiveAnimationView_riveLoop, 1)
loop = Loop.values()[loopIndex]

autoplay = getBoolean(R.styleable.RiveAnimationView_riveAutoPlay, true)
val autoplay = getBoolean(R.styleable.RiveAnimationView_riveAutoPlay, autoplay)
val artboardName = getString(R.styleable.RiveAnimationView_riveArtboard)
val animationName = getString(R.styleable.RiveAnimationView_riveAnimation)
val resourceId = getResourceId(R.styleable.RiveAnimationView_riveResource, -1)

artboardName = getString(R.styleable.RiveAnimationView_riveArtboard)
animationName = getString(R.styleable.RiveAnimationView_riveAnimation)
drawable.alignment = Alignment.values()[alignmentIndex]
drawable.fit = Fit.values()[fitIndex]
drawable.loop = Loop.values()[loopIndex]
drawable.autoplay = autoplay
drawable.artboardName = artboardName
drawable.animationName = animationName

val resourceId = getResourceId(R.styleable.RiveAnimationView_riveResource, -1)
if (resourceId != -1) {
setRiveResource(resourceId)
}
Expand Down Expand Up @@ -65,14 +59,16 @@ class RiveAnimationView(context: Context, attrs: AttributeSet?) : View(context,
) {
drawable.play(loop)
}

fun play(
animationNames: List<String>,
loop: Loop = Loop.NONE
) {
drawable.play(animationNames, loop)
}

fun play(
animationName: String? = null,
animationName: String,
loop: Loop = Loop.NONE
) {
drawable.play(animationName, loop)
Expand All @@ -98,26 +94,30 @@ class RiveAnimationView(context: Context, attrs: AttributeSet?) : View(context,
fun setRiveResource(@RawRes resId: Int) {
resourceId = resId
val file = File(resources.openRawResource(resId).readBytes())
setAnimationFile(file)
setRiveFile(file)

}

fun setAnimationFile(file: File) {
fun setRiveFile(file: File) {
drawable.run {
reset()
destroy()

}

// TODO: we maybe not be cleaning something up here,
// as we shouldnt have create a new drawable
drawable = RiveDrawable(
fit = fit,
alignment = alignment,
loop = loop,
artboardName = artboardName,
animationName = animationName,
autoplay = autoplay
).apply {
setAnimationFile(file)
background = this
}
fit = drawable.fit,
alignment = drawable.alignment,
loop = drawable.loop,
autoplay = drawable.autoplay,
animationName = drawable.animationName,
artboardName = drawable.artboardName,
)

drawable.setRiveFile(file)
background = drawable

requestLayout()
}

Expand All @@ -141,8 +141,8 @@ class RiveAnimationView(context: Context, attrs: AttributeSet?) : View(context,

// Lets work out how much space our artboard is going to actually use.
var usedBounds = Rive.calculateRequiredBounds(
fit,
alignment,
drawable.fit,
drawable.alignment,
AABB(providedWidth.toFloat(), providedHeight.toFloat()),
drawable.arboardBounds()
)
Expand Down Expand Up @@ -175,6 +175,24 @@ class RiveAnimationView(context: Context, attrs: AttributeSet?) : View(context,
setMeasuredDimension(width, height);
}

val file: File?
get() = drawable.file

var artboardName: String?
get() = drawable.artboardName
set(name) {
drawable.setArtboardByName(name)
}

var autoplay: Boolean
get() = drawable.autoplay
set(value) {
drawable.autoplay = value
}

val animations: List<LinearAnimationInstance>
get() = drawable.animations

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
pause()
Expand Down
70 changes: 51 additions & 19 deletions kotlin/src/main/java/app/rive/runtime/kotlin/RiveDrawable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ import android.graphics.drawable.Drawable
import app.rive.runtime.kotlin.core.*

class RiveDrawable(
private var fit: Fit = Fit.CONTAIN,
private var alignment: Alignment = Alignment.CENTER,
private var loop: Loop = Loop.NONE,
private var artboardName: String? = null,
private var animationName: String? = null,
private var autoplay: Boolean = true
var fit: Fit = Fit.CONTAIN,
var alignment: Alignment = Alignment.CENTER,
var loop: Loop = Loop.NONE,
var artboardName: String? = null,
var animationName: String? = null,
var autoplay: Boolean = true
) : Drawable(), Animatable {

private val renderer = Renderer()
private val animator = TimeAnimator()
private var animations = mutableListOf<LinearAnimationInstance>()
private var file: File? = null
var animations = mutableListOf<LinearAnimationInstance>()
var file: File? = null
private var artboard: Artboard? = null
private var targetBounds: AABB

Expand All @@ -31,7 +31,7 @@ class RiveDrawable(
targetBounds = AABB(bounds.width().toFloat(), bounds.height().toFloat())
animator.setTimeListener { _, _, delta ->

var continuePlaying = true;

artboard?.let { ab ->
val elapsed = delta.toFloat() / 1000

Expand All @@ -55,9 +55,10 @@ class RiveDrawable(
}
}
ab.advance(elapsed)

}
// TODO: set continuePlaying to false if all animations have come to an end.
if (!continuePlaying) {
if (playingAnimations.isEmpty()) {
animator.pause()
}
invalidateSelf()
Expand All @@ -68,20 +69,49 @@ class RiveDrawable(
}
}

fun setAnimationFile(file: File) {
fun setRiveFile(file: File) {
this.file = file
selectArtboard()
}

private fun selectArtboard() {
file?.let { file ->
artboardName?.let {
setArtboard(file.artboard(it))
} ?: run {
setArtboard(file.firstArtboard)
}
}
}

fun setArtboardByName(artboardName: String?) {
stop()
if (file == null) {
this.artboardName = artboardName
} else {
file?.let {
if (!it.artboardNames.contains(artboardName)) {
throw RiveException("Artboard $artboardName not found")
}
this.artboardName = artboardName
selectArtboard()
}
}

artboardName?.let {
setArtboard(file.artboard(it))
} ?: run {
setArtboard(file.firstArtboard)
this.file?.let {
setRiveFile(it)
}
}

fun setArtboard(artboard: Artboard) {
private fun setArtboard(artboard: Artboard) {
this.artboard = artboard
if (autoplay) {
play(animationName = animationName)
animationName?.let {
play(animationName = it)
} ?: run {
play()
}

} else {
artboard.advance(0f)
}
Expand Down Expand Up @@ -159,7 +189,7 @@ class RiveDrawable(
playingAnimations.clear()
animations.clear()
file?.let {
setAnimationFile(it)
setRiveFile(it)
}
invalidateSelf()
}
Expand Down Expand Up @@ -246,11 +276,13 @@ class RiveDrawable(
}

override fun stop() {
animations.clear()
playingAnimations.clear()
animator.cancel()
}

val isPlaying: Boolean
get() = animator.isRunning && !animator.isPaused
get() = playingAnimations.isNotEmpty()


override fun isRunning(): Boolean {
Expand Down