cordova-plugin-admob-nextgen 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,7 +58,13 @@ Move beyond simple 320x50 banners. The Native Overlay feature allows you to rend
58
58
  We prioritize the safety of your AdMob account and the stability of your app.
59
59
 
60
60
  * **Smart Throttling (`retryInterval`)**: Prevents accidental spamming of ad requests using a global interval validation.
61
- * **Background Thread Loading**: All ad requests are dispatched on background threads (`cordova.getThreadPool()`), ensuring your app's UI never freezes.
61
+
62
+ - If the loadAd or showAd function is called repeatedly accidentally by JavaScript (for example because it is tied to a scroll event or application loop), the system will continuously call loadAd or showAd.
63
+ ### This will cause two fatal problems:
64
+ 1. Banner Ad Flickering: Ads are constantly being destroyed and redrawn.
65
+ 2. Account Ban: Aggressively pulling ads (spamming impressions) is a serious violation of AdMob's Invalid Traffic (IVT) policy.
66
+
67
+ * **Background Thread Loading**: All ad requests are dispatched on background threads, ensuring your app's UI never freezes.
62
68
 
63
69
  ---
64
70
 
@@ -96,12 +102,6 @@ Add this to your `config.xml` to restore the plugin automatically.
96
102
 
97
103
  ### Step 1: Global Configuration (Run First)
98
104
 
99
- admobNextGen.setRequestConfiguration({
100
- maxAdContentRating: 'G', // 'G', 'PG', 'T', 'MA'
101
- tagForChildDirectedTreatment: true, // true/false/null
102
- tagForUnderAgeOfConsent: true // true/false/null
103
- });
104
-
105
105
  // Optional: Mute ads globally
106
106
  admobNextGen.setAppVolume(0.5);
107
107
  admobNextGen.setAppMuted(true);
@@ -111,7 +111,8 @@ Add this to your `config.xml` to restore the plugin automatically.
111
111
  // 1. Request Consent Information
