cloudx-react-native 3.4.6 → 3.6.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.
Files changed (114) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/cloudxreactnative/CloudXAdEvents.kt +16 -0
  3. package/android/src/main/java/com/cloudxreactnative/CloudXAdInfo.kt +14 -0
  4. package/android/src/main/java/com/cloudxreactnative/CloudXAdView.kt +238 -0
  5. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewEvent.kt +28 -0
  6. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewManagerImpl.kt +54 -0
  7. package/android/src/main/java/com/cloudxreactnative/CloudXExtraParameters.kt +101 -0
  8. package/android/src/main/java/com/cloudxreactnative/CloudXModuleImpl.kt +171 -57
  9. package/android/src/main/java/com/cloudxreactnative/CloudXPackage.kt +1 -1
  10. package/android/src/newarch/com/cloudxreactnative/CloudXAdViewManager.kt +52 -0
  11. package/android/src/newarch/com/cloudxreactnative/CloudXModule.kt +7 -0
  12. package/android/src/oldarch/com/cloudxreactnative/CloudXAdViewManager.kt +46 -0
  13. package/android/src/oldarch/com/cloudxreactnative/CloudXModule.kt +7 -0
  14. package/cloudx-react-native.podspec +3 -3
  15. package/ios/CloudX.h +2 -2
  16. package/ios/CloudX.mm +268 -62
  17. package/ios/CloudXAdView.h +33 -0
  18. package/ios/CloudXAdView.mm +500 -0
  19. package/ios/CloudXAdViewManager.h +12 -0
  20. package/ios/CloudXAdViewManager.mm +38 -0
  21. package/ios/CloudXExtraParameters.h +27 -0
  22. package/ios/CloudXExtraParameters.mm +117 -0
  23. package/lib/commonjs/CloudX.js +5 -1
  24. package/lib/commonjs/CloudX.js.map +1 -1
  25. package/lib/commonjs/CloudXAdViewBase.js +250 -0
  26. package/lib/commonjs/CloudXAdViewBase.js.map +1 -0
  27. package/lib/commonjs/CloudXAppOpenAd.js +98 -0
  28. package/lib/commonjs/CloudXAppOpenAd.js.map +1 -0
  29. package/lib/commonjs/CloudXBannerView.js +27 -0
  30. package/lib/commonjs/CloudXBannerView.js.map +1 -0
  31. package/lib/commonjs/CloudXMRECView.js +27 -0
  32. package/lib/commonjs/CloudXMRECView.js.map +1 -0
  33. package/lib/commonjs/hooks.js +81 -0
  34. package/lib/commonjs/hooks.js.map +1 -1
  35. package/lib/commonjs/index.js +53 -0
  36. package/lib/commonjs/index.js.map +1 -1
  37. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js +30 -0
  38. package/lib/commonjs/specs/CloudXAdViewNativeComponent.js.map +1 -0
  39. package/lib/commonjs/specs/NativeCloudX.js.map +1 -1
  40. package/lib/commonjs/types/AppOpenAd.js +6 -0
  41. package/lib/commonjs/types/AppOpenAd.js.map +1 -0
  42. package/lib/commonjs/types/RevenueData.js +21 -0
  43. package/lib/commonjs/types/RevenueData.js.map +1 -0
  44. package/lib/commonjs/types/index.js +17 -0
  45. package/lib/commonjs/types/index.js.map +1 -1
  46. package/lib/commonjs/version.js +1 -1
  47. package/lib/module/CloudX.js +5 -1
  48. package/lib/module/CloudX.js.map +1 -1
  49. package/lib/module/CloudXAdViewBase.js +242 -0
  50. package/lib/module/CloudXAdViewBase.js.map +1 -0
  51. package/lib/module/CloudXAppOpenAd.js +91 -0
  52. package/lib/module/CloudXAppOpenAd.js.map +1 -0
  53. package/lib/module/CloudXBannerView.js +19 -0
  54. package/lib/module/CloudXBannerView.js.map +1 -0
  55. package/lib/module/CloudXMRECView.js +19 -0
  56. package/lib/module/CloudXMRECView.js.map +1 -0
  57. package/lib/module/hooks.js +80 -0
  58. package/lib/module/hooks.js.map +1 -1
  59. package/lib/module/index.js +10 -1
  60. package/lib/module/index.js.map +1 -1
  61. package/lib/module/specs/CloudXAdViewNativeComponent.js +27 -0
  62. package/lib/module/specs/CloudXAdViewNativeComponent.js.map +1 -0
  63. package/lib/module/specs/NativeCloudX.js.map +1 -1
  64. package/lib/module/types/AppOpenAd.js +2 -0
  65. package/lib/module/types/AppOpenAd.js.map +1 -0
  66. package/lib/module/types/RevenueData.js +15 -0
  67. package/lib/module/types/RevenueData.js.map +1 -0
  68. package/lib/module/types/index.js +1 -1
  69. package/lib/module/types/index.js.map +1 -1
  70. package/lib/module/version.js +1 -1
  71. package/lib/typescript/src/CloudX.d.ts.map +1 -1
  72. package/lib/typescript/src/CloudXAdViewBase.d.ts +28 -0
  73. package/lib/typescript/src/CloudXAdViewBase.d.ts.map +1 -0
  74. package/lib/typescript/src/CloudXAppOpenAd.d.ts +4 -0
  75. package/lib/typescript/src/CloudXAppOpenAd.d.ts.map +1 -0
  76. package/lib/typescript/src/CloudXBannerView.d.ts +16 -0
  77. package/lib/typescript/src/CloudXBannerView.d.ts.map +1 -0
  78. package/lib/typescript/src/CloudXMRECView.d.ts +16 -0
  79. package/lib/typescript/src/CloudXMRECView.d.ts.map +1 -0
  80. package/lib/typescript/src/hooks.d.ts +14 -0
  81. package/lib/typescript/src/hooks.d.ts.map +1 -1
  82. package/lib/typescript/src/index.d.ts +9 -3
  83. package/lib/typescript/src/index.d.ts.map +1 -1
  84. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts +62 -0
  85. package/lib/typescript/src/specs/CloudXAdViewNativeComponent.d.ts.map +1 -0
  86. package/lib/typescript/src/specs/NativeCloudX.d.ts +13 -0
  87. package/lib/typescript/src/specs/NativeCloudX.d.ts.map +1 -1
  88. package/lib/typescript/src/types/AdProps.d.ts +3 -0
  89. package/lib/typescript/src/types/AdProps.d.ts.map +1 -1
  90. package/lib/typescript/src/types/AppOpenAd.d.ts +8 -0
  91. package/lib/typescript/src/types/AppOpenAd.d.ts.map +1 -0
  92. package/lib/typescript/src/types/CloudX.d.ts +6 -0
  93. package/lib/typescript/src/types/CloudX.d.ts.map +1 -1
  94. package/lib/typescript/src/types/RevenueData.d.ts +32 -0
  95. package/lib/typescript/src/types/RevenueData.d.ts.map +1 -0
  96. package/lib/typescript/src/types/index.d.ts +3 -0
  97. package/lib/typescript/src/types/index.d.ts.map +1 -1
  98. package/lib/typescript/src/version.d.ts +1 -1
  99. package/package.json +6 -2
  100. package/src/CloudX.ts +6 -0
  101. package/src/CloudXAdViewBase.tsx +294 -0
  102. package/src/CloudXAppOpenAd.ts +109 -0
  103. package/src/CloudXBannerView.tsx +20 -0
  104. package/src/CloudXMRECView.tsx +20 -0
  105. package/src/hooks.ts +103 -0
  106. package/src/index.ts +22 -2
  107. package/src/specs/CloudXAdViewNativeComponent.ts +84 -0
  108. package/src/specs/NativeCloudX.ts +22 -0
  109. package/src/types/AdProps.ts +4 -0
  110. package/src/types/AppOpenAd.ts +8 -0
  111. package/src/types/CloudX.ts +7 -0
  112. package/src/types/RevenueData.ts +39 -0
  113. package/src/types/index.ts +3 -0
  114. 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.5"
