Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/android controller functions #3

Merged
Merged
Show file tree
Hide file tree
Changes from 11 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
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,52 @@ npm install dotlottie-react-native

## Usage

```js
import { DotlottieReactNativeView } from 'dotlottie-react-native';
```ts
import { Button, StyleSheet, View } from 'react-native';
import { DotLottie, Mode, type Dotlottie } from 'dotlottie-react-native';
import { useRef } from 'react';

// ...
export default function App() {
const ref = useRef<Dotlottie>(null);

<DotlottieReactNativeView color="tomato" />;
return (
<View style={styles.container}>
<DotLottie
ref={ref}
source={require('../assets/animation.lottie')}
style={styles.box}
loop={false}
autoplay={false}
/>
<Button title="Play" onPress={() => ref.current?.play()} />
<Button title="Pause" onPress={() => ref.current?.pause()} />
<Button title="Stop" onPress={() => ref.current?.stop()} />
<Button title="Loop" onPress={() => ref.current?.setLoop(true)} />
<Button title="Speed" onPress={() => ref.current?.setSpeed(1)} />
<Button
title="FORWARD"
onPress={() => ref.current?.setPlayMode(Mode.FORWARD)}
/>
<Button
title="REVERSE"
onPress={() => ref.current?.setPlayMode(Mode.REVERSE)}
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: 200,
height: 200,
marginVertical: 20,
},
});
```

## Contributing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager


class DotlottieReactNativePackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return emptyList()
return listOf()
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.dotlottiereactnative

import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.events.RCTEventEmitter
import com.lottiefiles.dotlottie.core.compose.runtime.DotLottieController
import com.lottiefiles.dotlottie.core.compose.ui.DotLottieAnimation
import com.lottiefiles.dotlottie.core.util.DotLottieEventListener
import com.lottiefiles.dotlottie.core.util.DotLottieSource

class DotlottieReactNativeView(context: ThemedReactContext) : FrameLayout(context) {

private var reactContext: ReactContext = context.reactApplicationContext
private var animationUrl: String? = null
private var loop: Boolean = false
private var autoplay: Boolean = true
private var speed: Float = 1f
var dotLottieController: DotLottieController = DotLottieController()

private val composeView: ComposeView =
ComposeView(context).apply {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
}

init {
addView(composeView)
// Set initial content
composeView.setContent { DotLottieContent() }
}

fun onReceiveNativeEvent(eventName: String, value: String?) {
val event = Arguments.createMap().apply { putString("message", value) }
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, eventName, event)
}

@Composable
fun DotLottieContent() {
dotLottieController = remember { DotLottieController() }

animationUrl?.let { url ->
DotLottieAnimation(
source = DotLottieSource.Url(url),
autoplay = autoplay,
loop = loop,
speed = speed,
controller = dotLottieController,
eventListeners =
listOf(
object : DotLottieEventListener {
override fun onLoad() {
onReceiveNativeEvent("onLoad", null)
}
override fun onComplete() {

onReceiveNativeEvent("onComplete", null)
}
override fun onLoadError() {

onReceiveNativeEvent("onLoadError", null)
}
override fun onPlay() {
onReceiveNativeEvent("onPlay", null)
}
override fun onStop() {
onReceiveNativeEvent("onRender", null)
}
override fun onPause() {
onReceiveNativeEvent("onPause", null)
}
override fun onFreeze() {
onReceiveNativeEvent("onFreeze", null)
}
override fun onUnFreeze() {
onReceiveNativeEvent("onUnFreeze", null)
}
override fun onDestroy() {
onReceiveNativeEvent("onDestroy", null)
}
}
Comment on lines +96 to +126
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets create eventListeners same as dotLottieController with remember. I think current approach will create new DotLottieEventListener on every composition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no it won't because it's on the class so it is created only once

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool we can resolve this

)
)
}
}

fun setSource(url: String?) {
animationUrl = url
composeView.setContent { DotLottieContent() }
}

fun setLoop(value: Boolean) {
loop = value
composeView.setContent { DotLottieContent() }
}

fun setAutoPlay(value: Boolean) {
autoplay = value
composeView.setContent { DotLottieContent() }
}

fun setSpeed(value: Double) {
speed = value.toFloat()
composeView.setContent { DotLottieContent() }
}
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,137 @@
package com.dotlottiereactnative

import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import com.dotlottie.dlplayer.Mode
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.lottiefiles.dotlottie.core.compose.ui.DotLottieAnimation
import com.lottiefiles.dotlottie.core.util.DotLottieSource

class DotlottieReactNativeViewManager : SimpleViewManager<View>() {

private var animationUrl: String? = null
private var loop: Boolean = false
private var autoplay: Boolean = true
class DotlottieReactNativeViewManager : SimpleViewManager<DotlottieReactNativeView>() {

override fun getName() = "DotlottieReactNativeView"

override fun createViewInstance(reactContext: ThemedReactContext): ComposeView {
return ComposeView(reactContext).apply { setContent { DotLottieContent() } }
override fun createViewInstance(reactContext: ThemedReactContext): DotlottieReactNativeView {
return DotlottieReactNativeView(reactContext)
}

override fun getCommandsMap(): MutableMap<String, Int> {
return mutableMapOf(
COMMAND_PLAY to COMMAND_PLAY_ID,
COMMAND_PAUSE to COMMAND_PAUSE_ID,
COMMAND_STOP to COMMAND_STOP_ID,
COMMAND_SET_SPEED to COMMAND_SET_SPEED_ID,
COMMAND_FREEZE to COMMAND_FREEZE_ID,
COMMAND_UNFREEZE to COMMAND_UNFREEZE_ID,
COMMAND_SET_LOOP to COMMAND_SET_LOOP_ID,
COMMAND_SET_FRAME to COMMAND_SET_FRAME_ID,
COMMAND_SET_PLAY_MODE to COMMAND_SET_PLAY_MODE_ID
)
}

@Composable
fun DotLottieContent() {
// Use the URL to load the animation
animationUrl?.let { url ->
DotLottieAnimation(source = DotLottieSource.Url(url), autoplay = autoplay, loop = loop)
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> {
return getEventTypeConstants(
"onLoad",
"onComplete",
"onLoadError",
"onPlay",
"onLoop",
"onDestroy",
"onUnFreeze",
"onFreeze",
"onPause",
"onFrame",
"onStop",
"onRender",
"onLoop"
)
}

override fun receiveCommand(
view: DotlottieReactNativeView,
commandId: Int,
args: ReadableArray?
) {

if (commandId == COMMAND_PLAY_ID) view.dotLottieController.play()
if (commandId == COMMAND_PAUSE_ID) view.dotLottieController.pause()
if (commandId == COMMAND_STOP_ID) view.dotLottieController.stop()
if (commandId == COMMAND_SET_SPEED_ID) {
val speed = args?.getDouble(0)?.toFloat() ?: 1f
view.dotLottieController.setSpeed(speed)
}
if (commandId == COMMAND_FREEZE_ID) {
view.dotLottieController.freeze()
}
if (commandId == COMMAND_UNFREEZE_ID) {
view.dotLottieController.unFreeze()
}
if (commandId == COMMAND_SET_LOOP_ID) {
val loop = args?.getBoolean(0) ?: false
view.dotLottieController.setLoop(loop)
}

if (commandId == COMMAND_SET_FRAME_ID) {
val frame = args?.getDouble(0)?.toFloat() ?: 0f
view.dotLottieController.setFrame(frame)
}
if (commandId == COMMAND_SET_PLAY_MODE_ID) {
val mode = args?.getInt(0)
val modeValue = Mode.values()[mode ?: 0]
view.dotLottieController.setPlayMode(modeValue)
}
}

@ReactProp(name = "source")
fun setSource(view: ComposeView, url: String?) {
// Update the URL and re-compose the view
animationUrl = url
view.setContent { DotLottieContent() }
fun setSource(view: DotlottieReactNativeView, url: String?) {
view.setSource(url)
}

@ReactProp(name = "loop")
fun setLoop(view: ComposeView, value: Boolean) {
loop = value
view.setContent { DotLottieContent() }
fun setLoop(view: DotlottieReactNativeView, value: Boolean) {
view.setLoop(value)
}

@ReactProp(name = "autoplay")
fun setAutoPlay(view: ComposeView, value: Boolean) {
autoplay = value
view.setContent { DotLottieContent() }
fun setAutoPlay(view: DotlottieReactNativeView, value: Boolean) {
view.setAutoPlay(value)
}

@ReactProp(name = "speed")
fun setSpeed(view: DotlottieReactNativeView, value: Double) {
view.setSpeed(value)
}

companion object {
const val TAG = "DotlottieReactNativeViewManager"

private const val COMMAND_PLAY = "play"
private const val COMMAND_PLAY_ID = 1

private const val COMMAND_PAUSE = "pause"
private const val COMMAND_PAUSE_ID = 2

private const val COMMAND_STOP = "stop"
private const val COMMAND_STOP_ID = 3

private const val COMMAND_SET_SPEED = "setSpeed"
private const val COMMAND_SET_SPEED_ID = 4

private const val COMMAND_FREEZE = "freeze"
private const val COMMAND_FREEZE_ID = 5

private const val COMMAND_UNFREEZE = "unFreeze"
private const val COMMAND_UNFREEZE_ID = 6

private const val COMMAND_SET_LOOP = "setLoop"
private const val COMMAND_SET_LOOP_ID = 7

private const val COMMAND_SET_PROGRESS = "setProgress"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this command is used for ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove this. was trying to use it for segment

private const val COMMAND_SET_PROGRESS_ID = 8

private const val COMMAND_SET_FRAME = "setFrame"
private const val COMMAND_SET_FRAME_ID = 9

private const val COMMAND_SET_PLAY_MODE = "setPlayMode"
private const val COMMAND_SET_PLAY_MODE_ID = 10
}
}
5 changes: 5 additions & 0 deletions android/src/main/java/com/dotlottiereactnative/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.dotlottiereactnative

fun getEventTypeConstants(vararg list: String): Map<String, Any> {
return list.associateWith { mapOf("phasedRegistrationNames" to mapOf("bubbled" to it)) }
}
Binary file added example/assets/animation.lottie
Binary file not shown.
14 changes: 12 additions & 2 deletions example/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
const path = require('path');
const { getDefaultConfig } = require('@react-native/metro-config');
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { getConfig } = require('react-native-builder-bob/metro-config');
const pkg = require('../package.json');

const root = path.resolve(__dirname, '..');

const defaultConfig = getDefaultConfig(__dirname);

const config = {
resolver: {
assetExts: [...defaultConfig.resolver.assetExts, 'lottie'],
},
};

const mergedConfig = mergeConfig(defaultConfig, config);

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
module.exports = getConfig(getDefaultConfig(__dirname), {
module.exports = getConfig(mergedConfig, {
root,
pkg,
project: __dirname,
Expand Down
Loading
Loading