Compare commits
5 commits
main
...
expo-suppo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef69e5c422 | ||
|
|
817da3a088 | ||
|
|
314ff737ad | ||
|
|
1f828817f0 | ||
|
|
f8bfbc18e6 |
5 changed files with 139 additions and 217 deletions
|
|
@ -91,7 +91,7 @@ dependencies {
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
implementation("com.facebook.react:react-native:0.20.1")
|
implementation("com.facebook.react:react-native:0.20.1")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
|
||||||
implementation("com.github.openreplay:android-tracker:v1.1.2")
|
implementation("com.github.openreplay:android-tracker:v1.1.3")
|
||||||
}
|
}
|
||||||
|
|
||||||
//allprojects {
|
//allprojects {
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,10 @@ import com.openreplay.tracker.models.OROptions
|
||||||
class ReactNativeModule(reactContext: ReactApplicationContext) :
|
class ReactNativeModule(reactContext: ReactApplicationContext) :
|
||||||
ReactContextBaseJavaModule(reactContext) {
|
ReactContextBaseJavaModule(reactContext) {
|
||||||
|
|
||||||
// private val context = reactContext.acti
|
|
||||||
override fun getName(): String {
|
override fun getName(): String {
|
||||||
return NAME
|
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 {
|
companion object {
|
||||||
const val NAME = "ORTrackerConnector"
|
const val NAME = "ORTrackerConnector"
|
||||||
}
|
}
|
||||||
|
|
@ -33,14 +25,13 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
|
||||||
val logs: Boolean = true,
|
val logs: Boolean = true,
|
||||||
val screen: Boolean = true,
|
val screen: Boolean = true,
|
||||||
val debugLogs: Boolean = false,
|
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 {
|
private fun getBooleanOrDefault(map: ReadableMap, key: String, default: Boolean): Boolean {
|
||||||
return if (map.hasKey(key)) map.getBoolean(key) else default
|
return if (map.hasKey(key)) map.getBoolean(key) else default
|
||||||
}
|
}
|
||||||
|
|
||||||
// optionsMap: ReadableMap?,
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun startSession(
|
fun startSession(
|
||||||
projectKey: String,
|
projectKey: String,
|
||||||
|
|
@ -97,8 +88,8 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun getSessionID(promise: Promise) {
|
fun getSessionID(promise: Promise) {
|
||||||
try {
|
try {
|
||||||
val sessionId = OpenReplay.getSessionID() ?: ""
|
val sessionId = OpenReplay.getSessionID()
|
||||||
promise.resolve(sessionId) // Resolve the promise with the session ID
|
promise.resolve(sessionId)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
promise.reject("GET_SESSION_ID_ERROR", "Failed to retrieve session ID", e)
|
promise.reject("GET_SESSION_ID_ERROR", "Failed to retrieve session ID", e)
|
||||||
}
|
}
|
||||||
|
|
@ -111,8 +102,9 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
|
||||||
requestJSON: String,
|
requestJSON: String,
|
||||||
responseJSON: String,
|
responseJSON: String,
|
||||||
status: Int,
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
package com.openreplay.reactnative
|
package com.openreplay.reactnative
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.GestureDetector
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
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.ThemedReactContext
|
||||||
import com.facebook.react.uimanager.ViewGroupManager
|
import com.facebook.react.uimanager.ViewGroupManager
|
||||||
import com.openreplay.tracker.listeners.Analytics
|
import com.openreplay.tracker.listeners.Analytics
|
||||||
|
|
@ -15,151 +15,16 @@ import com.openreplay.tracker.listeners.SwipeDirection
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.sqrt
|
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>() {
|
class RnTrackerTouchManager : ViewGroupManager<FrameLayout>() {
|
||||||
|
|
||||||
override fun getName(): String = "RnTrackerTouchView"
|
override fun getName(): String = "RnTrackerTouchView"
|
||||||
|
|
||||||
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
||||||
return ReactRootView(reactContext).apply {
|
return RnTrackerRootLayout(reactContext)
|
||||||
// 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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addView(parent: FrameLayout, child: View, index: Int) {
|
override fun addView(parent: FrameLayout, child: View, index: Int) {
|
||||||
child.isClickable = true
|
parent.addView(child, index)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChildCount(parent: FrameLayout): Int = parent.childCount
|
override fun getChildCount(parent: FrameLayout): Int = parent.childCount
|
||||||
|
|
@ -175,63 +40,102 @@ class RnTrackerTouchManager : ViewGroupManager<FrameLayout>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//class RnTrackerTouchManager : ViewGroupManager<FrameLayout>() {
|
class RnTrackerRootLayout(context: Context) : FrameLayout(context) {
|
||||||
// override fun getName(): String = "RnTrackerTouchView"
|
private val touchStart = PointF()
|
||||||
//
|
private val gestureDetector: GestureDetector
|
||||||
// override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
||||||
// return FrameLayout(reactContext).apply {
|
private var currentTappedView: View? = null
|
||||||
// layoutParams = FrameLayout.LayoutParams(
|
|
||||||
// FrameLayout.LayoutParams.MATCH_PARENT,
|
// Variables to track total movement
|
||||||
// FrameLayout.LayoutParams.MATCH_PARENT
|
private var totalDeltaX: Float = 0f
|
||||||
// )
|
private var totalDeltaY: Float = 0f
|
||||||
// isClickable = true
|
|
||||||
// val touchStart = PointF()
|
init {
|
||||||
// setOnTouchListener { view, event ->
|
gestureDetector = GestureDetector(context, GestureListener())
|
||||||
// when (event.action) {
|
}
|
||||||
// MotionEvent.ACTION_DOWN -> {
|
|
||||||
// touchStart.set(event.x, event.y)
|
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||||
// view.performClick()
|
// Pass all touch events to the GestureDetector
|
||||||
// }
|
gestureDetector.onTouchEvent(ev)
|
||||||
//
|
|
||||||
// MotionEvent.ACTION_UP -> {
|
when (ev.action) {
|
||||||
// val deltaX = event.x - touchStart.x
|
MotionEvent.ACTION_DOWN -> {
|
||||||
// val deltaY = event.y - touchStart.y
|
// Record the starting point for potential swipe
|
||||||
// val distance = sqrt(deltaX * deltaX + deltaY * deltaY)
|
touchStart.x = ev.x
|
||||||
//
|
touchStart.y = ev.y
|
||||||
// if (distance > 10) {
|
// Reset total movement
|
||||||
// val direction = if (abs(deltaX) > abs(deltaY)) {
|
totalDeltaX = 0f
|
||||||
// if (deltaX > 0) "RIGHT" else "LEFT"
|
totalDeltaY = 0f
|
||||||
// } else {
|
// Find and store the view that was touched
|
||||||
// if (deltaY > 0) "DOWN" else "UP"
|
currentTappedView = findViewAt(this, ev.x.toInt(), ev.y.toInt())
|
||||||
// }
|
// Log.d(
|
||||||
// Analytics.sendSwipe(SwipeDirection.valueOf(direction), event.x, event.y)
|
// "RnTrackerRootLayout",
|
||||||
// view.performClick()
|
// "ACTION_DOWN at global: (${ev.rawX}, ${ev.rawY}) on view: $currentTappedView"
|
||||||
// } else {
|
// )
|
||||||
// Analytics.sendClick(event)
|
}
|
||||||
// view.performClick()
|
MotionEvent.ACTION_MOVE -> {
|
||||||
// }
|
// Accumulate movement
|
||||||
// true
|
val deltaX = ev.x - touchStart.x
|
||||||
// }
|
val deltaY = ev.y - touchStart.y
|
||||||
//
|
totalDeltaX += deltaX
|
||||||
// else -> false
|
totalDeltaY += deltaY
|
||||||
// }
|
// Update touchStart for the next move event
|
||||||
// }
|
touchStart.x = ev.x
|
||||||
// }
|
touchStart.y = ev.y
|
||||||
// }
|
// Log.d("RnTrackerRootLayout", "Accumulated movement - X: $totalDeltaX, Y: $totalDeltaY")
|
||||||
//
|
}
|
||||||
// override fun addView(parent: FrameLayout, child: View, index: Int) {
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
// parent.addView(child, index)
|
// Determine if the accumulated movement qualifies as a swipe
|
||||||
// }
|
val distance = sqrt(totalDeltaX * totalDeltaX + totalDeltaY * totalDeltaY)
|
||||||
//
|
|
||||||
// override fun getChildCount(parent: FrameLayout): Int = parent.childCount
|
if (distance > SWIPE_DISTANCE_THRESHOLD) {
|
||||||
//
|
val direction = if (abs(totalDeltaX) > abs(totalDeltaY)) {
|
||||||
// override fun getChildAt(parent: FrameLayout, index: Int): View = parent.getChildAt(index)
|
if (totalDeltaX > 0) "RIGHT" else "LEFT"
|
||||||
//
|
} else {
|
||||||
// override fun removeViewAt(parent: FrameLayout, index: Int) {
|
if (totalDeltaY > 0) "DOWN" else "UP"
|
||||||
// parent.removeViewAt(index)
|
}
|
||||||
// }
|
Log.d("RnTrackerRootLayout", "Swipe detected: $direction")
|
||||||
//
|
Analytics.sendSwipe(SwipeDirection.valueOf(direction), ev.rawX, ev.rawY)
|
||||||
// override fun removeAllViews(parent: FrameLayout) {
|
}
|
||||||
// parent.removeAllViews()
|
}
|
||||||
// }
|
}
|
||||||
//}
|
|
||||||
|
// Ensure normal event propagation
|
||||||
|
return super.dispatchTouchEvent(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SWIPE_DISTANCE_THRESHOLD = 100f // Adjust as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
|
||||||
|
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||||
|
Log.d("GestureListener", "Single tap detected at: (${e.rawX}, ${e.rawY})")
|
||||||
|
val label = currentTappedView?.contentDescription?.toString() ?: "Button"
|
||||||
|
Analytics.sendClick(e, label)
|
||||||
|
currentTappedView?.performClick()
|
||||||
|
return super.onSingleTapUp(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
22
tracker/tracker-reactnative/app.plugin.js
Normal file
22
tracker/tracker-reactnative/app.plugin.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const { 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;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@openreplay/react-native",
|
"name": "@openreplay/react-native",
|
||||||
"version": "0.6.6",
|
"version": "0.6.10",
|
||||||
"description": "Openreplay React-native connector for iOS applications",
|
"description": "Openreplay React-native connector for iOS applications",
|
||||||
"main": "lib/commonjs/index",
|
"main": "lib/commonjs/index",
|
||||||
"module": "lib/module/index",
|
"module": "lib/module/index",
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
"android",
|
"android",
|
||||||
"ios",
|
"ios",
|
||||||
"cpp",
|
"cpp",
|
||||||
|
"app.plugin.js",
|
||||||
"*.podspec",
|
"*.podspec",
|
||||||
"!ios/build",
|
"!ios/build",
|
||||||
"!android/build",
|
"!android/build",
|
||||||
|
|
@ -156,5 +157,8 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@expo/config-plugins": "^9.0.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue