@amplytools/react-native-amply-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +178 -0
- package/README.md +714 -0
- package/android/build.gradle +90 -0
- package/android/consumer-rules.pro +1 -0
- package/android/gradle.properties +3 -0
- package/android/settings.gradle +9 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/AmplyModule.kt +384 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/AmplyPackage.kt +39 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/core/AmplyClient.kt +30 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/core/DefaultAmplyClient.kt +296 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/model/AmplyInitializationOptions.kt +10 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/model/DataSetType.kt +42 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/model/DataSetTypeMapper.kt +38 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/model/DeepLinkPayload.kt +8 -0
- package/android/src/main/java/tools/amply/sdk/reactnative/model/EventEnvelope.kt +9 -0
- package/android/src/main/jni/AmplyTurboModule.cpp +29 -0
- package/android/src/main/jni/CMakeLists.txt +76 -0
- package/android/src/newarch/java/tools/amply/sdk/reactnative/NativeAmplyModuleSpec.java +75 -0
- package/android/src/newarch/jni/AmplyReactNative-generated.cpp +77 -0
- package/android/src/newarch/jni/AmplyReactNative.h +31 -0
- package/android/src/newarch/jni/CMakeLists.txt +40 -0
- package/app.plugin.js +1 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +234 -0
- package/dist/index.mjs.map +1 -0
- package/dist/plugin/index.d.ts +6 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +186 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/index.mjs +169 -0
- package/dist/plugin/index.mjs.map +1 -0
- package/dist/plugin/src/index.d.ts +6 -0
- package/dist/plugin/src/index.d.ts.map +1 -0
- package/dist/plugin/src/index.js +3 -0
- package/dist/plugin/src/withAmply.d.ts +30 -0
- package/dist/plugin/src/withAmply.d.ts.map +1 -0
- package/dist/plugin/src/withAmply.js +51 -0
- package/dist/plugin/withAmply.d.ts +12 -0
- package/dist/plugin/withAmply.d.ts.map +1 -0
- package/dist/src/__tests__/index.test.d.ts +2 -0
- package/dist/src/__tests__/index.test.d.ts.map +1 -0
- package/dist/src/__tests__/index.test.js +70 -0
- package/dist/src/hooks/useAmplySystemEvents.d.ts +12 -0
- package/dist/src/hooks/useAmplySystemEvents.d.ts.map +1 -0
- package/dist/src/hooks/useAmplySystemEvents.js +56 -0
- package/dist/src/index.d.ts +32 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +80 -0
- package/dist/src/nativeModule.d.ts +5 -0
- package/dist/src/nativeModule.d.ts.map +1 -0
- package/dist/src/nativeModule.js +48 -0
- package/dist/src/nativeSpecs/NativeAmplyModule.d.ts +75 -0
- package/dist/src/nativeSpecs/NativeAmplyModule.d.ts.map +1 -0
- package/dist/src/nativeSpecs/NativeAmplyModule.js +2 -0
- package/dist/src/systemEventUtils.d.ts +3 -0
- package/dist/src/systemEventUtils.d.ts.map +1 -0
- package/dist/src/systemEventUtils.js +30 -0
- package/dist/src/systemEvents.d.ts +6 -0
- package/dist/src/systemEvents.d.ts.map +1 -0
- package/dist/src/systemEvents.js +8 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/ARCHITECTURE.md +1115 -0
- package/expo-module.config.json +11 -0
- package/ios/AmplyReactNative.podspec +32 -0
- package/ios/README.md +11 -0
- package/ios/Sources/AmplyReactNative/AmplyModule.mm +332 -0
- package/ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative-generated.mm +111 -0
- package/ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative.h +152 -0
- package/package.json +71 -0
- package/plugin/build/index.d.ts +5 -0
- package/plugin/build/index.js +8 -0
- package/plugin/build/withAmply.d.ts +29 -0
- package/plugin/build/withAmply.js +53 -0
- package/plugin/src/index.ts +7 -0
- package/plugin/src/withAmply.ts +68 -0
- package/plugin/tsconfig.json +8 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/react-native.config.js +34 -0
- package/scripts/codegen.js +212 -0
- package/src/__tests__/index.test.ts +92 -0
- package/src/hooks/useAmplySystemEvents.ts +75 -0
- package/src/index.ts +115 -0
- package/src/nativeModule.ts +65 -0
- package/src/nativeSpecs/NativeAmplyModule.ts +80 -0
- package/src/systemEventUtils.ts +35 -0
- package/src/systemEvents.ts +13 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
package tools.amply.sdk.reactnative.core
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.app.Application
|
|
5
|
+
import android.os.Handler
|
|
6
|
+
import android.os.Looper
|
|
7
|
+
import tools.amply.sdk.reactnative.model.AmplyInitializationOptions
|
|
8
|
+
import tools.amply.sdk.reactnative.model.DataSetType
|
|
9
|
+
import tools.amply.sdk.reactnative.model.DeepLinkPayload
|
|
10
|
+
import tools.amply.sdk.reactnative.model.EventEnvelope
|
|
11
|
+
import tools.amply.sdk.reactnative.model.toNativeDataSetType
|
|
12
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
13
|
+
import java.util.concurrent.atomic.AtomicLong
|
|
14
|
+
import java.util.concurrent.atomic.AtomicReference
|
|
15
|
+
import java.lang.ref.WeakReference
|
|
16
|
+
import kotlinx.coroutines.Dispatchers
|
|
17
|
+
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
18
|
+
import kotlinx.coroutines.flow.SharedFlow
|
|
19
|
+
import kotlinx.coroutines.flow.asSharedFlow
|
|
20
|
+
import kotlinx.coroutines.runBlocking
|
|
21
|
+
import kotlinx.coroutines.sync.Mutex
|
|
22
|
+
import kotlinx.coroutines.sync.withLock
|
|
23
|
+
import kotlinx.coroutines.withContext
|
|
24
|
+
import tools.amply.sdk.Amply
|
|
25
|
+
import tools.amply.sdk.actions.DeepLinkListener
|
|
26
|
+
import tools.amply.sdk.config.AmplyConfig
|
|
27
|
+
import tools.amply.sdk.config.amplyConfig
|
|
28
|
+
import tools.amply.sdk.core.AmplySDKInterface
|
|
29
|
+
import tools.amply.sdk.events.EventInterface
|
|
30
|
+
import tools.amply.sdk.events.SystemEventsListener
|
|
31
|
+
|
|
32
|
+
class DefaultAmplyClient(
|
|
33
|
+
private val application: Application,
|
|
34
|
+
) : AmplyClient {
|
|
35
|
+
|
|
36
|
+
private val mutex = Mutex()
|
|
37
|
+
private var amplyInstance: Amply? = null
|
|
38
|
+
private val deepLinkRegistered = AtomicBoolean(false)
|
|
39
|
+
private val systemEventsRegistered = AtomicBoolean(false)
|
|
40
|
+
private val deepLinkSequence = AtomicLong(0L)
|
|
41
|
+
private val lastResumedActivity = AtomicReference<WeakReference<Activity>?>(null)
|
|
42
|
+
private val sessionPrimed = AtomicBoolean(false)
|
|
43
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
44
|
+
|
|
45
|
+
private val _deepLinkEvents = MutableSharedFlow<DeepLinkPayload>(
|
|
46
|
+
replay = 1,
|
|
47
|
+
extraBufferCapacity = 16,
|
|
48
|
+
)
|
|
49
|
+
override val deepLinkEvents: SharedFlow<DeepLinkPayload> = _deepLinkEvents.asSharedFlow()
|
|
50
|
+
private val _systemEvents = MutableSharedFlow<EventEnvelope>(
|
|
51
|
+
replay = 32,
|
|
52
|
+
extraBufferCapacity = 128,
|
|
53
|
+
)
|
|
54
|
+
override val systemEvents: SharedFlow<EventEnvelope> = _systemEvents.asSharedFlow()
|
|
55
|
+
|
|
56
|
+
override suspend fun initialize(options: AmplyInitializationOptions) {
|
|
57
|
+
var createdInstance = false
|
|
58
|
+
mutex.withLock {
|
|
59
|
+
if (amplyInstance == null) {
|
|
60
|
+
val config = buildConfig(options)
|
|
61
|
+
android.util.Log.i(
|
|
62
|
+
"AmplyReactNative",
|
|
63
|
+
"Initializing Amply with appId=${options.appId} apiKeyPublic=${options.apiKeyPublic.takeIf { it.isNotEmpty() } ?: "<empty>"}"
|
|
64
|
+
)
|
|
65
|
+
val instance = withContext(Dispatchers.Default) {
|
|
66
|
+
Amply(config, application)
|
|
67
|
+
}
|
|
68
|
+
ensureSystemEventsListener(instance)
|
|
69
|
+
amplyInstance = instance
|
|
70
|
+
createdInstance = true
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (createdInstance) {
|
|
74
|
+
maybePrimeSessionTracker()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override fun isInitialized(): Boolean = amplyInstance != null
|
|
79
|
+
|
|
80
|
+
override suspend fun track(name: String, properties: Map<String, Any?>?) {
|
|
81
|
+
val instance = requireInstance()
|
|
82
|
+
withContext(Dispatchers.IO) {
|
|
83
|
+
android.util.Log.i(
|
|
84
|
+
"AmplyReactNative",
|
|
85
|
+
"Tracking event '$name' with properties=${properties?.filterValues { it != null }}"
|
|
86
|
+
)
|
|
87
|
+
instance.track(name, properties?.toNonNullMap() ?: emptyMap())
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override suspend fun getRecentEvents(limit: Int): List<EventEnvelope> {
|
|
92
|
+
val instance = requireInstance()
|
|
93
|
+
return withContext(Dispatchers.IO) {
|
|
94
|
+
val events = instance.getRecentEvents(limit)
|
|
95
|
+
android.util.Log.i(
|
|
96
|
+
"AmplyReactNative",
|
|
97
|
+
"Fetched ${events.size} recent events (limit=$limit)"
|
|
98
|
+
)
|
|
99
|
+
events.map { it.toEventEnvelope() }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
override suspend fun getDataSetSnapshot(type: DataSetType): Map<String, Any?> {
|
|
104
|
+
val instance = requireInstance()
|
|
105
|
+
val nativeType = type.toNativeDataSetType()
|
|
106
|
+
return withContext(Dispatchers.IO) {
|
|
107
|
+
val snapshot = instance.getDataSetSnapshot(nativeType)
|
|
108
|
+
android.util.Log.i(
|
|
109
|
+
"AmplyReactNative",
|
|
110
|
+
"DataSetSnapshot(${type.javaClass.simpleName}) keys=${snapshot.keys}"
|
|
111
|
+
)
|
|
112
|
+
snapshot.toNullableValues()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Registers a listener for deep links triggered by Amply SDK campaigns.
|
|
118
|
+
*
|
|
119
|
+
* This listener allows app developers to:
|
|
120
|
+
* 1. Know that a deep link originated from Amply SDK (vs. external sources like
|
|
121
|
+
* push notifications, browser links, or other SDKs)
|
|
122
|
+
* 2. Access campaign metadata via the `info` map (campaign ID, variant, etc.)
|
|
123
|
+
* that is not available in the URL itself
|
|
124
|
+
* 3. Track/log Amply-specific deep link events for analytics
|
|
125
|
+
*
|
|
126
|
+
* Example use case:
|
|
127
|
+
* // In JS:
|
|
128
|
+
* Amply.addDeepLinkListener(event => {
|
|
129
|
+
* // We know this deep link came from an Amply campaign, not from elsewhere
|
|
130
|
+
* analytics.track('Amply campaign triggered', { url: event.url, info: event.info });
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* The deep link flow:
|
|
134
|
+
* Campaign triggers → KMP SDK → onDeepLink callback → JS event emitted
|
|
135
|
+
* ↓ (then)
|
|
136
|
+
* startActivity(Intent.ACTION_VIEW) → Linking API
|
|
137
|
+
*
|
|
138
|
+
* Note: The listener is an observer, not a controller. The SDK will still open
|
|
139
|
+
* the URL via system after emitting the event.
|
|
140
|
+
*/
|
|
141
|
+
override fun registerDeepLinkListener() {
|
|
142
|
+
val instance = requireInstance()
|
|
143
|
+
if (!deepLinkRegistered.compareAndSet(false, true)) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
android.util.Log.i("AmplyReactNative", "Registering deep link listener")
|
|
147
|
+
|
|
148
|
+
instance.registerDeepLinkListener(object : DeepLinkListener {
|
|
149
|
+
override fun onDeepLink(url: String, info: Map<String, Any>): Boolean {
|
|
150
|
+
android.util.Log.i(
|
|
151
|
+
"AmplyReactNative",
|
|
152
|
+
"Received deep link from Amply url=$url infoKeys=${info.keys}"
|
|
153
|
+
)
|
|
154
|
+
val payload = DeepLinkPayload(
|
|
155
|
+
sequenceId = deepLinkSequence.incrementAndGet(),
|
|
156
|
+
url = url,
|
|
157
|
+
info = info.mapValues { it.value },
|
|
158
|
+
consumed = false
|
|
159
|
+
)
|
|
160
|
+
if (!_deepLinkEvents.tryEmit(payload)) {
|
|
161
|
+
android.util.Log.w(
|
|
162
|
+
"AmplyReactNative",
|
|
163
|
+
"Dropping deep link event due to backpressure sequenceId=${payload.sequenceId}"
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
override fun registerSystemEventListener() {
|
|
172
|
+
val instance = requireInstance()
|
|
173
|
+
android.util.Log.i(
|
|
174
|
+
"AmplyReactNative",
|
|
175
|
+
"registerSystemEventListener() called; alreadyRegistered=${systemEventsRegistered.get()}"
|
|
176
|
+
)
|
|
177
|
+
ensureSystemEventsListener(instance)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
override fun onHostResume(activity: Activity?) {
|
|
181
|
+
if (activity != null) {
|
|
182
|
+
lastResumedActivity.set(WeakReference(activity))
|
|
183
|
+
}
|
|
184
|
+
maybePrimeSessionTracker()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
override fun shutdown() {
|
|
188
|
+
runBlocking {
|
|
189
|
+
mutex.withLock {
|
|
190
|
+
amplyInstance = null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
deepLinkRegistered.set(false)
|
|
194
|
+
systemEventsRegistered.set(false)
|
|
195
|
+
android.util.Log.i("AmplyReactNative", "Amply client shutdown; deep link listener cleared")
|
|
196
|
+
deepLinkSequence.set(0L)
|
|
197
|
+
sessionPrimed.set(false)
|
|
198
|
+
lastResumedActivity.set(null)
|
|
199
|
+
_deepLinkEvents.resetReplayCache()
|
|
200
|
+
_systemEvents.resetReplayCache()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private fun requireInstance(): Amply {
|
|
204
|
+
return amplyInstance ?: throw IllegalStateException("Amply has not been initialized yet")
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private fun buildConfig(options: AmplyInitializationOptions): AmplyConfig {
|
|
208
|
+
return amplyConfig {
|
|
209
|
+
api {
|
|
210
|
+
appId = options.appId
|
|
211
|
+
apiKeyPublic = options.apiKeyPublic
|
|
212
|
+
options.apiKeySecret?.let { apiKeySecret = it }
|
|
213
|
+
}
|
|
214
|
+
options.defaultConfig?.let { defaultConfig = it }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private fun Map<String, Any?>.toNonNullMap(): Map<String, Any> =
|
|
219
|
+
entries.mapNotNull { (key, value) -> value?.let { key to it } }.toMap()
|
|
220
|
+
|
|
221
|
+
private fun Map<String, Any>.toNullableValues(): Map<String, Any?> =
|
|
222
|
+
mapValues { it.value }
|
|
223
|
+
|
|
224
|
+
private fun EventInterface.toEventEnvelope(): EventEnvelope =
|
|
225
|
+
EventEnvelope(
|
|
226
|
+
id = null,
|
|
227
|
+
name = name,
|
|
228
|
+
type = type.name.lowercase(),
|
|
229
|
+
timestamp = timestamp,
|
|
230
|
+
properties = properties.mapValues { it.value }
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
private fun ensureSystemEventsListener(instance: Amply) {
|
|
234
|
+
if (!systemEventsRegistered.compareAndSet(false, true)) {
|
|
235
|
+
android.util.Log.i(
|
|
236
|
+
"AmplyReactNative",
|
|
237
|
+
"System events listener already registered; skipping setSystemEventsListener"
|
|
238
|
+
)
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
instance.setSystemEventsListener(object : SystemEventsListener {
|
|
242
|
+
override fun onEvent(event: EventInterface) {
|
|
243
|
+
android.util.Log.i(
|
|
244
|
+
"AmplyReactNative",
|
|
245
|
+
"System event ${event.name} ts=${event.timestamp} props=${event.properties.keys}"
|
|
246
|
+
)
|
|
247
|
+
val envelope = event.toEventEnvelope()
|
|
248
|
+
if (!_systemEvents.tryEmit(envelope)) {
|
|
249
|
+
android.util.Log.w(
|
|
250
|
+
"AmplyReactNative",
|
|
251
|
+
"Dropping system event due to backpressure name=${event.name}"
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private fun maybePrimeSessionTracker() {
|
|
259
|
+
if (sessionPrimed.get()) {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
val instance = amplyInstance ?: return
|
|
263
|
+
val activity = lastResumedActivity.get()?.get() ?: return
|
|
264
|
+
if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
265
|
+
mainHandler.post { maybePrimeSessionTracker() }
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
val coreField = Amply::class.java.getDeclaredField("amplyCore").apply {
|
|
271
|
+
isAccessible = true
|
|
272
|
+
}
|
|
273
|
+
val core = coreField.get(instance) as? AmplySDKInterface ?: return
|
|
274
|
+
val sessionManager = core.getSessionManager()
|
|
275
|
+
val trackerField = sessionManager.javaClass.getDeclaredField("sessionTracker").apply {
|
|
276
|
+
isAccessible = true
|
|
277
|
+
}
|
|
278
|
+
val sessionTracker = trackerField.get(sessionManager)
|
|
279
|
+
if (sessionTracker is Application.ActivityLifecycleCallbacks) {
|
|
280
|
+
sessionTracker.onActivityCreated(activity, null)
|
|
281
|
+
sessionTracker.onActivityStarted(activity)
|
|
282
|
+
sessionTracker.onActivityResumed(activity)
|
|
283
|
+
sessionPrimed.set(true)
|
|
284
|
+
android.util.Log.i(
|
|
285
|
+
"AmplyReactNative",
|
|
286
|
+
"Primed Amply session tracker with activity=${activity::class.java.simpleName}"
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
} catch (error: Throwable) {
|
|
290
|
+
android.util.Log.w(
|
|
291
|
+
"AmplyReactNative",
|
|
292
|
+
"Unable to prime Amply session tracker: ${error.message}"
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
package tools.amply.sdk.reactnative.model
|
|
2
|
+
|
|
3
|
+
data class AmplyInitializationOptions(
|
|
4
|
+
val appId: String,
|
|
5
|
+
val apiKeyPublic: String,
|
|
6
|
+
val apiKeySecret: String?,
|
|
7
|
+
val endpoint: String?,
|
|
8
|
+
val datasetPrefetch: List<DataSetType>?,
|
|
9
|
+
val defaultConfig: String?,
|
|
10
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package tools.amply.sdk.reactnative.model
|
|
2
|
+
|
|
3
|
+
sealed interface DataSetType {
|
|
4
|
+
data object Device : DataSetType
|
|
5
|
+
data object User : DataSetType
|
|
6
|
+
data object Session : DataSetType
|
|
7
|
+
|
|
8
|
+
data class TriggeredEvent(
|
|
9
|
+
val countStrategy: CountStrategy,
|
|
10
|
+
val params: List<EventParam>,
|
|
11
|
+
val eventName: String?
|
|
12
|
+
) : DataSetType {
|
|
13
|
+
enum class CountStrategy(val wireName: String) {
|
|
14
|
+
TOTAL("total"),
|
|
15
|
+
SESSION("session"),
|
|
16
|
+
USER("user");
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
fun fromWireName(name: String?): CountStrategy? = entries.firstOrNull { it.wireName == name }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
data class Events(val events: List<Event>) : DataSetType {
|
|
25
|
+
data class Event(
|
|
26
|
+
val name: String,
|
|
27
|
+
val type: EventType,
|
|
28
|
+
val params: List<EventParam>
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
enum class EventType(val wireName: String) {
|
|
32
|
+
CUSTOM("custom"),
|
|
33
|
+
SYSTEM("system");
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
fun fromWireName(name: String?): EventType? = entries.firstOrNull { it.wireName == name }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
data class EventParam(val name: String, val value: Any?)
|
|
42
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package tools.amply.sdk.reactnative.model
|
|
2
|
+
|
|
3
|
+
import tools.amply.sdk.datasets.DataSetType as NativeDataSetType
|
|
4
|
+
import tools.amply.sdk.events.EventType as NativeEventType
|
|
5
|
+
|
|
6
|
+
fun DataSetType.toNativeDataSetType(): NativeDataSetType = when (this) {
|
|
7
|
+
DataSetType.Device -> NativeDataSetType.Device
|
|
8
|
+
DataSetType.User -> NativeDataSetType.User
|
|
9
|
+
DataSetType.Session -> NativeDataSetType.Session
|
|
10
|
+
is DataSetType.TriggeredEvent ->
|
|
11
|
+
NativeDataSetType.TriggeredEvent(
|
|
12
|
+
countStrategy = this.countStrategy.toNativeCountStrategy(),
|
|
13
|
+
params = params.mapNotNull { it.toNativeParamOrNull() },
|
|
14
|
+
eventName = eventName
|
|
15
|
+
)
|
|
16
|
+
is DataSetType.Events -> NativeDataSetType.Events(events.map { it.toNativeEventOrNull() }.filterNotNull())
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private fun DataSetType.TriggeredEvent.CountStrategy.toNativeCountStrategy(): NativeDataSetType.TriggeredEvent.CountStrategy {
|
|
20
|
+
return NativeDataSetType.TriggeredEvent.CountStrategy.values().firstOrNull {
|
|
21
|
+
it.name.equals(name, ignoreCase = true)
|
|
22
|
+
} ?: throw IllegalArgumentException("Unsupported TriggeredEvent count strategy: $name")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private fun DataSetType.EventParam.toNativeParamOrNull(): NativeDataSetType.EventParam? =
|
|
26
|
+
value?.let { NativeDataSetType.EventParam(name, it) }
|
|
27
|
+
|
|
28
|
+
private fun DataSetType.Events.Event.toNativeEventOrNull(): NativeDataSetType.Events.Event? {
|
|
29
|
+
val transformedParams = params.mapNotNull { it.toNativeParamOrNull() }
|
|
30
|
+
if (transformedParams.size != params.size) {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
return NativeDataSetType.Events.Event(
|
|
34
|
+
name = name,
|
|
35
|
+
type = NativeEventType.valueOf(type.name.uppercase()),
|
|
36
|
+
params = transformedParams
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#include <fbjni/fbjni.h>
|
|
2
|
+
#include <react/newarchdefaults/DefaultTurboModuleManagerDelegate.h>
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
#include "AmplyReactNative.h"
|
|
7
|
+
|
|
8
|
+
using namespace facebook;
|
|
9
|
+
|
|
10
|
+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
|
11
|
+
return jni::initialize(vm, [] {
|
|
12
|
+
const auto previousProvider =
|
|
13
|
+
react::DefaultTurboModuleManagerDelegate::javaModuleProvider;
|
|
14
|
+
|
|
15
|
+
react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
|
|
16
|
+
[previousProvider](
|
|
17
|
+
const std::string &name,
|
|
18
|
+
const react::JavaTurboModule::InitParams ¶ms)
|
|
19
|
+
-> std::shared_ptr<react::TurboModule> {
|
|
20
|
+
if (auto module = react::AmplyReactNative_ModuleProvider(name, params)) {
|
|
21
|
+
return module;
|
|
22
|
+
}
|
|
23
|
+
if (previousProvider) {
|
|
24
|
+
return previousProvider(name, params);
|
|
25
|
+
}
|
|
26
|
+
return nullptr;
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.13)
|
|
2
|
+
|
|
3
|
+
project(AmplyReactNative LANGUAGES CXX)
|
|
4
|
+
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE on)
|
|
6
|
+
|
|
7
|
+
# React Native dir передаётся Gradle
|
|
8
|
+
set(REACT_SRC_ROOT "${REACT_NATIVE_DIR}")
|
|
9
|
+
set(REACT_COMMON_DIR "${REACT_SRC_ROOT}/ReactCommon")
|
|
10
|
+
set(REACT_ANDROID_SRC_DIR "${REACT_SRC_ROOT}/ReactAndroid/src/main/jni")
|
|
11
|
+
set(REACT_JAVA_TURBO_DIR "${REACT_COMMON_DIR}/react/nativemodule/core/platform/android")
|
|
12
|
+
set(REACT_CALL_INVOKER_DIR "${REACT_COMMON_DIR}/callinvoker")
|
|
13
|
+
set(REACT_TURBO_CORE_DIR "${REACT_COMMON_DIR}/react/nativemodule/core")
|
|
14
|
+
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
|
|
15
|
+
find_package(ReactAndroid REQUIRED CONFIG)
|
|
16
|
+
find_package(fbjni REQUIRED CONFIG)
|
|
17
|
+
|
|
18
|
+
# Add codegen subdirectory (this will define react_codegen_AmplyReactNative target)
|
|
19
|
+
add_subdirectory(
|
|
20
|
+
${CMAKE_CURRENT_SOURCE_DIR}/../../newarch/jni
|
|
21
|
+
${CMAKE_CURRENT_BINARY_DIR}/codegen
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Now that targets are defined, propagate includes to the codegen target
|
|
25
|
+
# This ensures OBJECT libraries can access all necessary headers (including folly)
|
|
26
|
+
get_target_property(FBJNI_IFACE_INCLUDES fbjni::fbjni INTERFACE_INCLUDE_DIRECTORIES)
|
|
27
|
+
if(FBJNI_IFACE_INCLUDES)
|
|
28
|
+
target_include_directories(react_codegen_AmplyReactNative PUBLIC ${FBJNI_IFACE_INCLUDES})
|
|
29
|
+
endif()
|
|
30
|
+
|
|
31
|
+
# Get includes from ReactAndroid::reactnative target (includes JSI, folly, etc.)
|
|
32
|
+
# These includes contain folly/dynamic.h and other necessary React Native headers
|
|
33
|
+
get_target_property(REACTNATIVE_IFACE_INCLUDES ReactAndroid::reactnative INTERFACE_INCLUDE_DIRECTORIES)
|
|
34
|
+
if(REACTNATIVE_IFACE_INCLUDES)
|
|
35
|
+
target_include_directories(react_codegen_AmplyReactNative PUBLIC ${REACTNATIVE_IFACE_INCLUDES})
|
|
36
|
+
endif()
|
|
37
|
+
|
|
38
|
+
target_include_directories(
|
|
39
|
+
react_codegen_AmplyReactNative
|
|
40
|
+
PUBLIC
|
|
41
|
+
${REACT_SRC_ROOT}
|
|
42
|
+
${REACT_ANDROID_SRC_DIR}
|
|
43
|
+
${REACT_COMMON_DIR}
|
|
44
|
+
${REACT_CALL_INVOKER_DIR}
|
|
45
|
+
${REACT_TURBO_CORE_DIR}
|
|
46
|
+
${REACT_JAVA_TURBO_DIR}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
add_library(
|
|
50
|
+
AmplyReactNative
|
|
51
|
+
SHARED
|
|
52
|
+
AmplyTurboModule.cpp
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
target_include_directories(
|
|
56
|
+
AmplyReactNative
|
|
57
|
+
PUBLIC
|
|
58
|
+
${CMAKE_CURRENT_SOURCE_DIR}
|
|
59
|
+
${CMAKE_CURRENT_SOURCE_DIR}/../../newarch/jni
|
|
60
|
+
${REACT_ANDROID_SRC_DIR}
|
|
61
|
+
${REACT_SRC_ROOT}
|
|
62
|
+
${REACT_COMMON_DIR}
|
|
63
|
+
${REACT_CALL_INVOKER_DIR}
|
|
64
|
+
${REACT_TURBO_CORE_DIR}
|
|
65
|
+
${REACT_JAVA_TURBO_DIR}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
target_link_libraries(
|
|
69
|
+
AmplyReactNative
|
|
70
|
+
react_codegen_AmplyReactNative # важно
|
|
71
|
+
ReactAndroid::reactnative
|
|
72
|
+
ReactAndroid::jsi
|
|
73
|
+
fbjni::fbjni
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
target_compile_reactnative_options(AmplyReactNative PRIVATE)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateModuleJavaSpec.js
|
|
9
|
+
*
|
|
10
|
+
* @nolint
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package tools.amply.sdk.reactnative;
|
|
14
|
+
|
|
15
|
+
import com.facebook.proguard.annotations.DoNotStrip;
|
|
16
|
+
import com.facebook.react.bridge.Promise;
|
|
17
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
18
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
19
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
20
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
21
|
+
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
|
22
|
+
import javax.annotation.Nonnull;
|
|
23
|
+
|
|
24
|
+
public abstract class NativeAmplyModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
|
25
|
+
public static final String NAME = "Amply";
|
|
26
|
+
|
|
27
|
+
public NativeAmplyModuleSpec(ReactApplicationContext reactContext) {
|
|
28
|
+
super(reactContext);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public @Nonnull String getName() {
|
|
33
|
+
return NAME;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected final void emitOnSystemEvent(ReadableMap value) {
|
|
37
|
+
mEventEmitterCallback.invoke("onSystemEvent", value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected final void emitOnDeepLink(ReadableMap value) {
|
|
41
|
+
mEventEmitterCallback.invoke("onDeepLink", value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@ReactMethod
|
|
45
|
+
@DoNotStrip
|
|
46
|
+
public abstract void initialize(ReadableMap config, Promise promise);
|
|
47
|
+
|
|
48
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
49
|
+
@DoNotStrip
|
|
50
|
+
public abstract boolean isInitialized();
|
|
51
|
+
|
|
52
|
+
@ReactMethod
|
|
53
|
+
@DoNotStrip
|
|
54
|
+
public abstract void track(ReadableMap payload, Promise promise);
|
|
55
|
+
|
|
56
|
+
@ReactMethod
|
|
57
|
+
@DoNotStrip
|
|
58
|
+
public abstract void getRecentEvents(double limit, Promise promise);
|
|
59
|
+
|
|
60
|
+
@ReactMethod
|
|
61
|
+
@DoNotStrip
|
|
62
|
+
public abstract void getDataSetSnapshot(ReadableMap type, Promise promise);
|
|
63
|
+
|
|
64
|
+
@ReactMethod
|
|
65
|
+
@DoNotStrip
|
|
66
|
+
public abstract void registerDeepLinkListener();
|
|
67
|
+
|
|
68
|
+
@ReactMethod
|
|
69
|
+
@DoNotStrip
|
|
70
|
+
public abstract void addListener(String eventName);
|
|
71
|
+
|
|
72
|
+
@ReactMethod
|
|
73
|
+
@DoNotStrip
|
|
74
|
+
public abstract void removeListeners(double count);
|
|
75
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateModuleJniCpp.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "AmplyReactNative.h"
|
|
12
|
+
|
|
13
|
+
namespace facebook::react {
|
|
14
|
+
|
|
15
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_initialize(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
16
|
+
static jmethodID cachedMethodId = nullptr;
|
|
17
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "initialize", "(Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_isInitialized(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
21
|
+
static jmethodID cachedMethodId = nullptr;
|
|
22
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, BooleanKind, "isInitialized", "()Z", args, count, cachedMethodId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_track(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
26
|
+
static jmethodID cachedMethodId = nullptr;
|
|
27
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "track", "(Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getRecentEvents(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
31
|
+
static jmethodID cachedMethodId = nullptr;
|
|
32
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getRecentEvents", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getDataSetSnapshot(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
36
|
+
static jmethodID cachedMethodId = nullptr;
|
|
37
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getDataSetSnapshot", "(Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_registerDeepLinkListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
41
|
+
static jmethodID cachedMethodId = nullptr;
|
|
42
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, VoidKind, "registerDeepLinkListener", "()V", args, count, cachedMethodId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
46
|
+
static jmethodID cachedMethodId = nullptr;
|
|
47
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, VoidKind, "addListener", "(Ljava/lang/String;)V", args, count, cachedMethodId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_removeListeners(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
51
|
+
static jmethodID cachedMethodId = nullptr;
|
|
52
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, VoidKind, "removeListeners", "(D)V", args, count, cachedMethodId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
NativeAmplyModuleSpecJSI::NativeAmplyModuleSpecJSI(const JavaTurboModule::InitParams ¶ms)
|
|
56
|
+
: JavaTurboModule(params) {
|
|
57
|
+
methodMap_["initialize"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_initialize};
|
|
58
|
+
methodMap_["isInitialized"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_isInitialized};
|
|
59
|
+
methodMap_["track"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_track};
|
|
60
|
+
methodMap_["getRecentEvents"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_getRecentEvents};
|
|
61
|
+
methodMap_["getDataSetSnapshot"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_getDataSetSnapshot};
|
|
62
|
+
methodMap_["registerDeepLinkListener"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_registerDeepLinkListener};
|
|
63
|
+
methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_addListener};
|
|
64
|
+
methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_removeListeners};
|
|
65
|
+
eventEmitterMap_["onSystemEvent"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
|
|
66
|
+
eventEmitterMap_["onDeepLink"] = std::make_shared<AsyncEventEmitter<folly::dynamic>>();
|
|
67
|
+
configureEventEmitterCallback();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
std::shared_ptr<TurboModule> AmplyReactNative_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) {
|
|
71
|
+
if (moduleName == "Amply") {
|
|
72
|
+
return std::make_shared<NativeAmplyModuleSpecJSI>(params);
|
|
73
|
+
}
|
|
74
|
+
return nullptr;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} // namespace facebook::react
|