cloudx-react-native 3.6.1 → 3.8.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 (38) hide show
  1. package/android/build.gradle +92 -2
  2. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewEvent.kt +2 -2
  3. package/android/src/main/java/com/cloudxreactnative/CloudXAdViewManagerImpl.kt +6 -6
  4. package/android/src/main/java/com/cloudxreactnative/CloudXModuleImpl.kt +208 -102
  5. package/android/src/test/java/com/cloudxreactnative/CloudXModuleImplArbiterBidsTest.kt +254 -0
  6. package/android/src/test/java/com/cloudxreactnative/CloudXModuleImplThreadingTest.kt +170 -0
  7. package/android/src/test/java/com/cloudxreactnative/FakeReadable.kt +101 -0
  8. package/cloudx-react-native.podspec +1 -1
  9. package/ios/CloudX.mm +62 -0
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/types/Arbiter.js +27 -0
  12. package/lib/commonjs/types/Arbiter.js.map +1 -1
  13. package/lib/commonjs/types/RevenueData.js +1 -0
  14. package/lib/commonjs/types/RevenueData.js.map +1 -1
  15. package/lib/commonjs/types/index.js.map +1 -1
  16. package/lib/commonjs/version.js +1 -1
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/types/Arbiter.js +27 -0
  19. package/lib/module/types/Arbiter.js.map +1 -1
  20. package/lib/module/types/RevenueData.js +1 -0
  21. package/lib/module/types/RevenueData.js.map +1 -1
  22. package/lib/module/types/index.js.map +1 -1
  23. package/lib/module/version.js +1 -1
  24. package/lib/typescript/src/index.d.ts +1 -1
  25. package/lib/typescript/src/index.d.ts.map +1 -1
  26. package/lib/typescript/src/types/Arbiter.d.ts +45 -2
  27. package/lib/typescript/src/types/Arbiter.d.ts.map +1 -1
  28. package/lib/typescript/src/types/RevenueData.d.ts +1 -0
  29. package/lib/typescript/src/types/RevenueData.d.ts.map +1 -1
  30. package/lib/typescript/src/types/index.d.ts +1 -1
  31. package/lib/typescript/src/types/index.d.ts.map +1 -1
  32. package/lib/typescript/src/version.d.ts +1 -1
  33. package/package.json +1 -1
  34. package/src/index.ts +2 -0
  35. package/src/types/Arbiter.ts +61 -2
  36. package/src/types/RevenueData.ts +1 -0
  37. package/src/types/index.ts +2 -0
  38. package/src/version.ts +1 -1
