@@ -3,16 +3,12 @@ package com.algolia.instantsearch.voice.ui
33import android.animation.AnimatorSet
44import android.content.Context
55import android.graphics.Canvas
6- import android.os.Looper
76import android.util.AttributeSet
87import android.view.View
9- import kotlinx.coroutines.experimental.*
10- import kotlinx.coroutines.experimental.android.Main
11- import kotlinx.coroutines.experimental.android.UI
12- import kotlinx.coroutines.experimental.android.awaitFrame
138
149/* * A View displaying a ripple effect. */
1510class RippleView : View {
11+
1612 enum class State {
1713 None ,
1814 Playing ,
@@ -34,50 +30,56 @@ class RippleView : View {
3430 /* * Current animations, filled when [State.Playing] triggers [animation] and emptied when [State.Stopped]. */
3531 private val animations = mutableMapOf<Int , AnimatorSet >()
3632
33+ /* * This runnable invalidate the view at 60 fps. */
34+ private val runnableFps = object : java.lang.Runnable {
35+
36+ override fun run () {
37+ invalidate()
38+ postDelayed(this , 1000 / 60 )
39+ }
40+ }
41+
42+ /* * This runnable generates an animation at each [interval][circleCount]. */
43+ private val runnableAnimation = object : Runnable {
44+
45+ private var index = 0
46+
47+ override fun run () {
48+ animations[index] = circles[index].circleAnimation().also { it.start() }
49+ index = if (index == circles.lastIndex) 0 else index + 1
50+ postDelayed(this , delay)
51+ }
52+ }
53+
3754 private var delay: Long = 0L
3855 private var duration: Long = 0L
3956 private var circleCount: Long = 0L
4057 private var radius: Float = 0f
4158 private var size: Int = 0
4259
43- private var jobAnimation: Job ? = null
44- private var jobFps : Job ? = null
45-
4660 private var state = State .None
4761 set(value) {
4862 field = value
4963 when (value) {
5064 State .Playing -> {
5165 animations.values.forEach { it.end() }
5266 animations.clear()
53- jobAnimation = animation()
54- }
55- State .Stopped -> try {
56- jobAnimation?.cancel()
57- jobAnimation = null
58- } catch (e: UninitializedPropertyAccessException ) {
59- // cancel() was called before start()
67+ post(runnableAnimation)
6068 }
69+ State .Stopped -> removeCallbacks(runnableAnimation)
6170 State .None -> Unit
6271 }
6372 }
6473
6574 override fun onAttachedToWindow () {
6675 super .onAttachedToWindow()
67- jobFps = GlobalScope .launch(Dispatchers .Main ) {
68- while (isActive) {
69- awaitFrame()
70- invalidate()
71- }
72- }
76+ post(runnableFps)
7377 }
7478
7579 override fun onDetachedFromWindow () {
7680 super .onDetachedFromWindow()
77- jobFps?.cancel()
78- jobFps = null
79- jobAnimation?.cancel()
80- jobAnimation = null
81+ removeCallbacks(runnableAnimation)
82+ removeCallbacks(runnableFps)
8183 }
8284
8385 private fun init (attrs : AttributeSet ) {
@@ -93,16 +95,6 @@ class RippleView : View {
9395 }.recycle()
9496 }
9597
96- /* * This coroutine generates an animation at each [interval][circleCount]. */
97- private fun animation (): Job = GlobalScope .launch(Dispatchers .Main ) {
98- var index = 0
99- while (isActive) {
100- animations[index] = circles[index].circleAnimation().also { it.start() }
101- index = if (index == circles.lastIndex) 0 else index + 1
102- delay(delay)
103- }
104- }
105-
10698 private fun DrawableSprite.circleAnimation (): AnimatorSet =
10799 AnimatorSet ().also {
108100 it.duration = duration
0 commit comments