change(react-native): updates to support expo

This commit is contained in:
Shekar Siri 2024-12-10 17:34:56 +01:00
parent f8bfbc18e6
commit 1f828817f0
4 changed files with 117 additions and 215 deletions

View file

@ -10,18 +10,10 @@ import com.openreplay.tracker.models.OROptions
class ReactNativeModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
// private val context = reactContext.acti
override fun getName(): String {
return NAME
}
// Example method
// See https://reactnative.dev/docs/native-modules-android
@ReactMethod
fun multiply(a: Double, b: Double, promise: Promise) {
promise.resolve(a * b * 2)
}
companion object {
const val NAME = "ORTrackerConnector"
}
@ -33,14 +25,13 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
val logs: Boolean = true,
val screen: Boolean = true,
val debugLogs: Boolean = false,
val wifiOnly: Boolean = true // assuming you want this as well
val wifiOnly: Boolean = true
)
private fun getBooleanOrDefault(map: ReadableMap, key: String, default: Boolean): Boolean {
return if (map.hasKey(key)) map.getBoolean(key) else default
}
// optionsMap: ReadableMap?,
@ReactMethod
fun startSession(
projectKey: String,
@ -97,8 +88,8 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
@ReactMethod
fun getSessionID(promise: Promise) {
try {
val sessionId = OpenReplay.getSessionID() ?: ""
promise.resolve(sessionId) // Resolve the promise with the session ID
val sessionId = OpenReplay.getSessionID()
promise.resolve(sessionId)
} catch (e: Exception) {
promise.reject("GET_SESSION_ID_ERROR", "Failed to retrieve session ID", e)
}
@ -111,8 +102,9 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
requestJSON: String,
responseJSON: String,
status: Int,
duration: ULong
duration: Double
) {
OpenReplay.networkRequest(url, method, requestJSON, responseJSON, status, duration)
val durationULong = duration.toLong().toULong()
OpenReplay.networkRequest(url, method, requestJSON, responseJSON, status, durationULong)
}
}

View file