@@ -0,0 +1,254 @@
1
+ package com.cloudxreactnative
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import io.cloudx.sdk.CloudXArbiterBid
5
+ import io.cloudx.sdk.CloudXArbiterPlatform
6
+ import io.mockk.every
7
+ import io.mockk.mockk
8
+ import org.junit.Assert.assertEquals
9
+ import org.junit.Assert.assertNull
10
+ import org.junit.Assert.assertTrue
11
+ import org.junit.Test
12
+
13
+ /**
14
+ * Covers ADMOB bid parsing in [CloudXModuleImpl.arbiterBids], including coexistence with the
15
+ * pre-existing bid platforms.
16
+ *
17
+ * The SDK's AdMobBid class is internal to io.cloudx.sdk, so its adUnitId / networkName /
18
+ * manualRevenuePerImpressionUSD are not visible from this module. Assertions read them back by
19
+ * reflection; platform and extras come off the public CloudXArbiterBid surface.
20
+ */
21
+ class CloudXModuleImplArbiterBidsTest {
22
+
23
+ // arbiterBids never touches the context, but the constructor registers a lifecycle listener.
24
+ private fun module(): CloudXModuleImpl {
25
+ val context = mockk<ReactApplicationContext>(relaxed = true)
26
+ every { context.addLifecycleEventListener(any()) } returns Unit
27
+ return CloudXModuleImpl(context)
28
+ }
29
+
30
+ private fun bidsFrom(vararg bids: Map<String, Any?>): List<CloudXArbiterBid> {
31
+ val configuration = FakeReadableMap(
32
+ mapOf("bids" to FakeReadableArray(bids.map { FakeReadableMap(it) }))
33
+ )
34
+ return module().arbiterBids(configuration)
35
+ }
36
+
37
+ private fun CloudXArbiterBid.field(name: String): Any? {
38
+ val field = javaClass.getDeclaredField(name)
39
+ field.isAccessible = true
40
+ return field.get(this)
41
+ }
42
+
43
+ @Test
44
+ fun `routes ADMOB platform to an AdMob bid`() {
45
+ val bids = bidsFrom(mapOf("platform" to "ADMOB", "adUnitId" to "unit-1"))
46
+
47
+ assertEquals(1, bids.size)
48
+ assertEquals(CloudXArbiterPlatform.ADMOB, bids[0].platform)
49
+ assertEquals("unit-1", bids[0].field("adUnitId"))
50
+ }
51
+
52
+ @Test
53
+ fun `accepts a lowercase platform token`() {
54
+ val bids = bidsFrom(mapOf("platform" to "admob", "adUnitId" to "unit-1"))
55
+
56
+ assertEquals(1, bids.size)
57
+ assertEquals(CloudXArbiterPlatform.ADMOB, bids[0].platform)
58
+ }
59
+
60
+ @Test
61
+ fun `defaults networkName to admob when absent`() {
62
+ val bids = bidsFrom(mapOf("platform" to "ADMOB", "adUnitId" to "unit-1"))
63
+
64
+ assertEquals("admob", bids[0].field("networkName"))
65
+ }
66
+
67
+ @Test
68
+ fun `defaults networkName to admob when blank`() {
69
+ val bids = bidsFrom(
70
+ mapOf("platform" to "ADMOB", "adUnitId" to "unit-1", "networkName" to " ")
71
+ )
72
+
73
+ assertEquals("admob", bids[0].field("networkName"))
74
+ }
75
+
76
+ @Test
77
+ fun `passes an explicit networkName through`() {
78
+ val bids = bidsFrom(
79
+ mapOf("platform" to "ADMOB", "adUnitId" to "unit-1", "networkName" to "applovin")
80
+ )
81
+
82
+ assertEquals("applovin", bids[0].field("networkName"))
83
+ }
84
+
85
+ @Test
86
+ fun `drops a bid with a missing adUnitId`() {
87
+ val bids = bidsFrom(mapOf("platform" to "ADMOB"))
88
+
89
+ assertTrue(bids.isEmpty())
90
+ }
91
+
92
+ @Test
93
+ fun `drops a bid with a blank adUnitId`() {
94
+ val bids = bidsFrom(mapOf("platform" to "ADMOB", "adUnitId" to " "))
95
+
96
+ assertTrue(bids.isEmpty())
97
+ }
98
+
99
+ @Test
100
+ fun `keeps surrounding bids when one ADMOB bid is dropped`() {
101
+ val bids = bidsFrom(
102
+ mapOf("platform" to "ADMOB", "adUnitId" to ""),
103
+ mapOf("platform" to "ADMOB", "adUnitId" to "unit-2"),
104
+ )
105
+
106
+ assertEquals(1, bids.size)
107
+ assertEquals("unit-2", bids[0].field("adUnitId"))
108
+ }
109
+
110
+ @Test
111
+ fun `carries a manual price when present`() {
112
+ val bids = bidsFrom(
113
+ mapOf(
114
+ "platform" to "ADMOB",
115
+ "adUnitId" to "unit-1",
116
+ "manualRevenuePerImpressionUSD" to 0.004,
117
+ )
118
+ )
119
+
120
+ assertEquals(0.004, bids[0].field("manualRevenuePerImpressionUSD") as Double, 1e-9)
121
+ }
122
+
123
+ @Test
124
+ fun `leaves the manual price null when absent`() {
125
+ val bids = bidsFrom(mapOf("platform" to "ADMOB", "adUnitId" to "unit-1"))
126
+
127
+ assertNull(bids[0].field("manualRevenuePerImpressionUSD"))
128
+ }
129
+
130
+ @Test
131
+ fun `keeps a zero manual price distinct from absent`() {
132
+ val bids = bidsFrom(
133
+ mapOf(
134
+ "platform" to "ADMOB",
135
+ "adUnitId" to "unit-1",
136
+ "manualRevenuePerImpressionUSD" to 0.0,
137
+ )
138
+ )
139
+
140
+ assertEquals(0.0, bids[0].field("manualRevenuePerImpressionUSD") as Double, 1e-9)
141
+ }
142
+
143
+ @Test
144
+ fun `forwards a negative manual price unchanged`() {
145
+ val bids = bidsFrom(
146
+ mapOf(
147
+ "platform" to "ADMOB",
148
+ "adUnitId" to "unit-1",
149
+ "manualRevenuePerImpressionUSD" to -1.5,
150
+ )
151
+ )
152
+
153
+ // The wrapper is transparent about the value it was handed; the native SDK decides to drop it.
154
+ assertEquals(-1.5, bids[0].field("manualRevenuePerImpressionUSD") as Double, 1e-9)
155
+ }
156
+
157
+ @Test
158
+ fun `maps a CUSTOM bid alongside an ADMOB bid in order`() {
159
+ val bids = bidsFrom(
160
+ mapOf(
161
+ "platform" to "CUSTOM",
162
+ "platformName" to "CUSTOM_MEDIATOR",
163
+ "revenuePerImpressionUSD" to 0.002,
164
+ "precision" to "exact",
165
+ ),
166
+ mapOf("platform" to "ADMOB", "adUnitId" to "unit-2"),
167
+ )
168
+
169
+ assertEquals(2, bids.size)
170
+ assertEquals(CloudXArbiterPlatform.CUSTOM, bids[0].platform)
171
+ assertEquals("CUSTOM_MEDIATOR", bids[0].platformName)
172
+ assertEquals(CloudXArbiterPlatform.ADMOB, bids[1].platform)
173
+ assertEquals("unit-2", bids[1].field("adUnitId"))
174
+ }
175
+
176
+ @Test
177
+ fun `maps string extras and skips non-string entries`() {
178
+ val bids = bidsFrom(
179
+ mapOf(
180
+ "platform" to "ADMOB",
181
+ "adUnitId" to "unit-1",
182
+ "extras" to FakeReadableMap(mapOf("k" to "v", "n" to 1.0)),
183
+ )
184
+ )
185
+
186
+ assertEquals(mapOf("k" to "v"), bids[0].extras)
187
+ }
188
+
189
+ @Test
190
+ fun `defaults extras to empty when absent`() {
191
+ val bids = bidsFrom(mapOf("platform" to "ADMOB", "adUnitId" to "unit-1"))
192
+
193
+ assertEquals(emptyMap<String, String>(), bids[0].extras)
194
+ }
195
+
196
+ @Test
197
+ fun `routes GAM platform to a Gam bid with default networkName`() {
198
+ val bids = bidsFrom(mapOf("platform" to "GAM", "adUnitId" to "/21775744923/example/banner"))
199
+
200
+ assertEquals(1, bids.size)
201
+ assertEquals(CloudXArbiterPlatform.GAM, bids[0].platform)
202
+ assertEquals("/21775744923/example/banner", bids[0].field("adUnitId"))
203
+ assertEquals("gam", bids[0].field("networkName"))
204
+ }
205
+
206
+ @Test
207
+ fun `carries GAM manual price and extras through`() {
208
+ val bids = bidsFrom(
209
+ mapOf(
210
+ "platform" to "GAM",
211
+ "adUnitId" to "/21775744923/example/banner",
212
+ "networkName" to "reservation",
213
+ "manualRevenuePerImpressionUSD" to 0.0025,
214
+ "extras" to FakeReadableMap(mapOf("k" to "v")),
215
+ )
216
+ )
217
+
218
+ assertEquals("reservation", bids[0].field("networkName"))
219
+ assertEquals(0.0025, bids[0].field("manualRevenuePerImpressionUSD") as Double, 1e-9)
220
+ assertEquals(mapOf("k" to "v"), bids[0].extras)
221
+ }
222
+
223
+ @Test
224
+ fun `keeps a zero GAM manual price distinct from absent`() {
225
+ val bids = bidsFrom(
226
+ mapOf(
227
+ "platform" to "GAM",
228
+ "adUnitId" to "/21775744923/example/banner",
229
+ "manualRevenuePerImpressionUSD" to 0.0,
230
+ )
231
+ )
232
+
233
+ assertEquals(0.0, bids[0].field("manualRevenuePerImpressionUSD") as Double, 1e-9)
234
+ }
235
+
236
+ @Test
237
+ fun `drops a GAM bid with a blank adUnitId`() {
238
+ val bids = bidsFrom(mapOf("platform" to "GAM", "adUnitId" to " "))
239
+
240
+ assertTrue(bids.isEmpty())
241
+ }
242
+
243
+ @Test
244
+ fun `maps an ADMOB bid alongside a GAM bid in order`() {
245
+ val bids = bidsFrom(
246
+ mapOf("platform" to "ADMOB", "adUnitId" to "ca-app-pub-1/2"),
247
+ mapOf("platform" to "GAM", "adUnitId" to "/21775744923/example/banner"),
248
+ )
249
+
250
+ assertEquals(2, bids.size)
251
+ assertEquals(CloudXArbiterPlatform.ADMOB, bids[0].platform)
252
+ assertEquals(CloudXArbiterPlatform.GAM, bids[1].platform)
253
+ }
254
+ }
@@ -0,0 +1,170 @@
1
+ package com.cloudxreactnative
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import io.cloudx.sdk.CloudX
5
+ import io.cloudx.sdk.CloudXAppOpenAd
6
+ import io.cloudx.sdk.CloudXInterstitialAd
7
+ import io.cloudx.sdk.CloudXRewardedAd
8
+ import io.cloudx.sdk.CloudXAdView
9
+ import io.mockk.every
10
+ import io.mockk.mockk
11
+ import io.mockk.mockkStatic
12
+ import io.mockk.unmockkAll
13
+ import io.mockk.verify
14
+ import kotlinx.coroutines.Dispatchers
15
+ import kotlinx.coroutines.ExperimentalCoroutinesApi
16
+ import kotlinx.coroutines.test.UnconfinedTestDispatcher
17
+ import kotlinx.coroutines.test.resetMain
18
+ import kotlinx.coroutines.test.setMain
19
+ import org.junit.After
20
+ import org.junit.Assert.assertEquals
21
+ import org.junit.Before
22
+ import org.junit.Test
23
+
24
+ /**
25
+ * Covers the module's UI-thread invariant: every public method posts its body via
26
+ * [ReactApplicationContext.runOnUiQueueThread], so JS call order is preserved and a destroy
27
+ * followed by a load always operates on a fresh native ad instance.
28
+ */
29
+ @OptIn(ExperimentalCoroutinesApi::class)
30
+ class CloudXModuleImplThreadingTest {
31
+
32
+ private val adUnitId = "unit-1"
33
+ private val queuedRunnables = mutableListOf<Runnable>()
34
+
35
+ @Before
36
+ fun setUp() {
37
+ // CloudX's static initializer resolves Dispatchers.Main, which needs a main looper on the JVM.
38
+ Dispatchers.setMain(UnconfinedTestDispatcher())
39
+ mockkStatic(CloudX::class)
40
+ }
41
+
42
+ @After
43
+ fun tearDown() {
44
+ unmockkAll()
45
+ Dispatchers.resetMain()
46
+ }
47
+
48
+ // Simulates the UI thread's FIFO queue by running every posted runnable immediately.
49
+ private fun moduleWithInlineUiThread(): CloudXModuleImpl {
50
+ val context = mockk<ReactApplicationContext>(relaxed = true)
51
+ every { context.runOnUiQueueThread(any()) } answers { firstArg<Runnable>().run() }
52
+ return CloudXModuleImpl(context)
53
+ }
54
+
55
+ // Swallows every posted runnable, so anything that runs is running off the UI thread.
56
+ private fun moduleWithStalledUiThread(): CloudXModuleImpl {
57
+ val context = mockk<ReactApplicationContext>(relaxed = true)
58
+ every { context.runOnUiQueueThread(any()) } returns Unit
59
+ return CloudXModuleImpl(context)
60
+ }
61
+
62
+ // Queues posted runnables into [queuedRunnables]; call [drainUiThread] to run them in FIFO order.
63
+ private fun moduleWithQueuedUiThread(): CloudXModuleImpl {
64
+ val context = mockk<ReactApplicationContext>(relaxed = true)
65
+ every { context.runOnUiQueueThread(any()) } answers { queuedRunnables.add(firstArg()) }
66
+ return CloudXModuleImpl(context)
67
+ }
68
+
69
+ private fun drainUiThread() {
70
+ while (queuedRunnables.isNotEmpty()) queuedRunnables.removeAt(0).run()
71
+ }
72
+
73
+ @Test
74
+ fun `destroy then load on same ad unit loads a fresh interstitial`() {
75
+ // Arrange
76
+ val first = mockk<CloudXInterstitialAd>(relaxed = true)
77
+ val second = mockk<CloudXInterstitialAd>(relaxed = true)
78
+ every { CloudX.createInterstitial(any(), any()) } returnsMany listOf(first, second)
79
+ val subject = moduleWithInlineUiThread()
80
+ subject.loadInterstitial(adUnitId)
81
+
82
+ // Act
83
+ subject.destroyInterstitial(adUnitId)
84
+ subject.loadInterstitial(adUnitId)
85
+
86
+ // Assert
87
+ verify(exactly = 1) { first.load() }
88
+ verify(exactly = 1) { first.destroy() }
89
+ verify(exactly = 1) { second.load() }
90
+ }
91
+
92
+ @Test
93
+ fun `destroy then load on same ad unit loads a fresh app open ad`() {
94
+ // Arrange
95
+ val first = mockk<CloudXAppOpenAd>(relaxed = true)
96
+ val second = mockk<CloudXAppOpenAd>(relaxed = true)
97
+ every { CloudX.createAppOpen(any(), any()) } returnsMany listOf(first, second)
98
+ val subject = moduleWithInlineUiThread()
99
+ subject.loadAppOpen(adUnitId)
100
+
101
+ // Act
102
+ subject.destroyAppOpen(adUnitId)
103
+ subject.loadAppOpen(adUnitId)
104
+
105
+ // Assert
106
+ verify(exactly = 1) { first.load() }
107
+ verify(exactly = 1) { first.destroy() }
108
+ verify(exactly = 1) { second.load() }
109
+ }
110
+
111
+ @Test
112
+ fun `destroy then load on same ad unit loads a fresh rewarded ad`() {
113
+ // Arrange
114
+ val first = mockk<CloudXRewardedAd>(relaxed = true)
115
+ val second = mockk<CloudXRewardedAd>(relaxed = true)
116
+ every { CloudX.createRewarded(any(), any()) } returnsMany listOf(first, second)
117
+ val subject = moduleWithInlineUiThread()
118
+ subject.loadRewarded(adUnitId)
119
+
120
+ // Act
121
+ subject.destroyRewarded(adUnitId)
122
+ subject.loadRewarded(adUnitId)
123
+
124
+ // Assert
125
+ verify(exactly = 1) { first.load() }
126
+ verify(exactly = 1) { first.destroy() }
127
+ verify(exactly = 1) { second.load() }
128
+ }
129
+
130
+ @Test
131
+ fun `load does not touch the SDK before the UI runnable runs`() {
132
+ // Arrange
133
+ val subject = moduleWithStalledUiThread()
134
+
135
+ // Act
136
+ subject.loadInterstitial(adUnitId)
137
+
138
+ // Assert
139
+ verify(exactly = 0) { CloudX.createInterstitial(any(), any()) }
140
+ }
141
+
142
+ @Test
143
+ fun `position update followed by offsets update keeps the new position`() {
144
+ // Arrange
145
+ every { CloudX.createBanner(any(), any()) } returns mockk<CloudXAdView>(relaxed = true)
146
+ val subject = moduleWithQueuedUiThread()
147
+ subject.createBanner(adUnitId, "top")
148
+ drainUiThread()
149
+
150
+ // Act
151
+ subject.updateBannerPosition(adUnitId, "bottom")
152
+ subject.updateBannerOffsets(adUnitId, 1.0, 2.0)
153
+ drainUiThread()
154
+
155
+ // Assert
156
+ assertEquals("bottom", subject.adViewPositionForTesting(adUnitId))
157
+ }
158
+
159
+ @Test
160
+ fun `config setter does not touch the SDK before the UI runnable runs`() {
161
+ // Arrange
162
+ val subject = moduleWithStalledUiThread()
163
+
164
+ // Act
165
+ subject.setHashedUserID("hashed")
166
+
167
+ // Assert
168
+ verify(exactly = 0) { CloudX.setHashedUserId(any()) }
169
+ }
170
+ }
@@ -0,0 +1,101 @@
1
+ package com.cloudxreactnative
2
+
3
+ import com.facebook.react.bridge.Dynamic
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.bridge.ReadableMap
6
+ import com.facebook.react.bridge.ReadableMapKeySetIterator
7
+ import com.facebook.react.bridge.ReadableType
8
+ import java.util.ArrayList
9
+ import java.util.HashMap
10
+
11
+ /**
12
+ * Minimal in-memory [ReadableMap] backed by a plain Kotlin map.
13
+ *
14
+ * The bridge only ever reads values through hasKey/getType/getString/getDouble/getMap, so the
15
+ * remaining members throw rather than pretend to work.
16
+ */
17
+ internal class FakeReadableMap(private val values: Map<String, Any?>) : ReadableMap {
18
+
19
+ override val entryIterator: Iterator<Map.Entry<String, Any>>
20
+ get() = throw UnsupportedOperationException("not used by the bridge")
21
+
22
+ override fun hasKey(name: String): Boolean = values.containsKey(name)
23
+
24
+ override fun isNull(name: String): Boolean = values[name] == null
25
+
26
+ override fun getType(name: String): ReadableType = when (values[name]) {
27
+ null -> ReadableType.Null
28
+ is Boolean -> ReadableType.Boolean
29
+ is Number -> ReadableType.Number
30
+ is String -> ReadableType.String
31
+ is ReadableMap -> ReadableType.Map
32
+ is ReadableArray -> ReadableType.Array
33
+ else -> throw IllegalArgumentException("unsupported value for key=$name")
34
+ }
35
+
36
+ override fun getString(name: String): String? = values[name] as String?
37
+
38
+ override fun getDouble(name: String): Double = (values[name] as Number).toDouble()
39
+
40
+ override fun getInt(name: String): Int = (values[name] as Number).toInt()
41
+
42
+ override fun getLong(name: String): Long = (values[name] as Number).toLong()
43
+
44
+ override fun getBoolean(name: String): Boolean = values[name] as Boolean
45
+
46
+ override fun getMap(name: String): ReadableMap? = values[name] as ReadableMap?
47
+
48
+ override fun getArray(name: String): ReadableArray? = values[name] as ReadableArray?
49
+
50
+ override fun keySetIterator(): ReadableMapKeySetIterator {
51
+ val keys = values.keys.toList().iterator()
52
+ return object : ReadableMapKeySetIterator {
53
+ override fun hasNextKey(): Boolean = keys.hasNext()
54
+ override fun nextKey(): String = keys.next()
55
+ }
56
+ }
57
+
58
+ override fun getDynamic(name: String): Dynamic =
59
+ throw UnsupportedOperationException("not used by the bridge")
60
+
61
+ override fun toHashMap(): HashMap<String, Any> =
62
+ throw UnsupportedOperationException("not used by the bridge")
63
+ }
64
+
65
+ /** Minimal in-memory [ReadableArray] holding the bid maps handed to the bridge. */
66
+ internal class FakeReadableArray(private val values: List<Any?>) : ReadableArray {
67
+
68
+ override fun size(): Int = values.size
69
+
70
+ override fun isNull(index: Int): Boolean = values[index] == null
71
+
72
+ override fun getType(index: Int): ReadableType = when (values[index]) {
73
+ null -> ReadableType.Null
74
+ is Boolean -> ReadableType.Boolean
75
+ is Number -> ReadableType.Number
76
+ is String -> ReadableType.String
77
+ is ReadableMap -> ReadableType.Map
78
+ is ReadableArray -> ReadableType.Array
79
+ else -> throw IllegalArgumentException("unsupported value at index=$index")
80
+ }
81
+
82
+ override fun getMap(index: Int): ReadableMap = values[index] as ReadableMap
83
+
84
+ override fun getString(index: Int): String = values[index] as String
85
+
86
+ override fun getDouble(index: Int): Double = (values[index] as Number).toDouble()
87
+
88
+ override fun getInt(index: Int): Int = (values[index] as Number).toInt()
89
+
90
+ override fun getLong(index: Int): Long = (values[index] as Number).toLong()
91
+
92
+ override fun getBoolean(index: Int): Boolean = values[index] as Boolean
93
+
94
+ override fun getArray(index: Int): ReadableArray = values[index] as ReadableArray
95
+
96
+ override fun getDynamic(index: Int): Dynamic =
97
+ throw UnsupportedOperationException("not used by the bridge")
98
+
99
+ override fun toArrayList(): ArrayList<Any> =
100
+ throw UnsupportedOperationException("not used by the bridge")
101
+ }
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
14
14
 
