Skip to content

Commit abccf48

Browse files
committed
refactor(android): register server side verification options after ad-reward loaded
1 parent d8c1b3d commit abccf48

File tree

7 files changed

+131
-51
lines changed

7 files changed

+131
-51
lines changed

android/src/main/java/com/getcapacitor/community/admob/models/AdOptions.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import androidx.annotation.VisibleForTesting;
44
import com.getcapacitor.PluginCall;
55
import com.getcapacitor.community.admob.banner.BannerAdSizeEnum;
6+
import com.getcapacitor.community.admob.rewarded.models.SsvInfo;
67

78
/**
89
* Holds the options for an Ad Request
@@ -59,6 +60,11 @@ public abstract class AdOptions {
5960
*/
6061
public final boolean npa;
6162

63+
/**
64+
* Used for Server side verification of Reward Ads
65+
*/
66+
public final SsvInfo ssvInfo;
67+
6268
private AdOptions(PluginCall call) {
6369
/*
6470
* TODO: Since the Id in the Typescript AdOptions interface is not optional
@@ -72,18 +78,20 @@ private AdOptions(PluginCall call) {
7278
this.position = call.getString("position", "BOTTOM_CENTER");
7379
this.margin = call.getInt("margin", 0);
7480
this.npa = call.getBoolean("npa", false);
81+
this.ssvInfo = new SsvInfo(call);
7582

7683
String sizeString = call.getString("adSize", BannerAdSizeEnum.ADAPTIVE_BANNER.name());
7784
this.adSize = AdOptions.adSizeStringToAdSizeEnum(sizeString);
7885
}
7986

80-
private AdOptions(String id, boolean isTesting, String position, int margin, boolean npa, BannerAdSizeEnum adSize) {
87+
private AdOptions(String id, boolean isTesting, String position, int margin, boolean npa, BannerAdSizeEnum adSize, SsvInfo ssvInfo) {
8188
this.adId = id;
8289
this.isTesting = isTesting;
8390
this.position = position;
8491
this.margin = margin;
8592
this.npa = npa;
8693
this.adSize = adSize;
94+
this.ssvInfo = ssvInfo;
8795
}
8896

8997
/**
@@ -163,7 +171,12 @@ public static class TesterAdOptionsBuilder {
163171
private int margin = 1;
164172
private boolean npa = false;
165173
private BannerAdSizeEnum adSize = BannerAdSizeEnum.ADAPTIVE_BANNER;
174+
private SsvInfo ssvInfo = new SsvInfo();
166175

176+
public TesterAdOptionsBuilder setSsvInfo(SsvInfo info) {
177+
ssvInfo = info;
178+
return this;
179+
}
167180
public TesterAdOptionsBuilder setIsTesting(boolean value) {
168181
isTesting = value;
169182
return this;
@@ -200,7 +213,7 @@ public TesterAdOptionsBuilder setAdSize(BannerAdSizeEnum value) {
200213
}
201214

202215
public AdOptions build() {
203-
return new AdOptions(id, isTesting, position, margin, npa, adSize) {
216+
return new AdOptions(id, isTesting, position, margin, npa, adSize, ssvInfo) {
204217
@Override
205218
public String getTestingId() {
206219
return testingID;

android/src/main/java/com/getcapacitor/community/admob/rewarded/AdRewardExecutor.java

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import android.app.Activity;
44
import android.content.Context;
5+
56
import androidx.core.util.Supplier;
7+
68
import com.getcapacitor.JSObject;
79
import com.getcapacitor.PluginCall;
810
import com.getcapacitor.PluginMethod;
@@ -13,7 +15,6 @@
1315
import com.getcapacitor.community.admob.models.Executor;
1416
import com.google.android.gms.ads.AdRequest;
1517
import com.google.android.gms.ads.rewarded.RewardedAd;
16-
import com.google.android.gms.ads.rewarded.ServerSideVerificationOptions;
1718
import com.google.android.gms.common.util.BiConsumer;
1819

1920
public class AdRewardExecutor extends Executor {
@@ -44,7 +45,7 @@ public void prepareRewardVideoAd(final PluginCall call, BiConsumer<String, JSObj
4445
contextSupplier.get(),
4546
id,
4647
adRequest,
47-
RewardedAdCallbackAndListeners.INSTANCE.getRewardedAdLoadCallback(call, notifyListenersFunction)
48+
RewardedAdCallbackAndListeners.INSTANCE.getRewardedAdLoadCallback(call, notifyListenersFunction, adOptions)
4849
);
4950
} catch (Exception ex) {
5051
call.reject(ex.getLocalizedMessage(), ex);
@@ -63,25 +64,6 @@ public void showRewardVideoAd(final PluginCall call, BiConsumer<String, JSObject
6364
return;
6465
}
6566

66-
JSObject providedOptions = call.getObject("ssv");
67-
if (providedOptions.length() != 0) {
68-
ServerSideVerificationOptions.Builder ssvOptions = new ServerSideVerificationOptions.Builder();
69-
70-
if (providedOptions.has("customData")) {
71-
String customData = providedOptions.getString("customData");
72-
assert customData != null;
73-
ssvOptions.setCustomData(customData);
74-
}
75-
76-
if (providedOptions.has("userId")) {
77-
String userId = providedOptions.getString("userId");
78-
assert userId != null;
79-
ssvOptions.setUserId(userId);
80-
}
81-
82-
mRewardedAd.setServerSideVerificationOptions(ssvOptions.build());
83-
}
84-
8567
try {
8668
activitySupplier
8769
.get()

android/src/main/java/com/getcapacitor/community/admob/rewarded/RewardedAdCallbackAndListeners.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import com.getcapacitor.JSObject
44
import com.getcapacitor.PluginCall
55
import com.getcapacitor.community.admob.helpers.FullscreenPluginCallback
66
import com.getcapacitor.community.admob.models.AdMobPluginError
7+
import com.getcapacitor.community.admob.models.AdOptions
78
import com.google.android.gms.ads.LoadAdError
89
import com.google.android.gms.ads.OnUserEarnedRewardListener
910
import com.google.android.gms.ads.rewarded.RewardItem
1011
import com.google.android.gms.ads.rewarded.RewardedAd
1112
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback
13+
import com.google.android.gms.ads.rewarded.ServerSideVerificationOptions
1214
import com.google.android.gms.common.util.BiConsumer
1315

1416
object RewardedAdCallbackAndListeners {
@@ -23,13 +25,24 @@ object RewardedAdCallbackAndListeners {
2325
}
2426
}
2527

26-
fun getRewardedAdLoadCallback(call: PluginCall, notifyListenersFunction: BiConsumer<String, JSObject>): RewardedAdLoadCallback {
28+
fun getRewardedAdLoadCallback(call: PluginCall, notifyListenersFunction: BiConsumer<String, JSObject>, adOptions: AdOptions): RewardedAdLoadCallback {
2729
return object : RewardedAdLoadCallback() {
2830
override fun onAdLoaded(ad: RewardedAd) {
2931
AdRewardExecutor.mRewardedAd = ad
3032
AdRewardExecutor.mRewardedAd.fullScreenContentCallback = FullscreenPluginCallback(
3133
RewardAdPluginEvents, notifyListenersFunction)
3234

35+
if(adOptions.ssvInfo.hasInfo){
36+
val ssvOptions = ServerSideVerificationOptions.Builder()
37+
adOptions.ssvInfo.customData?.let {
38+
ssvOptions.setCustomData(it)
39+
}
40+
41+
adOptions.ssvInfo.userId?.let {
42+
ssvOptions.setUserId(it)
43+
}
44+
AdRewardExecutor.mRewardedAd.setServerSideVerificationOptions(ssvOptions.build())
45+
}
3346

3447
val adInfo = JSObject()
3548
adInfo.put("adUnitId", ad.adUnitId)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.getcapacitor.community.admob.rewarded.models
2+
3+
import com.getcapacitor.PluginCall
4+
5+
class SsvInfo(val customData: String? = null,
6+
val userId: String? = null) {
7+
8+
constructor(pluginCall: PluginCall): this(
9+
pluginCall.getObject("ssv").getString("customData"),
10+
pluginCall.getObject("userId").getString("userId")
11+
)
12+
constructor(): this(null, null)
13+
14+
val hasInfo
15+
get(): Boolean {
16+
return customData != null || userId != null
17+
}
18+
}

android/src/test/java/com/getcapacitor/community/admob/models/AdOptionsTest.java

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import static org.mockito.Mockito.verify;
1111
import static org.mockito.Mockito.when;
1212

13+
import com.getcapacitor.JSObject;
1314
import com.getcapacitor.PluginCall;
15+
import com.getcapacitor.community.admob.rewarded.models.SsvInfo;
16+
1417
import org.junit.jupiter.api.BeforeEach;
1518
import org.junit.jupiter.api.DisplayName;
1619
import org.junit.jupiter.api.Nested;
@@ -98,24 +101,22 @@ public void npa() {
98101
verify(pluginCallMock).getBoolean(wantedProperty, defaultValue);
99102
assertEquals(expected, adOptions.npa);
100103
}
101-
// @Test
102-
// public void testingDevices() {
103-
// final String wantedProperty = "testingDevices";
104-
// final JSArray expected = new JSArray();
105-
// expected.put("one").put("two");
106-
// when(
107-
// pluginCallMock.getArray(
108-
// eq(wantedProperty),
109-
// Mockito.any(JSArray.class)
110-
// )
111-
// )
112-
// .thenReturn(expected);
113-
//
114-
// final AdOptions adOptions = AdOptions.getFactory().createGenericOptions(pluginCallMock, "");
115-
//
116-
// verify(pluginCallMock)
117-
// .getArray(wantedProperty, AdOptions.getFactory().EMPTY_TESTING_DEVICES);
118-
// assertEquals(expected, adOptions.testingDevices);
119-
// }
104+
105+
@Test
106+
public void ssv() {
107+
final String customData = "customData";
108+
final String userId = "userId";
109+
final String wantedProperty = "ssv";
110+
final JSObject expected = new JSObject();
111+
expected.put(customData, customData);
112+
expected.put(userId, userId);
113+
lenient().when(pluginCallMock.getObject(eq(wantedProperty))).thenReturn(expected);
114+
115+
final AdOptions adOptions = AdOptions.getFactory().createGenericOptions(pluginCallMock, "");
116+
117+
verify(pluginCallMock).getObject(wantedProperty);
118+
assertEquals(userId, adOptions.ssvInfo.getUserId());
119+
assertEquals(customData, adOptions.ssvInfo.getCustomData());
120+
}
120121
}
121122
}

android/src/test/java/com/getcapacitor/community/admob/rewarded/RewardedAdCallbackAndListenersTest.kt

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@ import android.content.Context
55
import com.getcapacitor.JSObject
66
import com.getcapacitor.PluginCall
77
import com.getcapacitor.community.admob.helpers.FullscreenPluginCallback
8-
import com.google.android.gms.ads.LoadAdError
8+
import com.getcapacitor.community.admob.models.AdOptions
9+
import com.getcapacitor.community.admob.rewarded.models.SsvInfo
910
import com.google.android.gms.ads.AdError
11+
import com.google.android.gms.ads.LoadAdError
1012
import com.google.android.gms.ads.rewarded.RewardItem
1113
import com.google.android.gms.ads.rewarded.RewardedAd
14+
import com.google.android.gms.ads.rewarded.ServerSideVerificationOptions
1215
import com.google.android.gms.common.util.BiConsumer
13-
import org.junit.jupiter.api.*
14-
import org.junit.jupiter.api.Assertions.*
16+
import org.junit.jupiter.api.Assertions.assertEquals
17+
import org.junit.jupiter.api.BeforeEach
18+
import org.junit.jupiter.api.Nested
19+
import org.junit.jupiter.api.Test
20+
import org.junit.jupiter.api.TestInstance
1521
import org.junit.jupiter.api.extension.ExtendWith
1622
import org.mockito.ArgumentCaptor
1723
import org.mockito.ArgumentMatchers
1824
import org.mockito.ArgumentMatchers.any
1925
import org.mockito.Mock
2026
import org.mockito.Mockito
21-
import org.mockito.Mockito.never
27+
import org.mockito.Mockito.*
2228
import org.mockito.junit.jupiter.MockitoExtension
2329

2430
@ExtendWith(MockitoExtension::class)
@@ -37,10 +43,14 @@ internal class RewardedAdCallbackAndListenersTest {
3743
@Mock
3844
lateinit var pluginCall: PluginCall
3945

46+
private lateinit var listener: com.google.android.gms.ads.rewarded.RewardedAdLoadCallback
47+
4048
@BeforeEach
4149
fun beforeEach() {
4250
Mockito.reset(context, activity, notifierMock)
4351
Mockito.verify(pluginCall, never()).resolve(any()) // Always a clean call
52+
listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall,
53+
notifierMock, AdOptions.TesterAdOptionsBuilder().build())
4454
}
4555

4656
@Nested
@@ -89,7 +99,6 @@ internal class RewardedAdCallbackAndListenersTest {
8999
@Nested
90100
inner class RewardedAdLoadCallback {
91101

92-
93102
@Nested
94103
inner class OnAdFailedToLoad {
95104
private var wantedReason = "This is the reason"
@@ -98,6 +107,8 @@ internal class RewardedAdCallbackAndListenersTest {
98107
@Mock
99108
lateinit var loadAdErrorMock: LoadAdError
100109

110+
111+
101112
@BeforeEach
102113
fun beforeEach() {
103114
Mockito.`when`(loadAdErrorMock.code).thenReturn(wantedErrorCode)
@@ -107,7 +118,8 @@ internal class RewardedAdCallbackAndListenersTest {
107118
@Test
108119
fun `onAdFailedToLoad should emit the the error code and reason in a FailedToLoad event`() {
109120
val argumentCaptor = ArgumentCaptor.forClass(JSObject::class.java)
110-
val listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall, notifierMock)
121+
val listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall,
122+
notifierMock, AdOptions.TesterAdOptionsBuilder().build())
111123

112124
// ACt
113125
listener.onAdFailedToLoad(loadAdErrorMock)
@@ -122,7 +134,6 @@ internal class RewardedAdCallbackAndListenersTest {
122134
@Test
123135
fun `onAdFailedToLoad should reject the error code and reason in a FailedToLoad event`() {
124136
val argumentCaptor = ArgumentCaptor.forClass(String::class.java)
125-
val listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall, notifierMock)
126137

127138
// ACt
128139
listener.onAdFailedToLoad(loadAdErrorMock)
@@ -149,7 +160,6 @@ internal class RewardedAdCallbackAndListenersTest {
149160
@Test
150161
fun `onAdLoaded should emit an Loaded with the ad unit id`() {
151162
val argumentCaptor = ArgumentCaptor.forClass(JSObject::class.java)
152-
val listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall, notifierMock)
153163

154164
// ACt
155165
listener.onAdLoaded(rewardedAdMock)
@@ -159,6 +169,47 @@ internal class RewardedAdCallbackAndListenersTest {
159169

160170
assertEquals(wantedAdUnitId, emittedAdInfo.getString("adUnitId"))
161171
}
172+
173+
@Test
174+
fun `register server side verification customData when ssv info exist and it has customData`() {
175+
176+
mockConstruction(ServerSideVerificationOptions.Builder::class.java).use { ssvOptionsMockedConstruction ->
177+
178+
val adOptions = AdOptions.TesterAdOptionsBuilder().setSsvInfo(SsvInfo("customData", null)).build()
179+
180+
listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall,
181+
notifierMock, adOptions)
182+
183+
// Act
184+
listener.onAdLoaded(rewardedAdMock)
185+
186+
val ssvOptions = ssvOptionsMockedConstruction.constructed()[0]
187+
verify(ssvOptions).setCustomData(adOptions.ssvInfo.customData!!)
188+
verify(ssvOptions, times(0)).setUserId(any())
189+
190+
}
191+
}
192+
193+
@Test
194+
fun `register server side verification userId data when ssv info exist and has userId`() {
195+
196+
mockConstruction(ServerSideVerificationOptions.Builder::class.java).use { ssvOptionsMockedConstruction ->
197+
198+
val adOptions = AdOptions.TesterAdOptionsBuilder().setSsvInfo(SsvInfo(null, "userId")).build()
199+
200+
listener = RewardedAdCallbackAndListeners.getRewardedAdLoadCallback(pluginCall,
201+
notifierMock, adOptions)
202+
203+
// Act
204+
listener.onAdLoaded(rewardedAdMock)
205+
206+
val ssvOptions = ssvOptionsMockedConstruction.constructed()[0]
207+
208+
verify(ssvOptions).setUserId(adOptions.ssvInfo.userId!!)
209+
verify(ssvOptions, times(0)).setCustomData(any())
210+
211+
}
212+
}
162213
}
163214

164215

src/reward/reward-ad-options.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export interface RewardAdOptions extends AdOptions {
99
* If you have enabled SSV in your AdMob Application. You can provide customData or
1010
* a userId be passed to your callback to do further processing on.
1111
*
12+
* *Important* You *HAVE* to define one of them.
13+
*
1214
* @see https://support.google.com/admob/answer/9603226?hl=en-GB
1315
*/
1416
ssv?: AtLeastOne<{

0 commit comments

Comments
 (0)