112
112
  admobNextGen.requestConsentInfo({
113
113
  debug: false, // Set true for testing
114
- reset: false
114
+ reset: false,
115
+ tagForUnderAgeOfConsent: false
115
116
  }, function() {
116
117
  console.log("Consent Info Ready.");
117
118
  startSdk();
@@ -137,7 +138,11 @@ Add this to your `config.xml` to restore the plugin automatically.
137
138
 
138
139
  // 3. Initialize the SDK
139
140
  function startSdk() {
140
- admobNextGen.initialize(function() {
141
+ admobNextGen.initialize({
142
+ maxAdContentRating: 'G', // 'G' || 'PG' || 'T' || 'MA' || ""
143
+ tagForChildDirectedTreatment: false,
144
+ tagForUnderAgeOfConsent: false
145
+ }, function() {
141
146
  console.log(">>> AdMob SDK Initialized & Ready <<<");
142
147
  }, function(err) {
143
148
  console.error("SDK Init Failed", err);
@@ -292,8 +297,9 @@ Use the background engine to pool ads for 0ms latency.
292
297
  adUnitId: 'ca-app-pub-xxx/xxx',
293
298
  size: 'ADAPTIVE', // 'BANNER', 'LARGE_BANNER', 'MEDIUM_RECTANGLE', 'ADAPTIVE', 'FULL_BANNER', 'LEADERBOARD'
294
299
  position: 'bottom', // 'top' or 'bottom'
295
- collapsible: false,
296
- isOverlapping: false // Coba mode Push
300
+ collapsible: false, // true = Enable Collapsible Format (High Revenue)
301
+ isOverlapping: false,
302
+ retryInterval: 5000 // Anti-spam delay (ms)
297
303
  });
298
304
 
299
305
  // 2. Show Instantly (from pool)
@@ -420,6 +426,7 @@ Supports Auto-Resume logic.
420
426
  on.rewarded.failed.show (obj)
421
427
  on.rewarded.revenue (obj)
422
428
  on.rewarded.shown
429
+ on.rewarded.canceled
423
430
  on.rewarded.dismissed
424
431
  on.rewarded.failed.show (obj)
425
432
  on.rewarded.impression
@@ -450,6 +457,7 @@ Auto-showing rewarded format (no opt-in).
450
457
  on.rewardedInter.failed.show (obj)
451
458
  on.rewardedInter.revenue (obj)
452
459
  on.rewardedInter.shown
460
+ on.rewardedInter.canceled
453
461
  on.rewardedInter.dismissed
454
462
  on.rewardedInter.failed.show (obj)
455
463
  on.rewardedInter.impression
@@ -44,7 +44,7 @@ function updateGradleDependencies(nextGenVersion, umpVersion) {
44
44
  if (umpRegex.test(content)) content = content.replace(umpRegex, newUmp);
45
45
 
46
46
  fs.writeFileSync(gradlePath, content, 'utf8');
47
- console.log(`[AdMob Hook] Success: Updated Android Next Gen SDK to ${nextGenVersion} and UMP to ${umpVersion}`);
47
+ //console.log(`[AdMob Hook] Success: Updated Android Next Gen SDK to ${nextGenVersion} and UMP to ${umpVersion}`);
48
48
  }
49
49
 
50
50
  function injectExclusionRules() {
@@ -62,7 +62,7 @@ function injectExclusionRules() {
62
62
  `;
63
63
  content += exclusionBlock;
64
64
  fs.writeFileSync(gradlePath, content, 'utf8');
65
- console.log('[AdMob Hook] Success: Injected legacy SDK exclusion rules into build.gradle');
65
+ // console.log('[AdMob Hook] Success: Injected legacy SDK exclusion rules into build.gradle');
66
66
  }
67
67
 
68
68
  /**
@@ -72,7 +72,7 @@ function injectExclusionRules() {
72
72
  */
73
73
  function updateIosInfoPlist(appIdIos) {
74
74
  if (!fs.existsSync(iosPlistPath)) {
75
- console.warn('[AdMob Hook] Warning: ios/App/App/Info.plist not found. Run "npx cap add ios" first.');
75
+ //console.warn('[AdMob Hook] Warning: ios/App/App/Info.plist not found. Run "npx cap add ios" first.');
76
76
  return;
77
77
  }
78
78
 
@@ -128,7 +128,7 @@ function updateIosInfoPlist(appIdIos) {
128
128
  }
129
129
 
130
130
  fs.writeFileSync(iosPlistPath, content, 'utf8');
131
- console.log(`[AdMob Hook] Success: Updated iOS Info.plist with App ID (${appIdIos}), ATT prompt, and SKAdNetworks.`);
131
+ // console.log(`[AdMob Hook] Success: Updated iOS Info.plist with App ID (${appIdIos}), ATT prompt, and SKAdNetworks.`);
132
132
  }
133
133
 
134
134
  /**
@@ -139,7 +139,7 @@ function updateIosInfoPlist(appIdIos) {
139
139
  function run() {
140
140
  try {
141
141
  if (!fs.existsSync(configPath)) {
142
- console.warn('[AdMob Hook] capacitor.config not found. Skipping auto-injection.');
142
+ // console.warn('[AdMob Hook] capacitor.config not found. Skipping auto-injection.');
143
143
  return;
144
144
  }
145
145
 
@@ -158,7 +158,7 @@ function run() {
158
158
  admob = {
159
159
  APP_ID_ANDROID: appIdAndroid ? appIdAndroid[1] : "ca-app-pub-3940256099942544~3347511713",
160
160
  APP_ID_IOS: appIdIos ? appIdIos[1] : "ca-app-pub-3940256099942544~1458002511",
161
- NEXT_GEN_SDK_VERSION: sdk ? sdk[1] : "0.25.0-beta01",
161
+ NEXT_GEN_SDK_VERSION: sdk ? sdk[1] : "1.0.0",
162
162
  UMP_VERSION: ump ? ump[1] : "4.0.0"
163
163
  };
164
164
  }
@@ -167,7 +167,7 @@ function run() {
167
167
  injectExclusionRules();
168
168
  if (admob?.NEXT_GEN_SDK_VERSION || admob?.UMP_VERSION) {
169
169
  updateGradleDependencies(
170
- admob.NEXT_GEN_SDK_VERSION || "0.25.0-beta01",
170
+ admob.NEXT_GEN_SDK_VERSION || "1.0.0",
171
171
  admob.UMP_VERSION || "4.0.0"
172
172
  );
173
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-admob-nextgen",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Google Mobile Ads Next Gen SDK for Cordova. High performance and modular architecture. ",
5
5
  "cordova": {
6
6
  "id": "cordova-plugin-admob-nextgen",
package/plugin.xml CHANGED
@@ -1,6 +1,6 @@
1
1
  <?xml version='1.0' encoding='utf-8'?>
2
2
  <plugin id="cordova-plugin-admob-nextgen"
3
- version="1.0.3"
3
+ version="1.0.5"
4
4
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
5
5
  xmlns:android="http://schemas.android.com/apk/res/android">
6
6
 
@@ -47,7 +47,7 @@
47
47
 
48
48
  </platform>
49
49
 
50
- <preference name="NEXT_GEN_SDK_VERSION" default="0.25.0-beta01" />
50
+ <preference name="NEXT_GEN_SDK_VERSION" default="1.0.0" />
51
51
  <preference name="UMP_VERSION" default="4.0.0" />
52
52
  <preference name="APP_ID_ANDROID" default="ca-app-pub-3940256099942544~3347511713" />
53
53
 
@@ -21,15 +21,17 @@ import com.google.android.libraries.ads.mobile.sdk.banner.AdSize;
21
21
  import com.google.android.libraries.ads.mobile.sdk.banner.BannerAd;
22
22
  import com.google.android.libraries.ads.mobile.sdk.banner.BannerAdEventCallback;
23
23
  import com.google.android.libraries.ads.mobile.sdk.banner.BannerAdPreloader;
24
- import com.google.android.libraries.ads.mobile.sdk.banner.BannerAdRefreshCallback;
24
+ import com.google.android.libraries.ads.mobile.sdk.banner.BannerAdRefreshCallback;
25
25
  import com.google.android.libraries.ads.mobile.sdk.banner.BannerAdRequest;
26
26
  import com.google.android.libraries.ads.mobile.sdk.common.AdValue;
27
- import com.google.android.libraries.ads.mobile.sdk.common.FullScreenContentError;
27
+ import com.google.android.libraries.ads.mobile.sdk.common.FullScreenContentError;
28
28
  import com.google.android.libraries.ads.mobile.sdk.common.LoadAdError;
29
29
  import com.google.android.libraries.ads.mobile.sdk.common.PreloadCallback;
30
30
  import com.google.android.libraries.ads.mobile.sdk.common.PreloadConfiguration;
31
31
  import com.google.android.libraries.ads.mobile.sdk.common.ResponseInfo;
32
32
 
33
+ import java.util.Date;
34
+
33
35
  public class BannerPreloadExecutor {
34
36
 
35
37
  private static final String TAG = "AdMobBannerPreload";
@@ -44,6 +46,14 @@ public class BannerPreloadExecutor {
44
46
 
45
47
  private String currentPosition = "bottom";
46
48
  private boolean isOverlapping = true;
49
+ private boolean isBannerVisible = false;
50
+
51
+ private int lastAdHeight = 0;
52
+ private int systemSafeTop = 0;
53
+ private int systemSafeBottom = 0;
54
+
55
+ private long lastShowTime = 0;
56
+ private long minShowInterval = 5000;
47
57
 
48
58
  public BannerPreloadExecutor(CordovaInterface cordova, CordovaWebView webView) {
49
59
  this.cordova = cordova;
@@ -63,23 +73,27 @@ public class BannerPreloadExecutor {
63
73
  isPreloaderActive = true;
64
74
  this.currentAdUnitId = adUnitId;
65
75
 
76
+ if (options.has("position")) this.currentPosition = options.getString("position");
77
+ if (options.has("isOverlapping")) this.isOverlapping = options.getBoolean("isOverlapping");
78
+
79
+ if (options.has("retryInterval")) this.minShowInterval = options.getLong("retryInterval");
80
+
66
81
  String requestedSize = "ADAPTIVE";
67
82
  if (options.has("size")) requestedSize = options.getString("size");
68
83
 
69
84
  boolean isCollapsible = false;
70
85
  if (options.has("collapsible")) isCollapsible = options.getBoolean("collapsible");
71
86
 
72
- String preloadPosition = "bottom";
73
- if (options.has("position")) preloadPosition = options.getString("position");
74
-
75
87
  final String finalSizeStr = requestedSize;
76
88
  final boolean finalIsCollapsible = isCollapsible;
77
- final String finalPosition = preloadPosition;
89
+ final String finalPosition = this.currentPosition;
78
90
 
79
91
  cordova.getActivity().runOnUiThread(() -> {
80
92
  Context context = cordova.getActivity();
81
93
  AdSize adSize = getAdSize(context, finalSizeStr);
82
94
 
95
+ this.lastAdHeight = adSize.getHeightInPixels(context);
96
+
83
97
  BannerAdRequest.Builder requestBuilder = new BannerAdRequest.Builder(adUnitId, adSize);
84
98
 
85
99
  if (finalIsCollapsible) {
@@ -87,7 +101,6 @@ public class BannerPreloadExecutor {
87
101
  String anchor = "top".equalsIgnoreCase(finalPosition) ? "top" : "bottom";
88
102
  extras.putString("collapsible", anchor);
89
103
  requestBuilder.setGoogleExtrasBundle(extras);
90
-
91
104
  }
92
105
 
93
106
  BannerAdRequest adRequest = requestBuilder.build();
@@ -96,12 +109,10 @@ public class BannerPreloadExecutor {
96
109
  PreloadCallback preloadCallback = new PreloadCallback() {
97
110
  @Override
98
111
  public void onAdPreloaded(@NonNull String preloadId, @NonNull ResponseInfo responseInfo) {
99
-
100
112
  fireEvent("on.preload.available", null);
101
113
  }
102
114
  @Override
103
115
  public void onAdFailedToPreload(@NonNull String preloadId, @NonNull LoadAdError loadAdError) {
104
-
105
116
  try {
106
117
  JSONObject err = new JSONObject();
107
118
  err.put("message", loadAdError.getMessage());
@@ -110,13 +121,11 @@ public class BannerPreloadExecutor {
110
121
  }
111
122
  @Override
112
123
  public void onAdsExhausted(@NonNull String preloadId) {
113
-
114
124
  fireEvent("on.preload.exhausted", null);
115
125
  }
116
126
  };
117
127
 
118
128
  BannerAdPreloader.start(adUnitId, preloadConfig, preloadCallback);
119
-
120
129
  callbackContext.success("Preloader Started");
121
130
  });
122
131
 
@@ -127,51 +136,235 @@ public class BannerPreloadExecutor {
127
136
  }
128
137
 
129
138
  public void showPolledAd(JSONArray args, CallbackContext callbackContext) {
130
- try {
131
- JSONObject options = args.getJSONObject(0);
132
- String adUnitId = options.getString("adUnitId");
139
+ if (currentAdUnitId == null || currentAdUnitId.isEmpty()) {
140
+ callbackContext.error("Preloader engine is not running.");
141
+ return;
142
+ }
133
143
 
134
- if (options.has("position")) this.currentPosition = options.getString("position");
135
- if (options.has("isOverlapping")) this.isOverlapping = options.getBoolean("isOverlapping");
144
+ String newPosition = this.currentPosition;
145
+ boolean newIsOverlapping = this.isOverlapping;
136
146
 
137
- cordova.getActivity().runOnUiThread(() -> {
138
- BannerAd ad = BannerAdPreloader.pollAd(adUnitId);
147
+ if (args != null && args.length() > 0) {
148
+ try {
149
+ JSONObject options = args.getJSONObject(0);
150
+ if (options.has("position")) newPosition = options.getString("position");
151
+ if (options.has("isOverlapping")) newIsOverlapping = options.getBoolean("isOverlapping");
152
+ } catch (JSONException ignored) {}
153
+ }
139
154
 
140
- if (ad == null) {
155
+ if (currentBannerAd != null && isBannerVisible) {
156
+ boolean isSamePos = newPosition.equalsIgnoreCase(this.currentPosition);
157
+ boolean isSameOverlap = (newIsOverlapping == this.isOverlapping);
141
158
 
142
- callbackContext.error("Pool Empty");
143
- return;
144
- }
159
+ if (isSamePos && isSameOverlap) {
145
160
 
146
- destroyCurrentBanner();
147
- this.currentBannerAd = ad;
161
+ callbackContext.success("Banner Already Visible (Flicker Prevented)");
162
+ return;
163
+ } else {
148
164
 
149
- setupAdEvents(ad);
165
+ this.currentPosition = newPosition;
166
+ this.isOverlapping = newIsOverlapping;
150
167
 
151
- renderBannerView(ad);
168
+ cordova.getActivity().runOnUiThread(() -> {
169
+ updateBannerLayout();
170
+ updateWebViewMargins();
171
+ callbackContext.success("Banner Repositioned (No Pool Exhaustion)");
172
+ });
173
+ return;
174
+ }
175
+ }
152
176
 
153
- sendLoadedEvent(ad.getAdSize(), ad.isCollapsible());
177
+ long currentTime = new Date().getTime();
178
+ if ((currentTime - lastShowTime) < minShowInterval) {
179
+ callbackContext.error("Spam protection active: Please wait " + minShowInterval + "ms.");
180
+ return;
181
+ }
182
+
183
+ this.currentPosition = newPosition;
184
+ this.isOverlapping = newIsOverlapping;
185
+ this.lastShowTime = currentTime;
186
+
187
+ cordova.getActivity().runOnUiThread(() -> {
188
+ BannerAd ad = BannerAdPreloader.pollAd(currentAdUnitId);
189
+
190
+ if (ad == null) {
191
+ callbackContext.error("Pool Empty");
192
+ return;
193
+ }
194
+
195
+ destroyCurrentBanner();
196
+ this.currentBannerAd = ad;
197
+
198
+ this.lastAdHeight = ad.getAdSize().getHeightInPixels(cordova.getActivity());
199
+
200
+ setupAdEvents(ad);
201
+ showBannerView();
202
+ sendLoadedEvent(ad.getAdSize(), ad.isCollapsible());
203
+
204
+ callbackContext.success("Ad Shown from Pool");
205
+ });
206
+ }
207
+
208
+ private void showBannerView() {
209
+ if (currentBannerAd == null) return;
210
+
211
+ if (adLayout == null) {
212
+ adLayout = new RelativeLayout(cordova.getActivity());
213
+
214
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
215
+ RelativeLayout.LayoutParams.MATCH_PARENT,
216
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
154
217
 
155
- callbackContext.success("Ad Shown from Pool");
218
+ if ("top".equalsIgnoreCase(currentPosition)) {
219
+ params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
220
+ } else {
221
+ params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
222
+ }
223
+
224
+ adLayout.setClickable(false);
225
+ adLayout.setFocusable(false);
226
+ cordova.getActivity().addContentView(adLayout, params);
227
+ }
228
+
229
+ isBannerVisible = true;
230
+ updateBannerLayout();
231
+
232
+ adLayout.bringToFront();
233
+ adLayout.setVisibility(View.VISIBLE);
234
+
235
+ View adView = currentBannerAd.getView(cordova.getActivity());
236
+ if (adView != null) adView.setVisibility(View.VISIBLE);
237
+
238
+ updateWebViewMargins();
239
+ }
240
+
241
+ private void updateBannerLayout() {
242
+ if (currentBannerAd == null || adLayout == null) return;
243
+
244
+ View adView = currentBannerAd.getView(cordova.getActivity());
245
+ if (adView == null) return;
246
+
247
+ if (adView.getParent() != null && adView.getParent() != adLayout) {
248
+ ((ViewGroup) adView.getParent()).removeView(adView);
249
+ }
250
+
251
+ RelativeLayout.LayoutParams bannerParams = new RelativeLayout.LayoutParams(
252
+ RelativeLayout.LayoutParams.WRAP_CONTENT,
253
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
254
+
255
+ bannerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
256
+ if ("top".equalsIgnoreCase(currentPosition)) {
257
+ bannerParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
258
+ } else {
259
+ bannerParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
260
+ }
261
+
262
+ if (adView.getParent() == null) {
263
+ adLayout.addView(adView, bannerParams);
264
+ } else {
265
+ adView.setLayoutParams(bannerParams);
266
+ }
267
+
268
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
269
+ adLayout.setOnApplyWindowInsetsListener((v, insets) -> {
270
+
271
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
272
+ systemSafeTop = insets.getInsets(android.view.WindowInsets.Type.systemBars()).top;
273
+ systemSafeBottom = insets.getInsets(android.view.WindowInsets.Type.systemBars()).bottom;
274
+ } else {
275
+ systemSafeTop = insets.getSystemWindowInsetTop();
276
+ systemSafeBottom = insets.getSystemWindowInsetBottom();
277
+ }
278
+
279
+ if (adView != null && adView.getLayoutParams() instanceof RelativeLayout.LayoutParams) {
280
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) adView.getLayoutParams();
281
+ if ("top".equalsIgnoreCase(currentPosition)) {
282
+ params.topMargin = systemSafeTop;
283
+ params.bottomMargin = 0;
284
+ } else {
285
+ params.bottomMargin = systemSafeBottom;
286
+ params.topMargin = 0;
287
+ }
288
+ adView.setLayoutParams(params);
289
+ }
290
+
291
+ updateWebViewMargins();
292
+
293
+ return insets;
156
294
  });
295
+ adLayout.requestApplyInsets();
296
+ }
297
+ }
157
298
 
158
- } catch (JSONException e) {
159
- callbackContext.error(e.getMessage());
299
+ private void updateWebViewMargins() {
300
+ if (webView == null || webView.getView() == null) return;
301
+
302
+ View webViewView = webView.getView();
303
+ ViewGroup.LayoutParams lp = webViewView.getLayoutParams();
304
+
305
+ if (lp instanceof ViewGroup.MarginLayoutParams) {
306
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) lp;
307
+
308
+ if ("top".equalsIgnoreCase(currentPosition)) {
309
+
310
+ params.setMargins(0, 0, 0, 0);
311
+
312
+ if (isBannerVisible && !isOverlapping) {
313
+ float shift = (float) (lastAdHeight + systemSafeTop);
314
+ webViewView.setTranslationY(shift);
315
+ } else {
316
+ webViewView.setTranslationY(0);
317
+ }
318
+ } else {
319
+
320
+ webViewView.setTranslationY(0);
321
+
322
+ if (!isBannerVisible || isOverlapping) {
323
+ params.setMargins(0, 0, 0, 0);
324
+ } else {
325
+ int finalBottom = lastAdHeight + systemSafeBottom;
326
+ params.setMargins(0, 0, 0, finalBottom);
327
+ }
328
+ }
329
+
330
+ webViewView.setLayoutParams(params);
331
+ webViewView.requestLayout();
160
332
  }
161
333
  }
162
334
 
163
- private void setupAdEvents(BannerAd ad) {
335
+ private void destroyCurrentBanner() {
336
+ if (currentBannerAd != null) {
337
+ isBannerVisible = false;
338
+ updateWebViewMargins();
339
+
340
+ View adView = currentBannerAd.getView(cordova.getActivity());
341
+ if (adView != null && adView.getParent() != null) {
342
+ ((ViewGroup) adView.getParent()).removeView(adView);
343
+ }
344
+ currentBannerAd.destroy();
345
+ currentBannerAd = null;
346
+ }
347
+ }
348
+
349
+ public void stopPreloadAndClear() {
350
+ cordova.getActivity().runOnUiThread(() -> {
351
+ isPreloaderActive = false;
352
+ destroyCurrentBanner();
353
+ if (adLayout != null) {
354
+ adLayout.removeAllViews();
355
+ ((ViewGroup)adLayout.getParent()).removeView(adLayout);
356
+ adLayout = null;
357
+ }
358
+ });
359
+ }
164
360
 
361
+ private void setupAdEvents(BannerAd ad) {
165
362
  ad.setAdEventCallback(new BannerAdEventCallback() {
166
363
  @Override
167
- public void onAdImpression() {
168
- fireEvent("on.banner.impression", null);
169
- }
364
+ public void onAdImpression() { fireEvent("on.banner.impression", null); }
170
365
 
171
366
  @Override
172
- public void onAdClicked() {
173
- fireEvent("on.banner.clicked", null);
174
- }
367
+ public void onAdClicked() { fireEvent("on.banner.clicked", null); }
175
368
 
176
369
  @Override
177
370
  public void onAdPaid(@NonNull AdValue adValue) {
@@ -186,20 +379,13 @@ public class BannerPreloadExecutor {
186
379
  }
187
380
 
188
381
  @Override
189
- public void onAdShowedFullScreenContent() {
190
-
191
- fireEvent("on.banner.opened", null);
192
- }
382
+ public void onAdShowedFullScreenContent() { fireEvent("on.banner.opened", null); }
193
383
 
194
384
  @Override
195
- public void onAdDismissedFullScreenContent() {
196
-
197
- fireEvent("on.banner.closed", null);
198
- }
385
+ public void onAdDismissedFullScreenContent() { fireEvent("on.banner.closed", null); }
199
386
 
200
387
  @Override
201
388
  public void onAdFailedToShowFullScreenContent(@NonNull FullScreenContentError error) {
202
-
203
389
  try {
204
390
  JSONObject errData = new JSONObject();
205
391
  errData.put("message", error.getMessage());
@@ -210,14 +396,10 @@ public class BannerPreloadExecutor {
210
396
 
211
397
  ad.setBannerAdRefreshCallback(new BannerAdRefreshCallback() {
212
398
  @Override
213
- public void onAdRefreshed() {
214
-
215
- fireEvent("on.banner.refreshed", null);
216
- }
399
+ public void onAdRefreshed() { fireEvent("on.banner.refreshed", null); }
217
400
 
218
401
  @Override
219
402
  public void onAdFailedToRefresh(@NonNull LoadAdError loadAdError) {
220
-
221
403
  try {
222
404
  JSONObject err = new JSONObject();
223
405
  err.put("message", loadAdError.getMessage());
@@ -231,7 +413,6 @@ public class BannerPreloadExecutor {
231
413
  try {
232
414
  Context context = cordova.getActivity();
233
415
  JSONObject data = new JSONObject();
234
-
235
416
  data.put("width", adSize.getWidth());
236
417
  data.put("height", adSize.getHeight());
237
418
  data.put("widthPixels", adSize.getWidthInPixels(context));
@@ -239,9 +420,7 @@ public class BannerPreloadExecutor {
239
420
  data.put("isCollapsible", isCollapsible);
240
421
 
241
422
  fireEvent("on.banner.load", data);
242
- } catch (JSONException e) {
243
-
244
- }
423
+ } catch (JSONException e) {}
245
424
  }
246
425
 
247
426
  private AdSize getAdSize(Context context, String sizeStr) {
@@ -258,95 +437,6 @@ public class BannerPreloadExecutor {
258
437
  return (int) (displayMetrics.widthPixels / displayMetrics.density);
259
438
  }
260
439
 
261
- private void renderBannerView(BannerAd ad) {
262
- if (adLayout == null) {
263
- adLayout = new RelativeLayout(cordova.getActivity());
264
- RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
265
- RelativeLayout.LayoutParams.MATCH_PARENT,
266
- RelativeLayout.LayoutParams.MATCH_PARENT);
267
- adLayout.setClickable(false);
268
- adLayout.setFocusable(false);
269
- cordova.getActivity().addContentView(adLayout, params);
270
- }
271
-
272
- adLayout.removeAllViews();
273
- adLayout.setVisibility(View.VISIBLE);
274
-
275
- View adView = ad.getView(cordova.getActivity());
276
- RelativeLayout.LayoutParams bannerParams = new RelativeLayout.LayoutParams(
277
- RelativeLayout.LayoutParams.WRAP_CONTENT,
278
- RelativeLayout.LayoutParams.WRAP_CONTENT);
279
-
280
- bannerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
281
-
282
- if ("top".equalsIgnoreCase(currentPosition)) {
283
- bannerParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
284
- } else {
285
- bannerParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
286
- }
287
-
288
- adLayout.addView(adView, bannerParams);
289
- adLayout.bringToFront();
290
-
291
- updateWebViewMargins(adView);
292
- }
293
-
294
- public void stopPreloadAndClear() {
295
- cordova.getActivity().runOnUiThread(() -> {
296
- isPreloaderActive = false;
297
- destroyCurrentBanner();
298
- if (adLayout != null) {
299
- adLayout.removeAllViews();
300
- ((ViewGroup)adLayout.getParent()).removeView(adLayout);
301
- adLayout = null;
302
- }
303
- });
304
- }
305
-
306
- private void destroyCurrentBanner() {
307
- if (currentBannerAd != null) {
308
- currentBannerAd.destroy();
309
- currentBannerAd = null;
310
- }
311
- resetWebViewMargins();
312
- }
313
-
314
- private void updateWebViewMargins(View adView) {
315
- if (isOverlapping || webView == null || webView.getView() == null) {
316
- resetWebViewMargins();
317
- return;
318
- }
319
-
320
- adView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
321
- int adHeight = adView.getMeasuredHeight();
322
-
323
- View webViewView = webView.getView();
324
- ViewGroup.LayoutParams lp = webViewView.getLayoutParams();
325
-
326
- if (lp instanceof ViewGroup.MarginLayoutParams) {
327
- ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) lp;
328
- if ("top".equalsIgnoreCase(currentPosition)) {
329
- params.setMargins(0, adHeight, 0, 0);
330
- } else {
331
- params.setMargins(0, 0, 0, adHeight);
332
- }
333
- webViewView.setLayoutParams(params);
334
- webViewView.requestLayout();
335
- }
336
- }
337
-
338
- private void resetWebViewMargins() {
339
- if (webView == null || webView.getView() == null) return;
340
- View webViewView = webView.getView();
341
- ViewGroup.LayoutParams lp = webViewView.getLayoutParams();
342
- if (lp instanceof ViewGroup.MarginLayoutParams) {
343
- ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) lp;
344
- params.setMargins(0, 0, 0, 0);
345
- webViewView.setLayoutParams(params);
346
- webViewView.requestLayout();
347
- }
348
- }
349
-
350
440
  private void fireEvent(String eventName, JSONObject data) {
351
441
  cordova.getActivity().runOnUiThread(() -> {
352
442
  StringBuilder js = new StringBuilder();
@@ -35,6 +35,7 @@ public class RewardedExecutor {
35
35
 
36
36
  private boolean isLoading = false;
37
37
  private boolean isAutoShow = false;
38
+ private boolean isRewardEarned = false;
38
39
 
39
40
  private long lastLoadTime = 0;
40
41
  private long minLoadInterval = 5000;
@@ -145,6 +146,8 @@ public class RewardedExecutor {
145
146
 
146
147
  activity.runOnUiThread(() -> {
147
148
 
149
+ isRewardEarned = false;
150
+
148
151
  rewardedAd.setAdEventCallback(new RewardedAdEventCallback() {
149
152
 
150
153
  @Override
@@ -172,6 +175,11 @@ public class RewardedExecutor {
172
175
 
173
176
  rewardedAd = null;
174
177
  isLoading = false;
178
+
179
+ if (!isRewardEarned) {
180
+ fireEvent("on.rewarded.canceled", null);
181
+ }
182
+
175
183
  fireEvent("on.rewarded.dismissed", null);
176
184
  }
177
185
 
@@ -202,6 +210,8 @@ public class RewardedExecutor {
202
210
  @Override
203
211
  public void onUserEarnedReward(@NonNull RewardItem rewardItem) {
204
212
 
213
+ isRewardEarned = true;
214
+
205
215
  try {
206
216
  JSONObject rewardData = new JSONObject();
207
217
  rewardData.put("amount", rewardItem.getAmount());
@@ -36,6 +36,7 @@ public class RewardedInterstitialExecutor {
36
36
  private boolean isLoading = false;
37
37
  private long lastLoadTime = 0;
38
38
  private long minLoadInterval = 5000;
39
+ private boolean isRewardEarned = false;
39
40
 
40
41
  public RewardedInterstitialExecutor(CordovaInterface cordova, CordovaWebView webView) {
41
42
  this.cordova = cordova;
@@ -121,6 +122,8 @@ public class RewardedInterstitialExecutor {
121
122
  cordova.getActivity().runOnUiThread(() -> {
122
123
  if (mRewardedInterstitialAd != null) {
123
124
 
125
+ isRewardEarned = false;
126
+
124
127
  mRewardedInterstitialAd.setAdEventCallback(new RewardedInterstitialAdEventCallback() {
125
128
 
126
129
  @Override
@@ -146,6 +149,10 @@ public class RewardedInterstitialExecutor {
146
149
  @Override
147
150
  public void onAdDismissedFullScreenContent() {
148
151
 
152
+ if (!isRewardEarned) {
153
+ fireEvent("on.rewardedInter.canceled", null);
154
+ }
155
+
149
156
  fireEvent("on.rewardedInter.dismissed", null);
150
157
  mRewardedInterstitialAd = null;
151
158
  }
@@ -176,6 +183,8 @@ public class RewardedInterstitialExecutor {
176
183
  Activity activity = cordova.getActivity();
177
184
  mRewardedInterstitialAd.show(activity, rewardItem -> {
178
185
 
186
+ isRewardEarned = true;
187
+
179
188
  try {
180
189
  JSONObject data = new JSONObject();
181
190
  data.put("amount", rewardItem.getAmount());
@@ -8,6 +8,7 @@
8
8
 
9
9
  @property (nonatomic, assign) BOOL isLoading;
10
10
  @property (nonatomic, assign) BOOL isAutoShow;
11
+ @property (nonatomic, assign) BOOL isRewardEarned;
11
12
 
12
13
  @property (nonatomic, assign) NSTimeInterval lastLoadTime;
13
14
  @property (nonatomic, assign) NSTimeInterval minLoadInterval;
@@ -22,6 +23,7 @@
22
23
  self.plugin = plugin;
23
24
  self.isLoading = NO;
24
25
  self.isAutoShow = NO;
26
+ self.isRewardEarned = NO;
25
27
  self.lastLoadTime = 0;
26
28
  self.minLoadInterval = 5.0;
27
29
  }
@@ -128,9 +130,13 @@
128
130
  - (void)showRewardedAd {
129
131
  if (self.rewardedAd == nil) return;
130
132
 
133
+ self.isRewardEarned = NO;
134
+
131
135
  [self.rewardedAd presentFromRootViewController:self.plugin.viewController
132
136
  userDidEarnRewardHandler:^{
133
137
 
138
+ self.isRewardEarned = YES;
139
+
134
140
  GADAdReward *reward = self.rewardedAd.adReward;
135
141
 
136
142
  NSString *jsonStr = [NSString stringWithFormat:@"{\"amount\":%f, \"type\":\"%@\"}", [reward.amount doubleValue], reward.type];
@@ -155,6 +161,9 @@
155
161
 
156
162
  - (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {
157
163
 
164
+ if (!self.isRewardEarned) {
165
+ [self.plugin fireEvent:@"document" event:@"on.rewarded.canceled" withData:nil];
166
+ }
158
167
  self.rewardedAd = nil;
159
168
  self.isLoading = NO;
160
169
  [self.plugin fireEvent:@"document" event:@"on.rewarded.dismissed" withData:nil];
@@ -8,6 +8,7 @@
8
8
 
9
9
  @property (nonatomic, assign) BOOL isLoading;
10
10
  @property (nonatomic, assign) BOOL isAutoShow;
11
+ @property (nonatomic, assign) BOOL isRewardEarned;
11
12
 
12
13
  @property (nonatomic, assign) NSTimeInterval lastLoadTime;
13
14
  @property (nonatomic, assign) NSTimeInterval minLoadInterval;
@@ -22,6 +23,7 @@
22
23
  self.plugin = plugin;
23
24
  self.isLoading = NO;
24
25
  self.isAutoShow = NO;
26
+ self.isRewardEarned = NO;
25
27
  self.lastLoadTime = 0;
26
28
  self.minLoadInterval = 5.0;
27
29
  }
@@ -143,9 +145,13 @@
143
145
  - (void)showRewardedInterstitialAd:(CDVInvokedUrlCommand *)command {
144
146
  if (self.rewardedInterstitialAd != nil) {
145
147
 
148
+ self.isRewardEarned = NO;
149
+
146
150
  [self.rewardedInterstitialAd presentFromRootViewController:self.plugin.viewController
147
151
  userDidEarnRewardHandler:^{
148
152
 
153
+ self.isRewardEarned = YES;
154
+
149
155
  GADAdReward *reward = self.rewardedInterstitialAd.adReward;
150
156
 
151
157
  NSString *jsonStr = [NSString stringWithFormat:@"{\"amount\":%f, \"type\":\"%@\"}", [reward.amount doubleValue], reward.type];
@@ -182,6 +188,9 @@
182
188
 
183
189
  - (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {
184
190
 
191
+ if (!self.isRewardEarned) {
192
+ [self.plugin fireEvent:@"document" event:@"on.rewardedInter.canceled" withData:nil];
193
+ }
185
194
  self.rewardedInterstitialAd = nil;
186
195
  self.isLoading = NO;
187
196
  [self.plugin fireEvent:@"document" event:@"on.rewardedInter.dismissed" withData:nil];
@@ -114,8 +114,8 @@ var AdMobNextGen = {
114
114
  exec(success, error, 'AdMobNextGen', 'startBannerPreload', [options]);
115
115
  },
116
116
 
117
- showPreloadedBanner: function (options, success, error) {
118
- exec(success, error, 'AdMobNextGen', 'showPreloadedBanner', [options]);
117
+ showPreloadedBanner: function (success, error) {
118
+ exec(success, error, 'AdMobNextGen', 'showPreloadedBanner', []);
119
119
  },
120
120
 
121
121
  stopBannerPreload: function (success, error) {