cloudx-react-native 3.4.7 → 3.6.1

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.
Files changed (102) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/cloudxreactnative/CloudXAdEvents.kt +11 -5
  3. package/android/src/main/java/com/cloudxreactnative/CloudXAdInfo.kt +14 -0
  4. package/android/src/main/java/com/cloudxreactnative/CloudXAdView.kt +9 -15
  5. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewEvent.kt +1 -1
  6. package/android/src/main/java/com/cloudxreactnative/CloudXModuleImpl.kt +202 -0
  7. package/android/src/newarch/com/cloudxreactnative/CloudXModule.kt +7 -0
  8. package/android/src/oldarch/com/cloudxreactnative/CloudXModule.kt +7 -0
  9. package/cloudx-react-native.podspec +3 -3
  10. package/ios/CloudX.h +2 -2
  11. package/ios/CloudX.mm +284 -9
  12. package/ios/CloudXAdView.h +10 -12
  13. package/ios/CloudXAdView.mm +10 -15
  14. package/ios/CloudXAdViewManager.h +6 -6
  15. package/ios/CloudXAdViewManager.mm +4 -4
  16. package/lib/commonjs/CloudX.js +5 -1
  17. package/lib/commonjs/CloudX.js.map +1 -1
  18. package/lib/commonjs/CloudXAdViewBase.js +11 -9
  19. package/lib/commonjs/CloudXAdViewBase.js.map +1 -1
  20. package/lib/commonjs/CloudXAppOpenAd.js +98 -0
  21. package/lib/commonjs/CloudXAppOpenAd.js.map +1 -0
  22. package/lib/commonjs/CloudXBannerView.js +2 -2
  23. package/lib/commonjs/CloudXMRECView.js +2 -2
  24. package/lib/commonjs/hooks.js +81 -0
  25. package/lib/commonjs/hooks.js.map +1 -1
  26. package/lib/commonjs/index.js +37 -0
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js +1 -1
  29. package/lib/commonjs/specs/NativeCloudX.js.map +1 -1
  30. package/lib/commonjs/types/AppOpenAd.js +6 -0
  31. package/lib/commonjs/types/AppOpenAd.js.map +1 -0
  32. package/lib/commonjs/types/Arbiter.js +6 -0
  33. package/lib/commonjs/types/Arbiter.js.map +1 -1
  34. package/lib/commonjs/types/RevenueData.js +21 -0
  35. package/lib/commonjs/types/RevenueData.js.map +1 -0
  36. package/lib/commonjs/types/index.js +17 -0
  37. package/lib/commonjs/types/index.js.map +1 -1
  38. package/lib/commonjs/version.js +1 -1
  39. package/lib/module/CloudX.js +5 -1
  40. package/lib/module/CloudX.js.map +1 -1
  41. package/lib/module/CloudXAdViewBase.js +11 -9
  42. package/lib/module/CloudXAdViewBase.js.map +1 -1
  43. package/lib/module/CloudXAppOpenAd.js +91 -0
  44. package/lib/module/CloudXAppOpenAd.js.map +1 -0
  45. package/lib/module/CloudXBannerView.js +2 -2
  46. package/lib/module/CloudXMRECView.js +2 -2
  47. package/lib/module/hooks.js +80 -0
  48. package/lib/module/hooks.js.map +1 -1
  49. package/lib/module/index.js +7 -1
  50. package/lib/module/index.js.map +1 -1
  51. package/lib/module/specs/CloudXAdViewNativeComponent.js +1 -1
  52. package/lib/module/specs/NativeCloudX.js.map +1 -1
  53. package/lib/module/types/AppOpenAd.js +2 -0
  54. package/lib/module/types/AppOpenAd.js.map +1 -0
  55. package/lib/module/types/Arbiter.js +6 -0
  56. package/lib/module/types/Arbiter.js.map +1 -1
  57. package/lib/module/types/RevenueData.js +15 -0
  58. package/lib/module/types/RevenueData.js.map +1 -0
  59. package/lib/module/types/index.js +1 -1
  60. package/lib/module/types/index.js.map +1 -1
  61. package/lib/module/version.js +1 -1
  62. package/lib/typescript/src/CloudX.d.ts.map +1 -1
  63. package/lib/typescript/src/CloudXAdViewBase.d.ts +1 -1
  64. package/lib/typescript/src/CloudXAdViewBase.d.ts.map +1 -1
  65. package/lib/typescript/src/CloudXAppOpenAd.d.ts +4 -0
  66. package/lib/typescript/src/CloudXAppOpenAd.d.ts.map +1 -0
  67. package/lib/typescript/src/CloudXBannerView.d.ts +2 -2
  68. package/lib/typescript/src/CloudXMRECView.d.ts +2 -2
  69. package/lib/typescript/src/hooks.d.ts +14 -0
  70. package/lib/typescript/src/hooks.d.ts.map +1 -1
  71. package/lib/typescript/src/index.d.ts +6 -3
  72. package/lib/typescript/src/index.d.ts.map +1 -1
  73. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts +1 -1
  74. package/lib/typescript/src/specs/NativeCloudX.d.ts +13 -0
  75. package/lib/typescript/src/specs/NativeCloudX.d.ts.map +1 -1
  76. package/lib/typescript/src/types/AppOpenAd.d.ts +8 -0
  77. package/lib/typescript/src/types/AppOpenAd.d.ts.map +1 -0
  78. package/lib/typescript/src/types/Arbiter.d.ts +12 -2
  79. package/lib/typescript/src/types/Arbiter.d.ts.map +1 -1
  80. package/lib/typescript/src/types/CloudX.d.ts +6 -0
  81. package/lib/typescript/src/types/CloudX.d.ts.map +1 -1
  82. package/lib/typescript/src/types/RevenueData.d.ts +32 -0
  83. package/lib/typescript/src/types/RevenueData.d.ts.map +1 -0
  84. package/lib/typescript/src/types/index.d.ts +4 -1
  85. package/lib/typescript/src/types/index.d.ts.map +1 -1
  86. package/lib/typescript/src/version.d.ts +1 -1
  87. package/package.json +6 -2
  88. package/src/CloudX.ts +6 -0
  89. package/src/CloudXAdViewBase.tsx +12 -11
  90. package/src/CloudXAppOpenAd.ts +109 -0
  91. package/src/CloudXBannerView.tsx +2 -2
  92. package/src/CloudXMRECView.tsx +2 -2
  93. package/src/hooks.ts +103 -0
  94. package/src/index.ts +18 -2
  95. package/src/specs/CloudXAdViewNativeComponent.ts +1 -1
  96. package/src/specs/NativeCloudX.ts +22 -0
  97. package/src/types/AppOpenAd.ts +8 -0
  98. package/src/types/Arbiter.ts +20 -2
  99. package/src/types/CloudX.ts +7 -0
  100. package/src/types/RevenueData.ts +39 -0
  101. package/src/types/index.ts +4 -0
  102. package/src/version.ts +1 -1