15
15
  s.requires_arc = true
16
16
 
17
- s.dependency "CloudXCore", "~> 3.6.0"
17
+ s.dependency "CloudXCore", "~> 3.8.0"
18
18
 
19
19
  s.source_files = "ios/**/*.{h,mm}"
20
20
 
package/ios/CloudX.mm CHANGED
@@ -141,6 +141,11 @@ static NSString *const ARBITER_PLATFORM_CLOUDX = @"CLOUDX";
141
141
  static NSString *const ARBITER_PLATFORM_LEVELPLAY = @"LEVELPLAY";
142
142
  static NSString *const ARBITER_PLATFORM_PUBMATIC = @"PUBMATIC";
143
143
  static NSString *const ARBITER_PLATFORM_CUSTOM = @"CUSTOM";
144
+ static NSString *const ARBITER_PLATFORM_ADMOB = @"ADMOB";
145
+ static NSString *const ARBITER_PLATFORM_GAM = @"GAM";
146
+
147
+ static NSString *const ADMOB_DEFAULT_NETWORK_NAME = @"admob";
148
+ static NSString *const GAM_DEFAULT_NETWORK_NAME = @"gam";
144
149
 
145
150
  static CloudX *CloudXShared;
146
151
 
@@ -1644,6 +1649,11 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
1644
1649
  return CLXRevenuePlatformAdMob;