@ -1,13 +1,12 @@
package com.openreplay.reactnative
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.PointF
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
import com.openreplay.tracker.listeners.Analytics
@ -15,151 +14,16 @@ import com.openreplay.tracker.listeners.SwipeDirection
import kotlin.math.abs
import kotlin.math.sqrt
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.GestureDetector
import com.facebook.react.ReactRootView
//class RnTrackerTouchManager : ViewGroupManager<TouchableFrameLayout>() {
// override fun getName(): String = "RnTrackerTouchView"
//
// override fun createViewInstance(reactContext: ThemedReactContext): TouchableFrameLayout {
// return TouchableFrameLayout(reactContext)
// }
//}
//
//class TouchableFrameLayout(context: Context) : FrameLayout(context) {
// private var gestureDetector: GestureDetector
// private var handler = Handler(Looper.getMainLooper())
// private var isScrolling = false
// private var lastX: Float = 0f
// private var lastY: Float = 0f
// private var swipeDirection: SwipeDirection = SwipeDirection.UNDEFINED
//
// init {
// gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
// override fun onSingleTapUp(e: MotionEvent): Boolean {
// Analytics.sendClick(e)
// return true
// }
//
// override fun onDown(e: MotionEvent): Boolean = true
//
// override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
// if (!isScrolling) {
// isScrolling = true
// }
//
// swipeDirection = SwipeDirection.fromDistances(distanceX, distanceY)
// lastX = e2.x
// lastY = e2.y
//
// handler.removeCallbacksAndMessages(null)
// handler.postDelayed({
// if (isScrolling) {
// isScrolling = false
// Analytics.sendSwipe(swipeDirection, lastX, lastY)
// }
// }, 200)
// return true
// }
// })
//
// setOnTouchListener { _, event ->
// Log.d("TouchEvent", "Event: ${event.actionMasked}, X: ${event.x}, Y: ${event.y}")
// gestureDetector.onTouchEvent(event)
// this.performClick()
// }
// }
//}
class RnTrackerTouchManager : ViewGroupManager<FrameLayout>() {
override fun getName(): String = "RnTrackerTouchView"
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
return ReactRootView(reactContext).apply {
// layoutParams = FrameLayout.LayoutParams(
// FrameLayout.LayoutParams.MATCH_PARENT,
// FrameLayout.LayoutParams.MATCH_PARENT
// )
// isClickable = true
// val touchStart = PointF()
// setOnTouchListener { view, event ->
// when (event.action) {
// MotionEvent.ACTION_DOWN -> {
// touchStart.set(event.x, event.y)
// true
// }
//
// MotionEvent.ACTION_UP -> {
// val deltaX = event.x - touchStart.x
// val deltaY = event.y - touchStart.y
// val distance = sqrt(deltaX * deltaX + deltaY * deltaY)
//
// if (distance > 10) {
// val direction = if (abs(deltaX) > abs(deltaY)) {
// if (deltaX > 0) "RIGHT" else "LEFT"
// } else {
// if (deltaY > 0) "DOWN" else "UP"
// }
// Analytics.sendSwipe(SwipeDirection.valueOf(direction), event.x, event.y)
// } else {
// Analytics.sendClick(event)
// view.performClick() // Perform click for accessibility
// }
// true
// }
//
// else -> false
// }
// }
}
return RnTrackerRootLayout(reactContext)
}
override fun addView(parent: FrameLayout, child: View, index: Int) {
child.isClickable = true
child.isFocusable = true
// child.layoutParams = FrameLayout.LayoutParams(
// FrameLayout.LayoutParams.MATCH_PARENT,
// FrameLayout.LayoutParams.MATCH_PARENT
// )
val touchStart = PointF()
child.setOnTouchListener(
View.OnTouchListener { view, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
view.performClick()
Analytics.sendClick(event)
true
}
MotionEvent.ACTION_UP -> {
val deltaX = event.x - touchStart.x
val deltaY = event.y - touchStart.y
val distance = sqrt(deltaX * deltaX + deltaY * deltaY)
if (distance > 10) {
val direction = if (abs(deltaX) > abs(deltaY)) {
if (deltaX > 0) "RIGHT" else "LEFT"
} else {
if (deltaY > 0) "DOWN" else "UP"
}
Analytics.sendSwipe(SwipeDirection.valueOf(direction), event.x, event.y)
} else {
Analytics.sendClick(event)
view.performClick() // Perform click for accessibility
}
true
}
else -> false
}
}
)
parent.addView(child)
parent.addView(child, index)
}
override fun getChildCount(parent: FrameLayout): Int = parent.childCount
@ -175,63 +39,79 @@ class RnTrackerTouchManager : ViewGroupManager<FrameLayout>() {
}
}
//class RnTrackerTouchManager : ViewGroupManager<FrameLayout>() {
// override fun getName(): String = "RnTrackerTouchView"
//
// override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
// return FrameLayout(reactContext).apply {
// layoutParams = FrameLayout.LayoutParams(
// FrameLayout.LayoutParams.MATCH_PARENT,
// FrameLayout.LayoutParams.MATCH_PARENT
// )
// isClickable = true
// val touchStart = PointF()
// setOnTouchListener { view, event ->
// when (event.action) {
// MotionEvent.ACTION_DOWN -> {
// touchStart.set(event.x, event.y)
// view.performClick()
// }
//
// MotionEvent.ACTION_UP -> {
// val deltaX = event.x - touchStart.x
// val deltaY = event.y - touchStart.y
// val distance = sqrt(deltaX * deltaX + deltaY * deltaY)
//
// if (distance > 10) {
// val direction = if (abs(deltaX) > abs(deltaY)) {
// if (deltaX > 0) "RIGHT" else "LEFT"
// } else {
// if (deltaY > 0) "DOWN" else "UP"
// }
// Analytics.sendSwipe(SwipeDirection.valueOf(direction), event.x, event.y)
// view.performClick()
// } else {
// Analytics.sendClick(event)
// view.performClick()
// }
// true
// }
//
// else -> false
// }
// }
// }
// }
//
// override fun addView(parent: FrameLayout, child: View, index: Int) {
// parent.addView(child, index)
// }
//
// override fun getChildCount(parent: FrameLayout): Int = parent.childCount
//
// override fun getChildAt(parent: FrameLayout, index: Int): View = parent.getChildAt(index)
//
// override fun removeViewAt(parent: FrameLayout, index: Int) {
// parent.removeViewAt(index)
// }
//
// override fun removeAllViews(parent: FrameLayout) {
// parent.removeAllViews()
// }
//}
class RnTrackerRootLayout(context: Context) : FrameLayout(context) {
private val touchStart = PointF()
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
// Record the starting point for swipe/tap differentiation
touchStart.x = ev.x
touchStart.y = ev.y
Log.d("RnTrackerRootLayout", "ACTION_DOWN at global: (${ev.rawX}, ${ev.rawY})")
}
MotionEvent.ACTION_UP -> {
val deltaX = ev.x - touchStart.x
val deltaY = ev.y - touchStart.y
val distance = sqrt(deltaX * deltaX + deltaY * deltaY)
// Find the exact view that was tapped
val tappedView = findViewAt(this, ev.x.toInt(), ev.y.toInt())
if (distance > 10) {
// Consider this a swipe
val direction = if (abs(deltaX) > abs(deltaY)) {
if (deltaX > 0) "RIGHT" else "LEFT"
} else {
if (deltaY > 0) "DOWN" else "UP"
}
Log.d("RnTrackerRootLayout", "Swipe detected: $direction")
Analytics.sendSwipe(SwipeDirection.valueOf(direction), ev.rawX, ev.rawY)
} else {
// Consider this a tap
val label = tappedView?.contentDescription?.toString() ?: "Button"
Log.d("RnTrackerRootLayout", "Tap detected on $tappedView with label: $label")
val currentTime = android.os.SystemClock.uptimeMillis()
val syntheticEvent = MotionEvent.obtain(
currentTime,
currentTime,
MotionEvent.ACTION_UP,
ev.rawX,
ev.rawY,
0
)
Analytics.sendClick(syntheticEvent, label)
syntheticEvent.recycle()
// Perform the click on the tapped view
tappedView?.performClick()
}
}
}
// Call super to ensure normal behavior (scrolling, clicks, etc.) is not disturbed
return super.dispatchTouchEvent(ev)
}
private fun findViewAt(parent: ViewGroup, x: Int, y: Int): View? {
for (i in parent.childCount - 1 downTo 0) {
val child = parent.getChildAt(i)
if (isPointInsideView(x, y, child)) {
if (child is ViewGroup) {
val childX = x - child.left
val childY = y - child.top
val result = findViewAt(child, childX, childY)
return result ?: child
} else {
return child
}
}
}
return null
}
private fun isPointInsideView(x: Int, y: Int, view: View): Boolean {
return x >= view.left && x <= view.right && y >= view.top && y <= view.bottom
}
}

View file

@ -0,0 +1,27 @@
// module.exports = function (config) {
// // Modify the config as needed
// return config;
// };
const { withAppBuildGradle, withMainApplication } = require('@expo/config-plugins');
function addPackageToMainApplication(src) {
console.log('Adding OpenReplay package to MainApplication.java', src);
// Insert `packages.add(new ReactNativePackage());` before return packages;
if (src.includes('packages.add(new ReactNativePackage())')) {
return src;
}
return src.replace(
'return packages;',
`packages.add(new com.openreplay.reactnative.ReactNativePackage());\n return packages;`
);
}
module.exports = function configPlugin(config) {
return withMainApplication(config, (config) => {
if (config.modResults.contents) {
config.modResults.contents = addPackageToMainApplication(config.modResults.contents);
}
return config;
});
};

View file

@ -156,5 +156,8 @@
}
]
]
},
"dependencies": {
"@expo/config-plugins": "^9.0.12"
}
}