@@ -72,5 +72,5 @@ repositories {
72
72
  dependencies {
73
73
  implementation "com.facebook.react:react-native:0.76.2"
74
74
 
75
- implementation "io.cloudx:sdk:4.1.7"
75
+ implementation "io.cloudx:sdk:4.4.0"
76
76
  }
@@ -17,11 +17,8 @@ object CloudXAdEvents {
17
17
  const val ON_MREC_AD_COLLAPSED_EVENT = "OnMRecAdCollapsedEvent"
18
18
  const val ON_MREC_AD_REVENUE_PAID = "OnMRecAdRevenuePaid"
19
19
 
20
- /*
21
- * In-tree Ad View component (CloudXBannerView / CloudXMRECView) per-view
22
- * direct events. These strings are both the event key and the JS prop
23
- * registrationName.
24
- */
20
+ // In-tree Ad View component (CloudXBannerView / CloudXMRECView) per-view direct events.
21
+ // These strings are both the event key and the JS prop registrationName.
25
22
  const val ON_AD_VIEW_LOADED_EVENT = "onAdLoadedEvent"
26
23
  const val ON_AD_VIEW_LOAD_FAILED_EVENT = "onAdLoadFailedEvent"
27
24
  const val ON_AD_VIEW_CLICKED_EVENT = "onAdClickedEvent"
@@ -36,6 +33,15 @@ object CloudXAdEvents {
36
33
  const val ON_INTERSTITIAL_HIDDEN_EVENT = "OnInterstitialHiddenEvent"
37
34
  const val ON_INTERSTITIAL_AD_REVENUE_PAID = "OnInterstitialAdRevenuePaid"
38
35
 
36
+ // App Open Events
37
+ const val ON_APPOPEN_LOADED_EVENT = "OnAppOpenLoadedEvent"
38
+ const val ON_APPOPEN_LOAD_FAILED_EVENT = "OnAppOpenLoadFailedEvent"
39
+ const val ON_APPOPEN_CLICKED_EVENT = "OnAppOpenClickedEvent"
40
+ const val ON_APPOPEN_DISPLAYED_EVENT = "OnAppOpenDisplayedEvent"
41
+ const val ON_APPOPEN_DISPLAY_FAILED_EVENT = "OnAppOpenDisplayFailedEvent"
42
+ const val ON_APPOPEN_HIDDEN_EVENT = "OnAppOpenHiddenEvent"
43
+ const val ON_APPOPEN_AD_REVENUE_PAID = "OnAppOpenAdRevenuePaid"
44
+
39
45
  // Rewarded Events
40
46
  const val ON_REWARDED_AD_LOADED_EVENT = "OnRewardedAdLoadedEvent"
41
47
  const val ON_REWARDED_AD_LOAD_FAILED_EVENT = "OnRewardedAdLoadFailedEvent"
@@ -39,3 +39,17 @@ fun getAdDisplayFailedInfo(ad: CloudXAd, error: CloudXError): WritableMap {
39
39
  map.putString("message", error.message)
40
40
  return map
41
41
  }
42
+
43
+ /**
44
+ * Synthetic display-failure payload for the no-current-activity show path.
45
+ * Mirrors the iOS no-view-controller payload ({adUnitId, code: -1, message})
46
+ * so JS can reset its isLoaded state instead of hanging on a show that
47
+ * silently never happened.
48
+ */
49
+ fun getNoActivityDisplayFailedInfo(adUnitId: String): WritableMap {
50
+ val map = Arguments.createMap()
51
+ map.putString("adUnitId", adUnitId)
52
+ map.putInt("code", -1)
53
+ map.putString("message", "No activity available to show the ad from")
54
+ return map
55
+ }
@@ -28,11 +28,9 @@ class CloudXAdView(private val reactContext: ThemedReactContext) : FrameLayout(r
28
28
  const val TAG = "CloudXAdView"
29
29
  }
30
30
 
31
- /*
32
- * adUnitId / adFormat are immutable once the ad view is created. Changing
33
- * them on a mounted view is ignored (with a warning) - remount via a React
34
- * key instead. The value check keeps benign same-value re-renders quiet.
35
- */
31
+ // adUnitId / adFormat are immutable once the ad view is created. Changing
32
+ // them on a mounted view is ignored (with a warning) — remount via a React
33
+ // key instead. The value check keeps benign same-value re-renders quiet.
36
34
  var adUnitId: String? = null
37
35
  set(value) {
38
36
  if (adView != null && value != field) {
@@ -80,7 +78,7 @@ class CloudXAdView(private val reactContext: ThemedReactContext) : FrameLayout(r
80
78
 
81
79
  /*
82
80
  * ReactViewGroup no-ops requestLayout, and getWidth()/getHeight() are 0 on
83
- * attach - re-run measure/layout on the next frame so the hosted ad renders.
81
+ * attach — re-run measure/layout on the next frame so the hosted ad renders.
84
82
  */
85
83
  override fun requestLayout() {
86
84
  super.requestLayout()
@@ -130,10 +128,8 @@ class CloudXAdView(private val reactContext: ThemedReactContext) : FrameLayout(r
130
128
  super.onDetachedFromWindow()
131
129
  adView?.stopAutoRefresh()
132
130
 
133
- /*
134
- * Defer one frame: a tab switch detaches but keeps the parent, a real
135
- * unmount removes it. Destroy only on a genuine unmount.
136
- */
131
+ // Defer one frame: a tab switch detaches but keeps the parent, a real
132
+ // unmount removes it. Destroy only on a genuine unmount.
137
133
  post {
138
134
  if (parent == null) {
139
135
  destroyAdView()
@@ -143,7 +139,7 @@ class CloudXAdView(private val reactContext: ThemedReactContext) : FrameLayout(r
143
139
  * unmount, so re-arm refresh. If the view is instead being
144
140
  * dropped, CloudXAdViewManagerImpl.onDropViewInstance calls
145
141
  * destroyAdView() first (nulling adView), so this stays a
146
- * null-safe no-op - refresh is never armed on a dead view.
142
+ * null-safe no-op — refresh is never armed on a dead view.
147
143
  */
148
144
  adView?.startAutoRefresh()
149
145
  }
@@ -233,10 +229,8 @@ class CloudXAdView(private val reactContext: ThemedReactContext) : FrameLayout(r
233
229
  private fun loadFailedPayload(failedAdUnitId: String, error: CloudXError): WritableMap {
234
230
  val map = Arguments.createMap()
235
231
  map.putString("adUnitId", failedAdUnitId)
236
- /*
237
- * Codegen spec declares `code` as Double; emit a double to match (and
238
- * to stay consistent with `revenue` and the iOS side).
239
- */
232
+ // Codegen spec declares `code` as Double; emit a double to match (and
233
+ // to stay consistent with `revenue` and the iOS side).
240
234
  map.putDouble("code", error.code.code.toDouble())
241
235
  map.putString("message", error.message)
242
236
  return map
@@ -21,7 +21,7 @@ class CloudXAdViewEvent(
21
21
  /*
22
22
  * Ad-lifecycle events are discrete and must never be dropped. The default
23
23
  * (true) lets the dispatcher coalesce events sharing the same
24
- * (eventName, viewId) within a batch, keeping only the latest - which would
24
+ * (eventName, viewId) within a batch, keeping only the latest — which would
25
25
  * silently drop, e.g., back-to-back revenue events on the same view.
26
26
  */
27
27
  override fun canCoalesce(): Boolean = false
@@ -26,13 +26,19 @@ import io.cloudx.sdk.CloudXAdViewListener
26
26
  import io.cloudx.sdk.CloudXArbiterBid
27
27
  import io.cloudx.sdk.CloudXArbiterConfiguration
28
28
  import io.cloudx.sdk.CloudXArbiterListener
29
+ import io.cloudx.sdk.CloudXArbiterPrecision
29
30
  import io.cloudx.sdk.CloudXArbiterResult
30
31
  import io.cloudx.sdk.CloudXError
31
32
  import io.cloudx.sdk.CloudXInitializationConfiguration
32
33
  import io.cloudx.sdk.CloudXInitializationListener
34
+ import io.cloudx.sdk.CloudXAppOpenAd
35
+ import io.cloudx.sdk.CloudXAppOpenListener
33
36
  import io.cloudx.sdk.CloudXInterstitialAd
34
37
  import io.cloudx.sdk.CloudXInterstitialListener
35
38
  import io.cloudx.sdk.CloudXLogLevel
39
+ import io.cloudx.sdk.CloudXRevenueData
40
+ import io.cloudx.sdk.CloudXRevenuePlatform
41
+ import io.cloudx.sdk.CloudXRevenuePrecision
36
42
  import io.cloudx.sdk.CloudXReward
37
43
  import io.cloudx.sdk.CloudXRewardedAd
38
44
  import io.cloudx.sdk.CloudXRewardedListener
@@ -47,6 +53,7 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
47
53
  }
48
54
 
49
55
  private val interstitials = ConcurrentHashMap<String, CloudXInterstitialAd>()
56
+ private val appOpenAds = ConcurrentHashMap<String, CloudXAppOpenAd>()
50
57
  private val rewardedAds = ConcurrentHashMap<String, CloudXRewardedAd>()
51
58
  private val adViews = ConcurrentHashMap<String, SdkAdView>()
52
59
  private val adViewAdFormats = ConcurrentHashMap<String, CloudXAdFormat>()
@@ -175,6 +182,11 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
175
182
 
176
183
  fun arbiter(configuration: ReadableMap, promise: Promise) {
177
184
  val bids = arbiterBids(configuration)
185
+ if (bids.isEmpty()) {
186
+ promise.resolve(emptyArbiterResult())
187
+ return
188
+ }
189
+
178
190
  val arbiterConfiguration = CloudXArbiterConfiguration.builder(bids).build()
179
191
  CloudX.arbiter(arbiterConfiguration, object : CloudXArbiterListener {
180
192
  override fun onCompleted(result: CloudXArbiterResult) {
@@ -183,6 +195,15 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
183
195
  })
184
196
  }
185
197
 
198
+ fun reportRevenueData(data: ReadableMap, promise: Promise) {
199
+ val revenueData = data.toRevenueData()
200
+ if (revenueData == null) {
201
+ promise.resolve(false)
202
+ return
203
+ }
204
+ promise.resolve(CloudX.reportRevenueData(revenueData))
205
+ }
206
+
186
207
  // =========================================================================
187
208
  // Banner (Programmatic)
188
209
  // =========================================================================
@@ -243,6 +264,10 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
243
264
  val activity = maybeGetCurrentActivity()
244
265
  if (activity == null) {
245
266
  Log.e(TAG, "showInterstitial: no activity available")
267
+ sendReactNativeEvent(
268
+ CloudXAdEvents.ON_INTERSTITIAL_DISPLAY_FAILED_EVENT,
269
+ getNoActivityDisplayFailedInfo(adUnitId),
270
+ )
246
271
  return@runOnUiQueueThread
247
272
  }
248
273
  interstitial.show(activity, placement, customData)
@@ -259,6 +284,50 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
259
284
  }
260
285
  }
261
286
 
287
+ // =========================================================================
288
+ // App Open
289
+ // =========================================================================
290
+
291
+ fun loadAppOpen(adUnitId: String) {
292
+ retrieveAppOpen(adUnitId).load()
293
+ }
294
+
295
+ fun setAppOpenExtraParameters(adUnitId: String, extraParameters: ReadableMap) {
296
+ CloudXExtraParameters.forEach(extraParameters) { key, value ->
297
+ retrieveAppOpen(adUnitId).setExtraParameter(key, value)
298
+ }
299
+ }
300
+
301
+ fun isAppOpenReady(adUnitId: String, promise: Promise) {
302
+ promise.resolve(retrieveAppOpen(adUnitId).isAdReady)
303
+ }
304
+
305
+ fun showAppOpen(adUnitId: String, placement: String?, customData: String?) {
306
+ reactApplicationContext.runOnUiQueueThread {
307
+ val appOpen = retrieveAppOpen(adUnitId)
308
+ val activity = maybeGetCurrentActivity()
309
+ if (activity == null) {
310
+ Log.e(TAG, "showAppOpen: no activity available")
311
+ sendReactNativeEvent(
312
+ CloudXAdEvents.ON_APPOPEN_DISPLAY_FAILED_EVENT,
313
+ getNoActivityDisplayFailedInfo(adUnitId),
314
+ )
315
+ return@runOnUiQueueThread
316
+ }
317
+ appOpen.show(activity, placement, customData)
318
+ }
319
+ }
320
+
321
+ fun destroyAppOpen(adUnitId: String) {
322
+ reactApplicationContext.runOnUiQueueThread {
323
+ appOpenAds.remove(adUnitId)?.let {
324
+ it.listener = null
325
+ it.revenueListener = null
326
+ it.destroy()
327
+ }
328
+ }
329
+ }
330
+
262
331
  // =========================================================================
263
332
  // Rewarded
264
333
  // =========================================================================
@@ -283,6 +352,10 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
283
352
  val activity = maybeGetCurrentActivity()
284
353
  if (activity == null) {
285
354
  Log.e(TAG, "showRewarded: no activity available")
355
+ sendReactNativeEvent(
356
+ CloudXAdEvents.ON_REWARDED_AD_DISPLAY_FAILED_EVENT,
357
+ getNoActivityDisplayFailedInfo(adUnitId),
358
+ )
286
359
  return@runOnUiQueueThread
287
360
  }
288
361
  rewarded.show(activity, placement, customData)
@@ -372,6 +445,29 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
372
445
  }
373
446
  }
374
447
 
448
+ private fun createAppOpenListener(adUnitId: String): CloudXAppOpenListener {
449
+ return object : CloudXAppOpenListener {
450
+ override fun onAdLoaded(cloudXAd: CloudXAd) {
451
+ sendReactNativeEvent(CloudXAdEvents.ON_APPOPEN_LOADED_EVENT, getAdInfo(cloudXAd))
452
+ }
453
+ override fun onAdLoadFailed(failedAdUnitId: String, cloudXError: CloudXError) {
454
+ sendReactNativeEvent(CloudXAdEvents.ON_APPOPEN_LOAD_FAILED_EVENT, getAdLoadFailedInfo(failedAdUnitId, cloudXError))
455
+ }
456
+ override fun onAdClicked(cloudXAd: CloudXAd) {
457
+ sendReactNativeEvent(CloudXAdEvents.ON_APPOPEN_CLICKED_EVENT, getAdInfo(cloudXAd))
458
+ }
459
+ override fun onAdDisplayed(cloudXAd: CloudXAd) {
460
+ sendReactNativeEvent(CloudXAdEvents.ON_APPOPEN_DISPLAYED_EVENT, getAdInfo(cloudXAd))
461
+ }
462
+ override fun onAdDisplayFailed(cloudXAd: CloudXAd, cloudXError: CloudXError) {
463
+ sendReactNativeEvent(CloudXAdEvents.ON_APPOPEN_DISPLAY_FAILED_EVENT, getAdDisplayFailedInfo(cloudXAd, cloudXError))
464
+ }
465
+ override fun onAdHidden(cloudXAd: CloudXAd) {
466
+ sendReactNativeEvent(CloudXAdEvents.ON_APPOPEN_HIDDEN_EVENT, getAdInfo(cloudXAd))
467
+ }
468
+ }
469
+ }
470
+
375
471
  private fun createRewardedListener(adUnitId: String): CloudXRewardedListener {
376
472
  return object : CloudXRewardedListener {
377
473
  override fun onAdLoaded(cloudXAd: CloudXAd) {
@@ -506,6 +602,7 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
506
602
  "CLOUDX" -> cloudXArbiterBid(index)
507
603
  "LEVELPLAY" -> levelPlayArbiterBid(index)
508
604
  "PUBMATIC" -> pubMaticArbiterBid(index)
605
+ "CUSTOM" -> customArbiterBid(index)
509
606
  else -> {
510
607
  Log.w(TAG, "arbiter: ignoring bid with unsupported platform=$platform at index=$index")
511
608
  null
@@ -572,10 +669,81 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
572
669
  )
573
670
  }
574
671
 
672
+ private fun ReadableMap.customArbiterBid(index: Int): CloudXArbiterBid? {
673
+ val platformName = stringValue("platformName")
674
+ val revenuePerImpressionUSD = doubleValue("revenuePerImpressionUSD")
675
+ val precision = stringValue("precision")
676
+ if (platformName.isNullOrBlank() || revenuePerImpressionUSD == null || precision.isNullOrBlank()) {
677
+ Log.w(TAG, "arbiter: ignoring CUSTOM bid with missing required fields at index=$index")
678
+ return null
679
+ }
680
+
681
+ return CloudXArbiterBid.custom(
682
+ platformName = platformName,
683
+ networkName = optionalStringValue("networkName").orEmpty(),
684
+ revenuePerImpressionUSD = revenuePerImpressionUSD,
685
+ precision = CloudXArbiterPrecision.of(precision),
686
+ extras = stringMapValue("extras"),
687
+ )
688
+ }
689
+
690
+ private fun ReadableMap.toRevenueData(): CloudXRevenueData? {
691
+ val platform = stringValue("platform")
692
+ val revenuePlatform = platform?.toRevenuePlatformOrNull()
693
+ val revenue = doubleValue("revenue")
694
+ val adFormat = stringValue("adFormat")
695
+ if (revenuePlatform == null || revenue == null || adFormat.isNullOrBlank()) {
696
+ Log.w(TAG, "reportRevenueData: ignoring data with missing or invalid required fields")
697
+ return null
698
+ }
699
+ if (!revenue.isFinite()) {
700
+ Log.w(TAG, "reportRevenueData: ignoring data with non-finite revenue=$revenue")
701
+ return null
702
+ }
703
+
704
+ return CloudXRevenueData.builder(
705
+ revenuePlatform,
706
+ revenue,
707
+ adFormat,
708
+ )
709
+ .currencyCode(optionalStringValue("currencyCode"))
710
+ .precision(stringValue("precision")?.toRevenuePrecisionOrNull())
711
+ .networkName(optionalStringValue("networkName"))
712
+ .adUnitId(optionalStringValue("adUnitId"))
713
+ .thirdPartyAdPlacementId(optionalStringValue("thirdPartyAdPlacementId"))
714
+ .creativeId(optionalStringValue("creativeId"))
715
+ .networkPlacement(optionalStringValue("networkPlacement"))
716
+ .countryCode(optionalStringValue("countryCode"))
717
+ .userSegment(optionalStringValue("userSegment"))
718
+ .build()
719
+ }
720
+
721
+ private fun String.toRevenuePlatformOrNull(): CloudXRevenuePlatform? {
722
+ val platform = trim()
723
+ if (platform.isBlank()) return null
724
+ return when {
725
+ platform.equals("AdMob", ignoreCase = true) -> CloudXRevenuePlatform.ADMOB
726
+ platform.equals("InMobi", ignoreCase = true) -> CloudXRevenuePlatform.INMOBI
727
+ else -> CloudXRevenuePlatform.custom(platform)
728
+ }
729
+ }
730
+
731
+ private fun String.toRevenuePrecisionOrNull(): CloudXRevenuePrecision? = when (lowercase(Locale.ROOT)) {
732
+ "exact" -> CloudXRevenuePrecision.EXACT
733
+ "estimated" -> CloudXRevenuePrecision.ESTIMATED
734
+ "publisher_defined" -> CloudXRevenuePrecision.PUBLISHER_DEFINED
735
+ "undefined" -> CloudXRevenuePrecision.UNDEFINED
736
+ else -> {
737
+ Log.w(TAG, "reportRevenueData: ignoring unknown precision=$this")
738
+ null
739
+ }
740
+ }
741
+
575
742
  private fun String.toCloudXAdFormatOrNull(): CloudXAdFormat? = when (uppercase(Locale.ROOT)) {
576
743
  CloudXAdFormat.BANNER.name -> CloudXAdFormat.BANNER
577
744
  CloudXAdFormat.MREC.name -> CloudXAdFormat.MREC
578
745
  CloudXAdFormat.INTERSTITIAL.name -> CloudXAdFormat.INTERSTITIAL
746
+ CloudXAdFormat.APP_OPEN.name -> CloudXAdFormat.APP_OPEN
579
747
  CloudXAdFormat.REWARDED.name -> CloudXAdFormat.REWARDED
580
748
  CloudXAdFormat.NATIVE.name -> CloudXAdFormat.NATIVE
581
749
  else -> null
@@ -606,10 +774,21 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
606
774
  return result
607
775
  }
608
776
 
777
+ private fun emptyArbiterResult(): WritableMap {
778
+ val map = Arguments.createMap()
779
+ map.putString("id", "")
780
+ map.putString("platform", "NONE")
781
+ map.putString("platformName", "NONE")
782
+ map.putNull("bidId")
783
+ map.putMap("extras", Arguments.createMap())
784
+ return map
785
+ }
786
+
609
787
  private fun CloudXArbiterResult.toWritableMap(): WritableMap {
610
788
  val map = Arguments.createMap()
611
789
  map.putString("id", id)
612
790
  map.putString("platform", platform.name)
791
+ map.putString("platformName", platformName)
613
792
  map.putString("bidId", bidId)
614
793
  map.putMap("extras", extras.toWritableMap())
615
794
  return map
@@ -743,6 +922,17 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
743
922
  return interstitial
744
923
  }
745
924
 
925
+ private fun retrieveAppOpen(adUnitId: String): CloudXAppOpenAd {
926
+ var appOpen = appOpenAds[adUnitId]
927
+ if (appOpen == null) {
928
+ appOpen = CloudX.createAppOpen(reactApplicationContext, adUnitId)
929
+ appOpen.listener = createAppOpenListener(adUnitId)
930
+ appOpen.revenueListener = createFullscreenRevenueListener(adUnitId, CloudXAdEvents.ON_APPOPEN_AD_REVENUE_PAID)
931
+ appOpenAds[adUnitId] = appOpen
932
+ }
933
+ return appOpen
934
+ }
935
+
746
936
  private fun retrieveRewarded(adUnitId: String): CloudXRewardedAd {
747
937
  var rewarded = rewardedAds[adUnitId]
748
938
  if (rewarded == null) {
@@ -885,6 +1075,11 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
885
1075
  }
886
1076
  interstitials.clear()
887
1077
 
1078
+ for (appOpen in appOpenAds.values) {
1079
+ appOpen.destroy()
1080
+ }
1081
+ appOpenAds.clear()
1082
+
888
1083
  for (rewarded in rewardedAds.values) {
889
1084
  rewarded.destroy()
890
1085
  }
@@ -926,6 +1121,13 @@ class CloudXModuleImpl(private val reactApplicationContext: ReactApplicationCont
926
1121
  "ON_INTERSTITIAL_DISPLAY_FAILED_EVENT" to CloudXAdEvents.ON_INTERSTITIAL_DISPLAY_FAILED_EVENT,
927
1122
  "ON_INTERSTITIAL_HIDDEN_EVENT" to CloudXAdEvents.ON_INTERSTITIAL_HIDDEN_EVENT,
928
1123
  "ON_INTERSTITIAL_AD_REVENUE_PAID" to CloudXAdEvents.ON_INTERSTITIAL_AD_REVENUE_PAID,
1124
+ "ON_APPOPEN_LOADED_EVENT" to CloudXAdEvents.ON_APPOPEN_LOADED_EVENT,
1125
+ "ON_APPOPEN_LOAD_FAILED_EVENT" to CloudXAdEvents.ON_APPOPEN_LOAD_FAILED_EVENT,
1126
+ "ON_APPOPEN_CLICKED_EVENT" to CloudXAdEvents.ON_APPOPEN_CLICKED_EVENT,
1127
+ "ON_APPOPEN_DISPLAYED_EVENT" to CloudXAdEvents.ON_APPOPEN_DISPLAYED_EVENT,
1128
+ "ON_APPOPEN_DISPLAY_FAILED_EVENT" to CloudXAdEvents.ON_APPOPEN_DISPLAY_FAILED_EVENT,
1129
+ "ON_APPOPEN_HIDDEN_EVENT" to CloudXAdEvents.ON_APPOPEN_HIDDEN_EVENT,
1130
+ "ON_APPOPEN_AD_REVENUE_PAID" to CloudXAdEvents.ON_APPOPEN_AD_REVENUE_PAID,
929
1131
  "ON_REWARDED_AD_LOADED_EVENT" to CloudXAdEvents.ON_REWARDED_AD_LOADED_EVENT,
930
1132
  "ON_REWARDED_AD_LOAD_FAILED_EVENT" to CloudXAdEvents.ON_REWARDED_AD_LOAD_FAILED_EVENT,
931
1133
  "ON_REWARDED_AD_CLICKED_EVENT" to CloudXAdEvents.ON_REWARDED_AD_CLICKED_EVENT,
@@ -31,6 +31,7 @@ class CloudXModule(reactContext: ReactApplicationContext) :
31
31
  @ReactMethod override fun setHasUserConsent(hasUserConsent: Boolean?) = impl.setHasUserConsent(hasUserConsent)
32
32
  @ReactMethod override fun setDoNotSell(isDoNotSell: Boolean?) = impl.setDoNotSell(isDoNotSell)
33
33
  @ReactMethod override fun arbiter(configuration: ReadableMap, promise: Promise) = impl.arbiter(configuration, promise)
34
+ @ReactMethod override fun reportRevenueData(data: ReadableMap, promise: Promise) = impl.reportRevenueData(data, promise)
34
35
 
35
36
  @ReactMethod override fun createBanner(adUnitId: String, position: String) = impl.createBanner(adUnitId, position)
36
37
  @ReactMethod override fun createBannerWithOffsets(adUnitId: String, position: String, xOffset: Double, yOffset: Double) = impl.createBannerWithOffsets(adUnitId, position, xOffset, yOffset)
@@ -66,6 +67,12 @@ class CloudXModule(reactContext: ReactApplicationContext) :
66
67
  @ReactMethod override fun showInterstitial(adUnitId: String, placement: String?, customData: String?) = impl.showInterstitial(adUnitId, placement, customData)
67
68
  @ReactMethod override fun destroyInterstitial(adUnitId: String) = impl.destroyInterstitial(adUnitId)
68
69
 
70
+ @ReactMethod override fun loadAppOpen(adUnitId: String) = impl.loadAppOpen(adUnitId)
71
+ @ReactMethod override fun setAppOpenExtraParameters(adUnitId: String, extraParameters: ReadableMap) = impl.setAppOpenExtraParameters(adUnitId, extraParameters)
72
+ @ReactMethod override fun isAppOpenReady(adUnitId: String, promise: Promise) = impl.isAppOpenReady(adUnitId, promise)
73
+ @ReactMethod override fun showAppOpen(adUnitId: String, placement: String?, customData: String?) = impl.showAppOpen(adUnitId, placement, customData)
74
+ @ReactMethod override fun destroyAppOpen(adUnitId: String) = impl.destroyAppOpen(adUnitId)
75
+
69
76
  @ReactMethod override fun loadRewarded(adUnitId: String) = impl.loadRewarded(adUnitId)
70
77
  @ReactMethod override fun setRewardedExtraParameters(adUnitId: String, extraParameters: ReadableMap) = impl.setRewardedExtraParameters(adUnitId, extraParameters)
71
78
  @ReactMethod override fun isRewardedReady(adUnitId: String, promise: Promise) = impl.isRewardedReady(adUnitId, promise)
@@ -46,6 +46,7 @@ class CloudXModule(reactContext: ReactApplicationContext) :
46
46
  }
47
47
 
48
48
  @ReactMethod fun arbiter(configuration: ReadableMap, promise: Promise) = impl.arbiter(configuration, promise)
49
+ @ReactMethod fun reportRevenueData(data: ReadableMap, promise: Promise) = impl.reportRevenueData(data, promise)
49
50
 
50
51
  @ReactMethod fun createBanner(adUnitId: String, position: String) = impl.createBanner(adUnitId, position)
51
52
  @ReactMethod fun createBannerWithOffsets(adUnitId: String, position: String, xOffset: Double, yOffset: Double) = impl.createBannerWithOffsets(adUnitId, position, xOffset, yOffset)
@@ -81,6 +82,12 @@ class CloudXModule(reactContext: ReactApplicationContext) :
81
82
  @ReactMethod fun showInterstitial(adUnitId: String, placement: String?, customData: String?) = impl.showInterstitial(adUnitId, placement, customData)
82
83
  @ReactMethod fun destroyInterstitial(adUnitId: String) = impl.destroyInterstitial(adUnitId)
83
84
 
85
+ @ReactMethod fun loadAppOpen(adUnitId: String) = impl.loadAppOpen(adUnitId)
86
+ @ReactMethod fun setAppOpenExtraParameters(adUnitId: String, extraParameters: ReadableMap) = impl.setAppOpenExtraParameters(adUnitId, extraParameters)
87
+ @ReactMethod fun isAppOpenReady(adUnitId: String, promise: Promise) = impl.isAppOpenReady(adUnitId, promise)
88
+ @ReactMethod fun showAppOpen(adUnitId: String, placement: String?, customData: String?) = impl.showAppOpen(adUnitId, placement, customData)
89
+ @ReactMethod fun destroyAppOpen(adUnitId: String) = impl.destroyAppOpen(adUnitId)
90
+
84
91
  @ReactMethod fun loadRewarded(adUnitId: String) = impl.loadRewarded(adUnitId)
85
92
  @ReactMethod fun setRewardedExtraParameters(adUnitId: String, extraParameters: ReadableMap) = impl.setRewardedExtraParameters(adUnitId, extraParameters)
86
93
  @ReactMethod fun isRewardedReady(adUnitId: String, promise: Promise) = impl.isRewardedReady(adUnitId, promise)
@@ -6,15 +6,15 @@ Pod::Spec.new do |s|
6
6
  s.name = "cloudx-react-native"
7
7
  s.version = package["version"]
8
8
  s.summary = package["description"]
9
- s.homepage = "https://github.com/cloudx-io/cloudx-react-native"
9
+ s.homepage = "https://github.com/cloudx-io/mobile/tree/main/react-native"
10
10
  s.license = package["license"]
11
11
  s.authors = { "CloudX" => "support@cloudx.io" }
12
12
  s.platforms = { :ios => "13.0" }
13
- s.source = { :git => "https://github.com/cloudx-io/cloudx-react-native.git", :tag => "#{s.version}" }
13
+ s.source = { :git => "https://github.com/cloudx-io/mobile.git", :tag => "react-native/sdk/#{s.version}" }
14
14
 
15
15
  s.requires_arc = true
16
16
 
17
- s.dependency "CloudXCore", "~> 3.4.5"
17
+ s.dependency "CloudXCore", "~> 3.6.0"
18
18
 
19
19
  s.source_files = "ios/**/*.{h,mm}"
20
20
 
package/ios/CloudX.h CHANGED
@@ -11,10 +11,10 @@
11
11
 
12
12
  #ifdef RCT_NEW_ARCH_ENABLED
13
13
  #import <CloudXSpec/CloudXSpec.h>
14
- @interface CloudX : RCTEventEmitter<NativeCloudXSpec, CLXInterstitialDelegate, CLXRewardedDelegate, CLXBannerDelegate, CLXAdRevenueDelegate>
14
+ @interface CloudX : RCTEventEmitter<NativeCloudXSpec, CLXInterstitialDelegate, CLXAppOpenDelegate, CLXRewardedDelegate, CLXBannerDelegate, CLXAdRevenueDelegate>
15
15
  #else
16
16
  #import <React/RCTBridgeModule.h>
17
- @interface CloudX : RCTEventEmitter<RCTBridgeModule, CLXInterstitialDelegate, CLXRewardedDelegate, CLXBannerDelegate, CLXAdRevenueDelegate>
17
+ @interface CloudX : RCTEventEmitter<RCTBridgeModule, CLXInterstitialDelegate, CLXAppOpenDelegate, CLXRewardedDelegate, CLXBannerDelegate, CLXAdRevenueDelegate>
18
18
  #endif
19
19
 
20
20
  NS_ASSUME_NONNULL_BEGIN