1645
1650
  }
1646
1651
 
1652
+ if ( [trimmedPlatform caseInsensitiveCompare: CLXRevenuePlatformGAM] == NSOrderedSame )
1653
+ {
1654
+ return CLXRevenuePlatformGAM;
1655
+ }
1656
+
1647
1657
  if ( [trimmedPlatform caseInsensitiveCompare: CLXRevenuePlatformInMobi] == NSOrderedSame )
1648
1658
  {
1649
1659
  return CLXRevenuePlatformInMobi;
@@ -1792,6 +1802,14 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
1792
1802
  {
1793
1803
  return [self customArbiterBidFromDictionary: dictionary];
1794
1804
  }
1805
+ if ( [platform isEqualToString: ARBITER_PLATFORM_ADMOB] )
1806
+ {
1807
+ return [self adMobArbiterBidFromDictionary: dictionary];
1808
+ }
1809
+ if ( [platform isEqualToString: ARBITER_PLATFORM_GAM] )
1810
+ {
1811
+ return [self gamArbiterBidFromDictionary: dictionary];
1812
+ }
1795
1813
  return nil;
1796
1814
  }
1797
1815
 
@@ -1874,6 +1892,50 @@ RCT_EXPORT_METHOD(destroyRewarded:(NSString *)adUnitIdentifier)
1874
1892
  extras: [self stringMapFromDictionaryValue: dictionary[@"extras"]]];
