Skip to content

Commit 63f71aa

Browse files
committed
feat: implement native auto play for android
1 parent 902d704 commit 63f71aa

File tree

6 files changed

+128
-81
lines changed

6 files changed

+128
-81
lines changed

packages/core/android/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def kotlin_version = getExtOrDefault('kotlinVersion', project.properties['lottie
112112

113113
dependencies {
114114
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
115+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
115116

116117
//noinspection GradleDynamicVersion
117118
implementation 'com.facebook.react:react-native:+' // From node_modules

packages/core/android/src/main/java/com/airbnb/android/react/lottie/LottieAnimationViewManagerImpl.kt

+25-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.airbnb.android.react.lottie
22

33
import android.os.Handler
44
import android.os.Looper
5+
import android.util.Log
56
import android.view.View
67
import android.view.View.OnAttachStateChangeListener
78
import android.widget.ImageView
@@ -11,13 +12,16 @@ import com.airbnb.lottie.RenderMode
1112
import com.facebook.react.bridge.ReadableArray
1213
import com.facebook.react.common.MapBuilder
1314
import com.facebook.react.uimanager.ThemedReactContext
15+
import com.facebook.react.util.RNLog
16+
import kotlinx.coroutines.*
1417
import java.io.BufferedReader
1518
import java.io.InputStreamReader
1619
import java.net.URL
1720
import kotlin.concurrent.thread
1821

1922
internal object LottieAnimationViewManagerImpl {
2023
const val REACT_CLASS = "LottieAnimationView"
24+
val coroutineScope = CoroutineScope(Dispatchers.Main)
2125

2226
@JvmStatic
2327
val exportedViewConstants: Map<String, Any>
@@ -135,12 +139,19 @@ internal object LottieAnimationViewManagerImpl {
135139
urlString: String?,
136140
propManagersMap: LottieAnimationViewPropertyManager
137141
) {
138-
thread {
139-
BufferedReader(InputStreamReader(URL(urlString).openStream())).useLines {
140-
Handler(Looper.getMainLooper()).post {
141-
propManagersMap.animationJson = it.toString()
142-
propManagersMap.commitChanges()
142+
CoroutineScope(Dispatchers.Main).launch {
143+
try {
144+
val jsonString = withContext(Dispatchers.IO) {
145+
URL(urlString).openStream().use {
146+
BufferedReader(InputStreamReader(it)).useLines { lines ->
147+
lines.joinToString("\n")
148+
}
149+
}
143150
}
151+
propManagersMap.animationJson = jsonString
152+
propManagersMap.commitChanges()
153+
} catch (e: Exception) {
154+
RNLog.l("Error while loading animation from URL")
144155
}
145156
}
146157
}
@@ -158,7 +169,7 @@ internal object LottieAnimationViewManagerImpl {
158169
var mode: ImageView.ScaleType? = null
159170
when (resizeMode) {
160171
"cover" -> {
161-
mode = ImageView.ScaleType.CENTER_CROP
172+
mode = ImageView.ScaleType.FIT_XY
162173
}
163174
"contain" -> {
164175
mode = ImageView.ScaleType.CENTER_INSIDE
@@ -226,6 +237,14 @@ internal object LottieAnimationViewManagerImpl {
226237
viewManager.loop = loop
227238
}
228239

240+
@JvmStatic
241+
fun setAutoPlay(
242+
autoPlay: Boolean,
243+
viewManager: LottieAnimationViewPropertyManager
244+
) {
245+
viewManager.autoPlay = autoPlay
246+
}
247+
229248
@JvmStatic
230249
fun setEnableMergePaths(
231250
enableMergePaths: Boolean,

packages/core/android/src/main/java/com/airbnb/android/react/lottie/LottieAnimationViewPropertyManager.kt

+35-29
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
package com.airbnb.android.react.lottie
22

3-
import com.airbnb.lottie.LottieAnimationView
4-
import java.lang.ref.WeakReference
3+
import android.graphics.ColorFilter
54
import android.widget.ImageView
6-
import com.facebook.react.bridge.ReadableArray
7-
import com.airbnb.lottie.RenderMode
8-
import com.airbnb.lottie.TextDelegate
5+
import com.airbnb.lottie.LottieAnimationView
96
import com.airbnb.lottie.LottieDrawable
10-
import com.facebook.react.bridge.ReadableType
11-
import com.facebook.react.bridge.ColorPropConverter
12-
import android.graphics.ColorFilter
7+
import com.airbnb.lottie.LottieProperty
8+
import com.airbnb.lottie.RenderMode
139
import com.airbnb.lottie.SimpleColorFilter
10+
import com.airbnb.lottie.TextDelegate
1411
import com.airbnb.lottie.model.KeyPath
1512
import com.airbnb.lottie.value.LottieValueCallback
16-
import com.airbnb.lottie.LottieProperty
13+
import com.facebook.react.bridge.ColorPropConverter
14+
import com.facebook.react.bridge.ReadableArray
15+
import com.facebook.react.bridge.ReadableType
16+
import java.lang.ref.WeakReference
1717
import java.util.regex.Pattern
1818

1919
/**
20-
* Class responsible for applying the properties to the LottieView.
21-
* The way react-native works makes it impossible to predict in which order properties will be set,
22-
* also some of the properties of the LottieView needs to be set simultaneously.
20+
* Class responsible for applying the properties to the LottieView. The way react-native works makes
21+
* it impossible to predict in which order properties will be set, also some of the properties of
22+
* the LottieView needs to be set simultaneously.
2323
*
24-
* To solve this, instance of this class accumulates all changes to the view and applies them at
25-
* the end of react transaction, so it could control how changes are applied.
24+
* To solve this, instance of this class accumulates all changes to the view and applies them at the
25+
* end of react transaction, so it could control how changes are applied.
2626
*/
2727
class LottieAnimationViewPropertyManager(view: LottieAnimationView) {
2828
private val viewWeakReference: WeakReference<LottieAnimationView>
@@ -48,26 +48,26 @@ class LottieAnimationViewPropertyManager(view: LottieAnimationView) {
4848
var animationJson: String? = null
4949
var progress: Float? = null
5050
var loop: Boolean? = null
51+
var autoPlay: Boolean? = null
5152
var speed: Float? = null
5253

5354
init {
5455
viewWeakReference = WeakReference(view)
5556
}
5657

5758
/**
58-
* Updates the view with changed fields.
59-
* Majority of the properties here are independent so they are has to be reset to null
60-
* as soon as view is updated with the value.
59+
* Updates the view with changed fields. Majority of the properties here are independent so they
60+
* are has to be reset to null as soon as view is updated with the value.
6161
*
62-
* The only exception from this rule is the group of the properties for the animation.
63-
* For now this is animationName and cacheStrategy. These two properties are should be set
62+
* The only exception from this rule is the group of the properties for the animation. For now
63+
* this is animationName and cacheStrategy. These two properties are should be set
6464
* simultaneously if the dirty flag is set.
6565
*/
6666
fun commitChanges() {
6767
val view = viewWeakReference.get() ?: return
6868

6969
textFilters?.let {
70-
if(it.size() > 0) {
70+
if (it.size() > 0) {
7171
val textDelegate = TextDelegate(view)
7272
for (i in 0 until textFilters!!.size()) {
7373
val current = textFilters!!.getMap(i)
@@ -99,6 +99,13 @@ class LottieAnimationViewPropertyManager(view: LottieAnimationView) {
9999
loop = null
100100
}
101101

102+
autoPlay?.let {
103+
if (it && !view.isAnimating) {
104+
view.playAnimation()
105+
}
106+
autoPlay = null
107+
}
108+
102109
speed?.let {
103110
view.speed = it
104111
speed = null
@@ -114,9 +121,7 @@ class LottieAnimationViewPropertyManager(view: LottieAnimationView) {
114121
renderMode = null
115122
}
116123

117-
layerType?.let {
118-
view.setLayerType(it, null)
119-
}
124+
layerType?.let { view.setLayerType(it, null) }
120125

121126
imageAssetsFolder?.let {
122127
view.imageAssetsFolder = it
@@ -133,11 +138,12 @@ class LottieAnimationViewPropertyManager(view: LottieAnimationView) {
133138
for (i in 0 until it.size()) {
134139
val current = it.getMap(i)
135140

136-
val color: Int = if (current.getType("color") == ReadableType.Map) {
137-
ColorPropConverter.getColor(current.getMap("color"), view.context)
138-
} else {
139-
current.getInt("color")
140-
}
141+
val color: Int =
142+
if (current.getType("color") == ReadableType.Map) {
143+
ColorPropConverter.getColor(current.getMap("color"), view.context)
144+
} else {
145+
current.getInt("color")
146+
}
141147

142148
val path = current.getString("keypath")
143149
val pathWithGlobStar = "$path.**"
@@ -153,4 +159,4 @@ class LottieAnimationViewPropertyManager(view: LottieAnimationView) {
153159
}
154160
}
155161
}
156-
}
162+
}

packages/core/android/src/main/java/com/airbnb/android/react/lottie/LottiePackage.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ class LottiePackage : ReactPackage {
1313
}
1414

1515
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
16-
return listOf(LottieAnimationViewManager())
16+
return listOf(LottieAnimationViewManager(reactContext))
1717
}
1818
}

packages/core/android/src/newarch/com/airbnb/android/react/lottie/LottieAnimationViewManager.kt

+27-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.airbnb.android.react.lottie
22

33
import android.animation.Animator
4+
import android.util.Log
45
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setColorFilters
56
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setEnableMergePaths
67
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setHardwareAcceleration
@@ -14,22 +15,28 @@ import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setSourceN
1415
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setSourceURL
1516
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setSpeed
1617
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setTextFilters
18+
import com.airbnb.android.react.lottie.LottieAnimationViewManagerImpl.setAutoPlay
1719
import com.airbnb.lottie.LottieAnimationView
1820
import com.facebook.react.bridge.Arguments
21+
import com.facebook.react.bridge.ReactContext
1922
import com.facebook.react.bridge.ReadableArray
2023
import com.facebook.react.bridge.ReadableMap;
2124
import com.facebook.react.module.annotations.ReactModule
2225
import com.facebook.react.uimanager.SimpleViewManager
2326
import com.facebook.react.uimanager.ThemedReactContext
27+
import com.facebook.react.uimanager.UIManagerHelper
2428
import com.facebook.react.uimanager.ViewManagerDelegate
2529
import com.facebook.react.uimanager.annotations.ReactProp
30+
import com.facebook.react.uimanager.events.Event
31+
import com.facebook.react.uimanager.events.RCTEventEmitter
2632
import com.facebook.react.uimanager.events.RCTModernEventEmitter
2733
import com.facebook.react.viewmanagers.LottieAnimationViewManagerDelegate
2834
import com.facebook.react.viewmanagers.LottieAnimationViewManagerInterface
2935
import java.util.*
3036

3137
@ReactModule(name = LottieAnimationViewManagerImpl.REACT_CLASS)
32-
class LottieAnimationViewManager : SimpleViewManager<LottieAnimationView>(),
38+
class LottieAnimationViewManager(val reactContext: ReactContext) :
39+
SimpleViewManager<LottieAnimationView>(),
3340
LottieAnimationViewManagerInterface<LottieAnimationView> {
3441
private val propManagersMap =
3542
WeakHashMap<LottieAnimationView, LottieAnimationViewPropertyManager>()
@@ -52,16 +59,16 @@ class LottieAnimationViewManager : SimpleViewManager<LottieAnimationView>(),
5259
val event = Arguments.createMap()
5360
event.putBoolean("isCancelled", isCancelled)
5461

55-
val screenContext = view.context
56-
if (screenContext is ThemedReactContext) {
57-
screenContext.getJSModule(RCTModernEventEmitter::class.java)
58-
?.receiveEvent(
59-
screenContext.surfaceId,
60-
view.id,
61-
"animationFinish",
62-
event
63-
)
64-
}
62+
val screenContext = view.context as ThemedReactContext
63+
64+
Log.d("Lottie", "view surface id ${screenContext.surfaceId} - view id ${view.id}")
65+
reactContext.getJSModule(RCTModernEventEmitter::class.java)
66+
?.receiveEvent(
67+
screenContext.surfaceId,
68+
view.id,
69+
"animationFinish",
70+
event
71+
)
6572
}
6673

6774
override fun getDelegate(): ViewManagerDelegate<LottieAnimationView> {
@@ -84,11 +91,13 @@ class LottieAnimationViewManager : SimpleViewManager<LottieAnimationView>(),
8491
}
8592

8693
override fun onAnimationEnd(animation: Animator) {
87-
sendOnAnimationFinishEvent(view, false)
94+
// TODO: fix crash
95+
// sendOnAnimationFinishEvent(view, false)
8896
}
8997

9098
override fun onAnimationCancel(animation: Animator) {
91-
sendOnAnimationFinishEvent(view, true)
99+
// TODO: fix crash
100+
// sendOnAnimationFinishEvent(view, true)
92101
}
93102

94103
override fun onAnimationRepeat(animation: Animator) {
@@ -176,6 +185,11 @@ class LottieAnimationViewManager : SimpleViewManager<LottieAnimationView>(),
176185
setLoop(loop, getOrCreatePropertyManager(view))
177186
}
178187

188+
@ReactProp(name = "autoPlay")
189+
override fun setAutoPlay(view: LottieAnimationView, autoPlay: Boolean) {
190+
setAutoPlay(autoPlay, getOrCreatePropertyManager(view))
191+
}
192+
179193
@ReactProp(name = "imageAssetsFolder")
180194
override fun setImageAssetsFolder(view: LottieAnimationView, imageAssetsFolder: String?) {
181195
setImageAssetsFolder(imageAssetsFolder, getOrCreatePropertyManager(view))

0 commit comments

Comments
 (0)