From 21885edcb75abe857f08dd52dad6e8c68a48180d Mon Sep 17 00:00:00 2001 From: MrMuzyk Date: Tue, 11 May 2021 16:14:02 +0200 Subject: [PATCH] feat: added onStop callback for android (#7) * feat: added onStop callback for android * fix: prevented onPlay event after reset Co-authored-by: TMaszko --- .../rivereactnative/RiveReactNativeView.kt | 33 ++++++++++++++----- example/src/App.tsx | 11 +++++-- src/Rive.tsx | 21 ++++++++++++ 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt b/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt index f96b184..dceefd5 100644 --- a/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt +++ b/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt @@ -20,11 +20,13 @@ import java.net.URL class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout(context), LifecycleEventListener { private var riveAnimationView: RiveAnimationView + private var resId: Int = -1 private val httpClient = ViewModelProvider(context.currentActivity as ViewModelStoreOwner).get(HttpClient::class.java) enum class Events(private val mName: String) { PLAY("onPlay"), - PAUSE("onPause"); + PAUSE("onPause"), + STOP("onStop"); override fun toString(): String { return mName @@ -32,7 +34,6 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout } - init { context.addLifecycleEventListener(this) riveAnimationView = RiveAnimationView(context) @@ -64,7 +65,12 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout } override fun notifyStop(animation: PlayableInstance) { - //TODO("Not yet implemented") + if (animation is LinearAnimationInstance) { + onStop(animation.animation.name) + } + if (animation is StateMachineInstance) { + onStop(animation.stateMachine.name, true) + } } } @@ -92,6 +98,16 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, Events.PAUSE.toString(), data) } + fun onStop(animationName: String, isStateMachine: Boolean = false) { + val reactContext = context as ReactContext + + val data = Arguments.createMap() + data.putString("animationName", animationName) + data.putBoolean("isStateMachine", isStateMachine) + + reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, Events.STOP.toString(), data) + } + fun play() { riveAnimationView.play() } @@ -101,13 +117,12 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout } fun stop() { - riveAnimationView.reset() - riveAnimationView.stop() + riveAnimationView.setRiveResource(resId, autoplay = false) } fun setResourceName(resourceName: String) { val (propsFit, propsAlignment) = Pair(riveAnimationView.fit, riveAnimationView.alignment) - val resId = resources.getIdentifier(resourceName, "raw", context.packageName) + resId = resources.getIdentifier(resourceName, "raw", context.packageName) riveAnimationView.setRiveResource(resId, fit = propsFit, alignment = propsAlignment) } @@ -125,7 +140,7 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout fun setUrl(url: String) { httpClient.byteLiveData.observe(context.currentActivity as LifecycleOwner, Observer { bytes -> - // Pass the Rive file bytes to the animation view + // Pass the Rive file bytes to the animation view riveAnimationView.setRiveBytes( bytes, // Fit the animation to the cover the entire view @@ -148,11 +163,11 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout } } -class HttpClient: ViewModel() { +class HttpClient : ViewModel() { var byteLiveData = MutableLiveData() fun fetchUrl(url: String) { - viewModelScope.launch{ + viewModelScope.launch { withContext(Dispatchers.IO) { fetchAsync(url) } diff --git a/example/src/App.tsx b/example/src/App.tsx index 16286e1..6e383f9 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -33,10 +33,17 @@ export default function App() { onPause={(animationName, isStateMachine) => { console.log('paused animation name :', animationName, isStateMachine); }} + onStop={(animationName, isStateMachine) => { + console.log( + 'stopped animation name :', + animationName, + isStateMachine + ); + }} style={styles.box} fit={Fit.ScaleDown} - // resourceName={Platform.OS === 'android' ? 'flying_car' : 'bird'} - url={'https://cdn.rive.app/animations/juice_v7.riv'} + resourceName={Platform.OS === 'android' ? 'flying_car' : 'bird'} + // url={'https://cdn.rive.app/animations/juice_v7.riv'} /> diff --git a/src/Rive.tsx b/src/Rive.tsx index 967d831..076f38e 100644 --- a/src/Rive.tsx +++ b/src/Rive.tsx @@ -24,6 +24,12 @@ type RiveProps = { isStateMachine: boolean; }> ) => void; + onStop?: ( + event: NativeSyntheticEvent<{ + animationName: string; + isStateMachine: boolean; + }> + ) => void; fit: Fit; alignment: Alignment; ref: any; @@ -38,6 +44,7 @@ const VIEW_NAME = 'RiveReactNativeView'; type Props = { onPlay?: (animationName: string, isStateMachine: boolean) => void; onPause?: (animationName: string, isStateMachine: boolean) => void; + onStop?: (animationName: string, isStateMachine: boolean) => void; fit?: Fit; style?: ViewStyle; testID?: string; @@ -51,6 +58,7 @@ const RiveContainer = React.forwardRef( { onPlay, onPause, + onStop, style, resourceName, url, @@ -87,6 +95,18 @@ const RiveContainer = React.forwardRef( [onPause] ); + const onStopHandler = useCallback( + ( + event: NativeSyntheticEvent<{ + animationName: string; + isStateMachine: boolean; + }> + ) => { + const { animationName, isStateMachine } = event.nativeEvent; + onStop?.(animationName, isStateMachine); + }, + [onStop] + ); const play = useCallback(() => { UIManager.dispatchViewManagerCommand( findNodeHandle(riveRef.current), @@ -130,6 +150,7 @@ const RiveContainer = React.forwardRef( url={url} onPlay={onPlayHandler} onPause={onPauseHandler} + onStop={onStopHandler} alignment={alignment} /> );