1875
1893
  }
1876
1894
 
1895
+ - (nullable CLXArbiterBid *)adMobArbiterBidFromDictionary:(NSDictionary<NSString *, id> *)dictionary
1896
+ {
1897
+ // Values are passed untrimmed once valid; blankness only decides accept vs default.
1898
+ NSString *adUnitId = [self stringValueFromDictionary: dictionary key: @"adUnitId"];
1899
+ if ( [self isBlankString: adUnitId] )
1900
+ {
1901
+ [self log: @"arbiter: ignoring ADMOB bid with missing adUnitId"];
1902
+ return nil;
1903
+ }
1904
+
1905
+ NSString *networkName = [self stringValueFromDictionary: dictionary key: @"networkName"];
1906
+ if ( [self isBlankString: networkName] )
1907
+ {
1908
+ networkName = ADMOB_DEFAULT_NETWORK_NAME;
1909
+ }
1910
+
1911
+ return [CLXArbiterBid adMobBidWithAdUnitId: adUnitId
1912
+ networkName: networkName
1913
+ manualRevenuePerImpressionUSD: [self revenueNumberValueFromDictionary: dictionary key: @"manualRevenuePerImpressionUSD"]
1914
+ extras: [self stringMapFromDictionaryValue: dictionary[@"extras"]]];
1915
+ }
1916
+
1917
+ - (nullable CLXArbiterBid *)gamArbiterBidFromDictionary:(NSDictionary<NSString *, id> *)dictionary
1918
+ {
1919
+ // Same blank-check rationale as the ADMOB parser above.
1920
+ NSString *adUnitId = [self stringValueFromDictionary: dictionary key: @"adUnitId"];
1921
+ if ( [self isBlankString: adUnitId] )
1922
+ {
1923
+ [self log: @"arbiter: ignoring GAM bid with missing adUnitId"];
1924
+ return nil;
1925
+ }
1926
+
1927
+ NSString *networkName = [self stringValueFromDictionary: dictionary key: @"networkName"];
1928
+ if ( [self isBlankString: networkName] )
1929
+ {
1930
+ networkName = GAM_DEFAULT_NETWORK_NAME;
1931
+ }
1932
+
1933
+ return [CLXArbiterBid gamBidWithAdUnitId: adUnitId
1934
+ networkName: networkName
1935
+ manualRevenuePerImpressionUSD: [self revenueNumberValueFromDictionary: dictionary key: @"manualRevenuePerImpressionUSD"]
1936
+ extras: [self stringMapFromDictionaryValue: dictionary[@"extras"]]];
1937
+ }
1938
+
1877
1939
  - (NSDictionary<NSString *, id> *)arbiterResultInfoForResult:(CLXArbiterResult *)result