75
+ implementation "io.cloudx:sdk:4.4.0"
76
76
  }
@@ -17,6 +17,13 @@ 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
+ // In-tree Ad View component (CloudXBannerView / CloudXMRECView) per-view direct events.
21
+ // These strings are both the event key and the JS prop registrationName.
22
+ const val ON_AD_VIEW_LOADED_EVENT = "onAdLoadedEvent"
23
+ const val ON_AD_VIEW_LOAD_FAILED_EVENT = "onAdLoadFailedEvent"
24
+ const val ON_AD_VIEW_CLICKED_EVENT = "onAdClickedEvent"
25
+ const val ON_AD_VIEW_REVENUE_PAID_EVENT = "onAdRevenuePaidEvent"
26
+
20
27
  // Interstitial Events
21
28
  const val ON_INTERSTITIAL_LOADED_EVENT = "OnInterstitialLoadedEvent"
22
29
  const val ON_INTERSTITIAL_LOAD_FAILED_EVENT = "OnInterstitialLoadFailedEvent"
@@ -26,6 +33,15 @@ object CloudXAdEvents {
26
33
  const val ON_INTERSTITIAL_HIDDEN_EVENT = "OnInterstitialHiddenEvent"
27
34
  const val ON_INTERSTITIAL_AD_REVENUE_PAID = "OnInterstitialAdRevenuePaid"
28
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
+
29
45
  // Rewarded Events
30
46
  const val ON_REWARDED_AD_LOADED_EVENT = "OnRewardedAdLoadedEvent"
31
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
+ }
@@ -0,0 +1,238 @@
1
+ package com.cloudxreactnative
2
+
3
+ import android.util.Log
4
+ import android.widget.FrameLayout
5
+ import com.facebook.react.bridge.Arguments
6
+ import com.facebook.react.bridge.WritableMap
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.facebook.react.uimanager.UIManagerHelper
9
+ import io.cloudx.sdk.CloudX
10
+ import io.cloudx.sdk.CloudXAd
11
+ import io.cloudx.sdk.CloudXAdRevenueListener
12
+ import io.cloudx.sdk.CloudXAdView as SdkAdView
13
+ import io.cloudx.sdk.CloudXAdViewListener
14
+ import io.cloudx.sdk.CloudXError
15
+ import org.json.JSONObject
16
+
17
+ /**
18
+ * In-tree banner/MREC view component.
19
+ *
20
+ * Extends [FrameLayout] (not ReactViewGroup, whose requestLayout is a no-op that
21
+ * would leave the hosted ad view at 0x0) and hosts the CloudX SDK ad view as a
22
+ * real child. Because it lives in the React view tree, app overlays such as
23
+ * toasts layer above it via normal RN z-ordering.
24
+ */
25
+ class CloudXAdView(private val reactContext: ThemedReactContext) : FrameLayout(reactContext) {
26
+
27
+ private companion object {
28
+ const val TAG = "CloudXAdView"
29
+ }
30
+
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.
34
+ var adUnitId: String? = null
35
+ set(value) {
36
+ if (adView != null && value != field) {
37
+ Log.w(TAG, "adUnitId cannot change after the ad view is created; remount the component (e.g. via a React key).")
38
+ return
39
+ }
40
+ field = value
41
+ }
42
+ var adFormat: String? = null
43
+ set(value) {
44
+ if (adView != null && value != field) {
45
+ Log.w(TAG, "adFormat cannot change after the ad view is created; remount the component (e.g. via a React key).")
46
+ return
47
+ }
48
+ field = value
49
+ }
50
+ var placement: String? = null
51
+ set(value) {
52
+ field = value
53
+ adView?.setPlacement(value)
54
+ }
55
+ var customData: String? = null
56
+ set(value) {
57
+ field = value
58
+ adView?.setCustomData(value)
59
+ }
60
+
61
+ /** Latest declarative extra parameters from the RN component. */
62
+ var extraParameters: Map<String, Any?> = emptyMap()
63
+ set(value) {
64
+ field = value
65
+ applyExtraParametersIfReady()
66
+ }
67
+
68
+ private var adView: SdkAdView? = null
69
+ private var appliedExtraParameterKeys: Set<String> = emptySet()
70
+
71
+ private val measureAndLayoutRunnable = Runnable {
72
+ measure(
73
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
74
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
75
+ )
76
+ layout(left, top, right, bottom)
77
+ }
78
+
79
+ /*
80
+ * ReactViewGroup no-ops requestLayout, and getWidth()/getHeight() are 0 on
81
+ * attach — re-run measure/layout on the next frame so the hosted ad renders.
82
+ */
83
+ override fun requestLayout() {
84
+ super.requestLayout()
85
+ removeCallbacks(measureAndLayoutRunnable)
86
+ post(measureAndLayoutRunnable)
87
+ }
88
+
89
+ /** Invoked from the ViewManager's onAfterUpdateTransaction, after all props are set. */
90
+ fun onSetProps() {
91
+ maybeAttachAdView()
92
+ }
93
+
94
+ private fun maybeAttachAdView() {
95
+ if (adView != null) return
96
+ val unitId = adUnitId ?: return
97
+ val format = adFormat ?: return
98
+
99
+ val created = if (format.equals("MREC", ignoreCase = true)) {
100
+ CloudX.createMREC(reactContext, unitId)
101
+ } else {
102
+ CloudX.createBanner(reactContext, unitId)
103
+ }
104
+
105
+ created.listener = createListener()
106
+ created.revenueListener = createRevenueListener()
107
+ placement?.let { created.setPlacement(it) }
108
+ customData?.let { created.setCustomData(it) }
109
+
110
+ adView = created
111
+ applyExtraParametersIfReady()
112
+ addView(created)
113
+ created.load()
114
+ updateAutoRefresh()
115
+ }
116
+
117
+ private fun updateAutoRefresh() {
118
+ val view = adView ?: return
119
+ if (isAttachedToWindow) view.startAutoRefresh() else view.stopAutoRefresh()
120
+ }
121
+
122
+ override fun onAttachedToWindow() {
123
+ super.onAttachedToWindow()
124
+ adView?.startAutoRefresh()
125
+ }
126
+
127
+ override fun onDetachedFromWindow() {
128
+ super.onDetachedFromWindow()
129
+ adView?.stopAutoRefresh()
130
+
131
+ // Defer one frame: a tab switch detaches but keeps the parent, a real
132
+ // unmount removes it. Destroy only on a genuine unmount.
133
+ post {
134
+ if (parent == null) {
135
+ destroyAdView()
136
+ } else {
137
+ /*
138
+ * Still parented on the deferred frame = a tab switch, not an
139
+ * unmount, so re-arm refresh. If the view is instead being
140
+ * dropped, CloudXAdViewManagerImpl.onDropViewInstance calls
141
+ * destroyAdView() first (nulling adView), so this stays a
142
+ * null-safe no-op — refresh is never armed on a dead view.
143
+ */
144
+ adView?.startAutoRefresh()
145
+ }
146
+ }
147
+ }
148
+
149
+ fun destroyAdView() {
150
+ val view = adView ?: return
151
+ view.stopAutoRefresh()
152
+ view.listener = null
153
+ view.revenueListener = null
154
+ view.destroy()
155
+ removeView(view)
156
+ adView = null
157
+ appliedExtraParameterKeys = emptySet()
158
+ }
159
+
160
+ private fun applyExtraParametersIfReady() {
161
+ val view = adView ?: return
162
+ CloudXExtraParameters.applyToAdView(view, extraParameters)
163
+
164
+ val currentKeys = extraParameters.keys.toSet()
165
+ for (key in appliedExtraParameterKeys - currentKeys) {
166
+ view.setExtraParameter(key, null)
167
+ }
168
+ appliedExtraParameterKeys = currentKeys
169
+ }
170
+
171
+ private fun dispatchAdEvent(eventName: String, payload: WritableMap) {
172
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id) ?: return
173
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
174
+ dispatcher.dispatchEvent(CloudXAdViewEvent(surfaceId, id, eventName, payload))
175
+ }
176
+
177
+ private fun createListener(): CloudXAdViewListener = object : CloudXAdViewListener {
178
+ override fun onAdLoaded(cloudXAd: CloudXAd) {
179
+ dispatchAdEvent(CloudXAdEvents.ON_AD_VIEW_LOADED_EVENT, adInfoPayload(cloudXAd))
180
+ }
181
+
182
+ override fun onAdLoadFailed(failedAdUnitId: String, cloudXError: CloudXError) {
183
+ dispatchAdEvent(CloudXAdEvents.ON_AD_VIEW_LOAD_FAILED_EVENT, loadFailedPayload(failedAdUnitId, cloudXError))
184
+ }
185
+
186
+ override fun onAdClicked(cloudXAd: CloudXAd) {
187
+ dispatchAdEvent(CloudXAdEvents.ON_AD_VIEW_CLICKED_EVENT, adInfoPayload(cloudXAd))
188
+ }
189
+
190
+ override fun onAdExpanded(cloudXAd: CloudXAd) = Unit
191
+
192
+ override fun onAdCollapsed(cloudXAd: CloudXAd) = Unit
193
+ }
194
+
195
+ private fun createRevenueListener(): CloudXAdRevenueListener = object : CloudXAdRevenueListener {
196
+ override fun onAdRevenuePaid(cloudXAd: CloudXAd) {
197
+ dispatchAdEvent(CloudXAdEvents.ON_AD_VIEW_REVENUE_PAID_EVENT, adInfoPayload(cloudXAd))
198
+ }
199
+ }
200
+
201
+ /*
202
+ * Codegen cannot express Record<string,string>, so ad values are serialized
203
+ * to a JSON string here and re-parsed by the JS wrapper.
204
+ */
205
+ private fun adInfoPayload(ad: CloudXAd): WritableMap {
206
+ val map = Arguments.createMap()
207
+ map.putString("adUnitId", ad.adUnitId)
208
+ map.putString("adFormat", ad.adFormat.name)
209
+ map.putString("networkName", ad.networkName)
210
+ map.putString("networkPlacement", ad.networkPlacement ?: "")
211
+ map.putString("placement", ad.placement ?: "")
212
+ map.putDouble("revenue", ad.revenue)
213
+ /*
214
+ * The SDK types adValues as Map<String, String>, so the cast is safe
215
+ * today. Guard defensively anyway: an unexpected type or serialization
216
+ * failure here would otherwise crash the ad listener. Log rather than
217
+ * swallow silently so a genuine SDK contract change stays visible.
218
+ */
219
+ val adValuesJson = try {
220
+ JSONObject(ad.adValues as Map<*, *>).toString()
221
+ } catch (e: Exception) {
222
+ Log.w(TAG, "Failed to serialize adValues; delivering empty object", e)
223
+ "{}"
224
+ }
225
+ map.putString("adValuesJson", adValuesJson)
226
+ return map
227
+ }
228
+
229
+ private fun loadFailedPayload(failedAdUnitId: String, error: CloudXError): WritableMap {
230
+ val map = Arguments.createMap()
231
+ map.putString("adUnitId", failedAdUnitId)
232
+ // Codegen spec declares `code` as Double; emit a double to match (and
233
+ // to stay consistent with `revenue` and the iOS side).
234
+ map.putDouble("code", error.code.code.toDouble())
235
+ map.putString("message", error.message)
236
+ return map
237
+ }
238
+ }
@@ -0,0 +1,28 @@
1
+ package com.cloudxreactnative
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+ import com.facebook.react.uimanager.events.Event
5
+
6
+ /**
7
+ * A per-view direct event for the in-tree CloudXAdView. The event name doubles
8
+ * as the JS prop registrationName (see CloudXAdViewManagerImpl).
9
+ */
10
+ class CloudXAdViewEvent(
11
+ surfaceId: Int,
12
+ viewId: Int,
13
+ private val eventName: String,
14
+ private val payload: WritableMap,
15
+ ) : Event<CloudXAdViewEvent>(surfaceId, viewId) {
16
+
17
+ override fun getEventName(): String = eventName
18
+
19
+ override fun getEventData(): WritableMap = payload
20
+
21
+ /*
22
+ * Ad-lifecycle events are discrete and must never be dropped. The default
23
+ * (true) lets the dispatcher coalesce events sharing the same
24
+ * (eventName, viewId) within a batch, keeping only the latest — which would
25
+ * silently drop, e.g., back-to-back revenue events on the same view.
26
+ */
27
+ override fun canCoalesce(): Boolean = false
28
+ }
@@ -0,0 +1,54 @@
1
+ package com.cloudxreactnative
2
+
3
+ import com.facebook.react.common.MapBuilder
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+
7
+ /**
8
+ * Shared logic for the CloudXAdView view manager, used by both the Old (Paper)
9
+ * and New (Fabric) architecture manager variants under src/oldarch and src/newarch.
10
+ */
11
+ object CloudXAdViewManagerImpl {
12
+
13
+ const val NAME = "CloudXAdView"
14
+
15
+ fun createViewInstance(reactContext: ThemedReactContext): CloudXAdView = CloudXAdView(reactContext)
16
+
17
+ fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
18
+ MapBuilder.builder<String, Any>()
19
+ .put(CloudXAdEvents.ON_AD_VIEW_LOADED_EVENT, eventRegistration(CloudXAdEvents.ON_AD_VIEW_LOADED_EVENT))
20
+ .put(CloudXAdEvents.ON_AD_VIEW_LOAD_FAILED_EVENT, eventRegistration(CloudXAdEvents.ON_AD_VIEW_LOAD_FAILED_EVENT))
21
+ .put(CloudXAdEvents.ON_AD_VIEW_CLICKED_EVENT, eventRegistration(CloudXAdEvents.ON_AD_VIEW_CLICKED_EVENT))
22
+ .put(CloudXAdEvents.ON_AD_VIEW_REVENUE_PAID_EVENT, eventRegistration(CloudXAdEvents.ON_AD_VIEW_REVENUE_PAID_EVENT))
23
+ .build()
24
+
25
+ private fun eventRegistration(name: String): Map<String, String> = MapBuilder.of("registrationName", name)
26
+
27
+ fun setAdUnitId(view: CloudXAdView, adUnitId: String?) {
28
+ view.adUnitId = adUnitId
29
+ }
30
+
31
+ fun setAdFormat(view: CloudXAdView, adFormat: String?) {
32
+ view.adFormat = adFormat
33
+ }
34
+
35
+ fun setPlacement(view: CloudXAdView, placement: String?) {
36
+ view.placement = placement
37
+ }
38
+
39
+ fun setCustomData(view: CloudXAdView, customData: String?) {
40
+ view.customData = customData
41
+ }
42
+
43
+ fun setExtraParameters(view: CloudXAdView, extraParameters: ReadableArray?) {
44
+ view.extraParameters = CloudXExtraParameters.entryArrayToMap(extraParameters)
45
+ }
46
+
47
+ fun onAfterUpdateTransaction(view: CloudXAdView) {
48
+ view.onSetProps()
49
+ }
50
+
51
+ fun onDropViewInstance(view: CloudXAdView) {
52
+ view.destroyAdView()
53
+ }
54
+ }
@@ -0,0 +1,101 @@
1
+ package com.cloudxreactnative
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.bridge.ReadableType
6
+ import io.cloudx.sdk.CloudXAdView as SdkAdView
7
+
8
+ /**
9
+ * Shared conversion and application helpers for CloudX extra parameters.
10
+ *
11
+ * Programmatic APIs receive a [ReadableMap], while component props receive a
12
+ * [ReadableArray] of key/value entries from codegen. Both paths normalize into
13
+ * Kotlin values before calling the same SDK setter behavior.
14
+ */
15
+ object CloudXExtraParameters {
16
+
17
+ /**
18
+ * Iterates a programmatic [ReadableMap], ignoring blank keys and preserving
19
+ * nested arrays/maps as Kotlin collections.
20
+ */
21
+ fun forEach(extraParameters: ReadableMap, apply: (String, Any?) -> Unit) {
22
+ val iterator = extraParameters.keySetIterator()
23
+ while (iterator.hasNextKey()) {
24
+ val key = iterator.nextKey()
25
+ if (key.isBlank()) continue
26
+ apply(key, readableMapValue(extraParameters, key))
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Converts component prop entries into a key/value map that can be diffed
32
+ * by the owning component view before being applied.
33
+ */
34
+ fun entryArrayToMap(extraParameters: ReadableArray?): Map<String, Any?> {
35
+ if (extraParameters == null) {
36
+ return emptyMap()
37
+ }
38
+
39
+ val result = linkedMapOf<String, Any?>()
40
+ for (index in 0 until extraParameters.size()) {
41
+ if (extraParameters.getType(index) != ReadableType.Map) continue
42
+
43
+ val entry = extraParameters.getMap(index) ?: continue
44
+ if (!entry.hasKey("key") || entry.getType("key") != ReadableType.String) continue
45
+
46
+ val key = entry.getString("key") ?: continue
47
+ if (key.isBlank()) continue
48
+ result[key] = if (entry.hasKey("value")) readableMapValue(entry, "value") else null
49
+ }
50
+ return result
51
+ }
52
+
53
+ /**
54
+ * Applies already-normalized extra parameters to [adView].
55
+ */
56
+ fun applyToAdView(adView: SdkAdView, extraParameters: Map<String, Any?>) {
57
+ for ((key, value) in extraParameters) {
58
+ adView.setExtraParameter(key, value)
59
+ }
60
+ }
61
+
62
+ private fun readableMapValue(map: ReadableMap, key: String): Any? {
63
+ return when (map.getType(key)) {
64
+ ReadableType.Null -> null
65
+ ReadableType.Boolean -> map.getBoolean(key)
66
+ ReadableType.Number -> map.getDouble(key)
67
+ ReadableType.String -> map.getString(key)
68
+ ReadableType.Map -> map.getMap(key)?.let(::readableMapToMap)
69
+ ReadableType.Array -> map.getArray(key)?.let(::readableArrayToList)
70
+ }
71
+ }
72
+
73
+ private fun readableMapToMap(readableMap: ReadableMap): Map<String, Any?> {
74
+ val result = mutableMapOf<String, Any?>()
75
+ val iterator = readableMap.keySetIterator()
76
+ while (iterator.hasNextKey()) {
77
+ val key = iterator.nextKey()
78
+ result[key] = readableMapValue(readableMap, key)
79
+ }
80
+ return result
81
+ }
82
+
83
+ private fun readableArrayToList(readableArray: ReadableArray): List<Any?> {
84
+ val result = mutableListOf<Any?>()
85
+ for (index in 0 until readableArray.size()) {
86
+ result.add(readableArrayValue(readableArray, index))
87
+ }
88
+ return result
89
+ }
90
+
91
+ private fun readableArrayValue(array: ReadableArray, index: Int): Any? {
92
+ return when (array.getType(index)) {
93
+ ReadableType.Null -> null
94
+ ReadableType.Boolean -> array.getBoolean(index)
95
+ ReadableType.Number -> array.getDouble(index)
96
+ ReadableType.String -> array.getString(index)
97
+ ReadableType.Map -> array.getMap(index)?.let(::readableMapToMap)
98
+ ReadableType.Array -> array.getArray(index)?.let(::readableArrayToList)
99
+ }
100
+ }
101
+ }