1878
1940
  {
1879
1941
  return @{@"id": result.identifier ?: @"",
@@ -1 +1 @@
1
- {"version":3,"names":["_CloudX","_interopRequireWildcard","require","_NativeCloudX","_interopRequireDefault","_EventEmitter","_Arbiter","_RevenueData","_CloudXInterstitialAd","_CloudXAppOpenAd","_CloudXRewardedAd","_CloudXBannerAd","_CloudXMRECAd","_CloudXAdPosition","_CloudXBannerView","_CloudXMRECView","_CloudXErrorCode","_AppTrackingTransparency","_hooks","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","CloudXLogLevel","exports","VERBOSE","DEBUG","INFO","WARN","ERROR","NONE"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,aAAA,GAAAC,sBAAA,CAAAF,OAAA;AAIA,IAAAG,aAAA,GAAAH,OAAA;AACA,IAAAI,QAAA,GAAAJ,OAAA;AACA,IAAAK,YAAA,GAAAL,OAAA;AAGA,IAAAM,qBAAA,GAAAN,OAAA;AACA,IAAAO,gBAAA,GAAAP,OAAA;AACA,IAAAQ,iBAAA,GAAAR,OAAA;AACA,IAAAS,eAAA,GAAAT,OAAA;AACA,IAAAU,aAAA,GAAAV,OAAA;AACA,IAAAW,iBAAA,GAAAX,OAAA;AAGA,IAAAY,iBAAA,GAAAZ,OAAA;AACA,IAAAa,eAAA,GAAAb,OAAA;AAIA,IAAAc,gBAAA,GAAAd,OAAA;AAGA,IAAAe,wBAAA,GAAAf,OAAA;AAGA,IAAAgB,MAAA,GAAAhB,OAAA;AAAsG,SAAAE,uBAAAe,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,SAAAlB,wBAAAkB,CAAA,EAAAG,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAtB,uBAAA,YAAAA,CAAAkB,CAAA,EAAAG,CAAA,SAAAA,CAAA,IAAAH,CAAA,IAAAA,CAAA,CAAAC,UAAA,SAAAD,CAAA,MAAAO,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAR,OAAA,EAAAF,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAS,CAAA,MAAAF,CAAA,GAAAJ,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAE,CAAA,CAAAI,GAAA,CAAAX,CAAA,UAAAO,CAAA,CAAAK,GAAA,CAAAZ,CAAA,GAAAO,CAAA,CAAAM,GAAA,CAAAb,CAAA,EAAAS,CAAA,gBAAAN,CAAA,IAAAH,CAAA,gBAAAG,CAAA,OAAAW,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAG,CAAA,OAAAK,CAAA,IAAAD,CAAA,GAAAS,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAG,CAAA,OAAAK,CAAA,CAAAI,GAAA,IAAAJ,CAAA,CAAAK,GAAA,IAAAN,CAAA,CAAAE,CAAA,EAAAN,CAAA,EAAAK,CAAA,IAAAC,CAAA,CAAAN,CAAA,IAAAH,CAAA,CAAAG,CAAA,WAAAM,CAAA,KAAAT,CAAA,EAAAG,CAAA;AA9BtG;;AAGA;AACA;AACA;;AAKA;;AAQA;;AAKA;;AAGA;;AAGA;;AASA;;AAkCA;AACO,MAAMgB,cAAc,GAAAC,OAAA,CAAAD,cAAA,GAAG;EAC1BE,OAAO,EAAE,SAAS;EAClBC,KAAK,EAAE,OAAO;EACdC,IAAI,EAAE,MAAM;EACZC,IAAI,EAAE,MAAM;EACZC,KAAK,EAAE,OAAO;EACdC,IAAI,EAAE;AACV,CAAU","ignoreList":[]}
1
+ {"version":3,"names":["_CloudX","_interopRequireWildcard","require","_NativeCloudX","_interopRequireDefault","_EventEmitter","_Arbiter","_RevenueData","_CloudXInterstitialAd","_CloudXAppOpenAd","_CloudXRewardedAd","_CloudXBannerAd","_CloudXMRECAd","_CloudXAdPosition","_CloudXBannerView","_CloudXMRECView","_CloudXErrorCode","_AppTrackingTransparency","_hooks","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","CloudXLogLevel","exports","VERBOSE","DEBUG","INFO","WARN","ERROR","NONE"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,aAAA,GAAAC,sBAAA,CAAAF,OAAA;AAIA,IAAAG,aAAA,GAAAH,OAAA;AACA,IAAAI,QAAA,GAAAJ,OAAA;AACA,IAAAK,YAAA,GAAAL,OAAA;AAGA,IAAAM,qBAAA,GAAAN,OAAA;AACA,IAAAO,gBAAA,GAAAP,OAAA;AACA,IAAAQ,iBAAA,GAAAR,OAAA;AACA,IAAAS,eAAA,GAAAT,OAAA;AACA,IAAAU,aAAA,GAAAV,OAAA;AACA,IAAAW,iBAAA,GAAAX,OAAA;AAGA,IAAAY,iBAAA,GAAAZ,OAAA;AACA,IAAAa,eAAA,GAAAb,OAAA;AAIA,IAAAc,gBAAA,GAAAd,OAAA;AAGA,IAAAe,wBAAA,GAAAf,OAAA;AAGA,IAAAgB,MAAA,GAAAhB,OAAA;AAAsG,SAAAE,uBAAAe,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,SAAAlB,wBAAAkB,CAAA,EAAAG,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAtB,uBAAA,YAAAA,CAAAkB,CAAA,EAAAG,CAAA,SAAAA,CAAA,IAAAH,CAAA,IAAAA,CAAA,CAAAC,UAAA,SAAAD,CAAA,MAAAO,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAR,OAAA,EAAAF,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAS,CAAA,MAAAF,CAAA,GAAAJ,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAE,CAAA,CAAAI,GAAA,CAAAX,CAAA,UAAAO,CAAA,CAAAK,GAAA,CAAAZ,CAAA,GAAAO,CAAA,CAAAM,GAAA,CAAAb,CAAA,EAAAS,CAAA,gBAAAN,CAAA,IAAAH,CAAA,gBAAAG,CAAA,OAAAW,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAG,CAAA,OAAAK,CAAA,IAAAD,CAAA,GAAAS,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAG,CAAA,OAAAK,CAAA,CAAAI,GAAA,IAAAJ,CAAA,CAAAK,GAAA,IAAAN,CAAA,CAAAE,CAAA,EAAAN,CAAA,EAAAK,CAAA,IAAAC,CAAA,CAAAN,CAAA,IAAAH,CAAA,CAAAG,CAAA,WAAAM,CAAA,KAAAT,CAAA,EAAAG,CAAA;AA9BtG;;AAGA;AACA;AACA;;AAKA;;AAQA;;AAKA;;AAGA;;AAGA;;AASA;;AAoCA;AACO,MAAMgB,cAAc,GAAAC,OAAA,CAAAD,cAAA,GAAG;EAC1BE,OAAO,EAAE,SAAS;EAClBC,KAAK,EAAE,OAAO;EACdC,IAAI,EAAE,MAAM;EACZC,IAAI,EAAE,MAAM;EACZC,KAAK,EAAE,OAAO;EACdC,IAAI,EAAE;AACV,CAAU","ignoreList":[]}
@@ -28,6 +28,33 @@ const CloudXArbiterBid = exports.CloudXArbiterBid = {
28
28
  platform: 'CUSTOM',
29
29
  ...params
30
30
  };
31
+ },
32
+ /**
33
+ * Creates an AdMob bid candidate.
34
+ *
35
+ * `manualRevenuePerImpressionUSD` of `0.0` is a real price, not an absent one; negative and
36
+ * non-finite values are dropped natively with a warning. Omitting it leaves pricing to the
37
+ * realized price recorded for `adUnitId`.
38
+ */
39
+ adMob(params) {
40
+ return {
41
+ platform: 'ADMOB',
42
+ ...params
43
+ };
44
+ },
45
+ /**
46
+ * Creates a Google Ad Manager bid candidate. Priced exactly like {@link adMob}; Ad Manager
47
+ * is a separate demand source, so both may bid in the same arbitration.
48
+ *
49
+ * `manualRevenuePerImpressionUSD` of `0.0` is a real price, not an absent one; negative and
50
+ * non-finite values are dropped natively with a warning. Omitting it leaves pricing to the
51
+ * realized price recorded for `adUnitId`.
52
+ */
53
+ gam(params) {
54
+ return {
55
+ platform: 'GAM',
56
+ ...params
57
+ };
31
58
  }
32
59
  };
33
60
  //# sourceMappingURL=Arbiter.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["CloudXArbiterBid","exports","cloudX","ad","platform","levelPlay","params","pubMatic","custom"],"sourceRoot":"../../../src","sources":["types/Arbiter.ts"],"mappings":";;;;;;AAuCO,MAAMA,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG;EAC5BE,MAAMA,CAACC,EAAgB,EAAuB;IAC1C,OAAO;MACHC,QAAQ,EAAE,QAAQ;MAClBD;IACJ,CAAC;EACL,CAAC;EAEDE,SAASA,CAACC,MAAmD,EAA6B;IACtF,OAAO;MACHF,QAAQ,EAAE,WAAW;MACrB,GAAGE;IACP,CAAC;EACL,CAAC;EAEDC,QAAQA,CAACD,MAAkD,EAA4B;IACnF,OAAO;MACHF,QAAQ,EAAE,UAAU;MACpB,GAAGE;IACP,CAAC;EACL,CAAC;EAEDE,MAAMA,CAACF,MAAgD,EAA0B;IAC7E,OAAO;MACHF,QAAQ,EAAE,QAAQ;MAClB,GAAGE;IACP,CAAC;EACL;AACJ,CAAU","ignoreList":[]}
1
+ {"version":3,"names":["CloudXArbiterBid","exports","cloudX","ad","platform","levelPlay","params","pubMatic","custom","adMob","gam"],"sourceRoot":"../../../src","sources":["types/Arbiter.ts"],"mappings":";;;;;;AAqEO,MAAMA,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG;EAC5BE,MAAMA,CAACC,EAAgB,EAAuB;IAC1C,OAAO;MACHC,QAAQ,EAAE,QAAQ;MAClBD;IACJ,CAAC;EACL,CAAC;EAEDE,SAASA,CAACC,MAAmD,EAA6B;IACtF,OAAO;MACHF,QAAQ,EAAE,WAAW;MACrB,GAAGE;IACP,CAAC;EACL,CAAC;EAEDC,QAAQA,CAACD,MAAkD,EAA4B;IACnF,OAAO;MACHF,QAAQ,EAAE,UAAU;MACpB,GAAGE;IACP,CAAC;EACL,CAAC;EAEDE,MAAMA,CAACF,MAAgD,EAA0B;IAC7E,OAAO;MACHF,QAAQ,EAAE,QAAQ;MAClB,GAAGE;IACP,CAAC;EACL,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;EACIG,KAAKA,CAACH,MAA+C,EAAyB;IAC1E,OAAO;MACHF,QAAQ,EAAE,OAAO;MACjB,GAAGE;IACP,CAAC;EACL,CAAC;EAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;EACII,GAAGA,CAACJ,MAA6C,EAAuB;IACpE,OAAO;MACHF,QAAQ,EAAE,KAAK;MACf,GAAGE;IACP,CAAC;EACL;AACJ,CAAU","ignoreList":[]}