@capacitor-community/admob 8.1.0 → 8.2.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 (41) hide show
  1. package/README.md +129 -324
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +13 -3
  4. package/android/src/main/java/com/getcapacitor/community/admob/banner/BannerExecutor.java +261 -141
  5. package/android/src/main/java/com/getcapacitor/community/admob/helpers/FullscreenPluginCallback.kt +5 -1
  6. package/android/src/main/java/com/getcapacitor/community/admob/interstitial/AdInterstitialExecutor.java +27 -31
  7. package/android/src/main/java/com/getcapacitor/community/admob/interstitial/InterstitialAdCallbackAndListeners.kt +1 -1
  8. package/android/src/main/java/com/getcapacitor/community/admob/models/LoadPluginEventNames.kt +2 -1
  9. package/android/src/main/java/com/getcapacitor/community/admob/rewarded/AdRewardExecutor.java +29 -33
  10. package/android/src/main/java/com/getcapacitor/community/admob/rewarded/RewardAdPluginEvents.kt +2 -1
  11. package/android/src/main/java/com/getcapacitor/community/admob/rewarded/RewardedAdCallbackAndListeners.kt +1 -1
  12. package/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/AdRewardInterstitialExecutor.java +29 -37
  13. package/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardedInterstitialAdCallbackAndListeners.kt +1 -1
  14. package/dist/docs.json +33 -3
  15. package/dist/esm/interstitial/interstitial-definitions.interface.d.ts +3 -0
  16. package/dist/esm/reward/reward-ad-plugin-events.enum.d.ts +4 -0
  17. package/dist/esm/reward/reward-ad-plugin-events.enum.js +4 -0
  18. package/dist/esm/reward/reward-ad-plugin-events.enum.js.map +1 -1
  19. package/dist/esm/reward/reward-definitions.interface.d.ts +8 -0
  20. package/dist/esm/reward-interstitial/reward-interstitial-definitions.interface.d.ts +3 -0
  21. package/dist/plugin.cjs.js +4 -0
  22. package/dist/plugin.cjs.js.map +1 -1
  23. package/dist/plugin.js +4 -0
  24. package/dist/plugin.js.map +1 -1
  25. package/docs/app-open.md +53 -0
  26. package/docs/banner.md +100 -0
  27. package/docs/configuration.md +30 -0
  28. package/docs/consent.md +80 -0
  29. package/docs/events.md +75 -0
  30. package/docs/interstitial.md +47 -0
  31. package/docs/migration.md +126 -0
  32. package/docs/rewarded.md +135 -0
  33. package/docs/testing.md +67 -0
  34. package/ios/Sources/AdMobPlugin/AdMobPlugin.swift +4 -1
  35. package/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift +1 -1
  36. package/ios/Sources/AdMobPlugin/Banner/BannerExecutor.swift +1 -1
  37. package/ios/Sources/AdMobPlugin/Interstitial/AdInterstitialExecutor.swift +3 -3
  38. package/ios/Sources/AdMobPlugin/Rewarded/AdRewardExecutor.swift +7 -3
  39. package/ios/Sources/AdMobPlugin/Rewarded/RewardAdPluginEvents.swift +1 -0
  40. package/ios/Sources/AdMobPlugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift +3 -3
  41. package/package.json +3 -1
@@ -2,17 +2,22 @@ package com.getcapacitor.community.admob.banner;
2
2
 
3
3
  import android.app.Activity;
4
4
  import android.content.Context;
5
- import android.os.Build;
5
+ import android.os.Handler;
6
+ import android.os.Looper;
6
7
  import android.util.DisplayMetrics;
7
8
  import android.util.Log;
8
9
  import android.view.Gravity;
9
10
  import android.view.View;
10
11
  import android.view.ViewGroup;
12
+ import android.view.ViewTreeObserver;
11
13
  import android.view.WindowManager;
12
14
  import android.widget.RelativeLayout;
13
15
  import androidx.annotation.NonNull;
14
16
  import androidx.coordinatorlayout.widget.CoordinatorLayout;
17
+ import androidx.core.graphics.Insets;
18
+ import androidx.core.util.Consumer;
15
19
  import androidx.core.util.Supplier;
20
+ import androidx.core.view.WindowInsetsCompat;
16
21
  import com.getcapacitor.JSObject;
17
22
  import com.getcapacitor.PluginCall;
18
23
  import com.getcapacitor.community.admob.helpers.AdViewIdHelper;
@@ -44,11 +49,120 @@ public class BannerExecutor extends Executor {
44
49
  super(contextSupplier, activitySupplier, notifyListenersFunction, pluginLogTag, "BannerExecutor");
45
50
  }
46
51
 
47
- public void initialize() {
48
- mViewGroup = (ViewGroup) ((ViewGroup) activitySupplier.get().findViewById(android.R.id.content)).getChildAt(0);
52
+ /**
53
+ * How long to wait for the banner parent to appear before giving up. Deliberately
54
+ * generous: the devices where the parent is late are the slow ones, and a premature
55
+ * timeout now fails AdMob.initialize() outright rather than only the banner.
56
+ */
57
+ private static final long PARENT_TIMEOUT_MS = 5000;
58
+
59
+ /**
60
+ * Resolve the banner parent, waiting for the layout pass that adds it when it is not
61
+ * there yet, and report the outcome to `onResult` on the UI thread.
62
+ *
63
+ * android.R.id.content can still have no child when initialize() runs - for example
64
+ * when the app is relaunched quickly after being closed - and the old code cached that
65
+ * null for the lifetime of the process, so every later showBanner() threw
66
+ * NullPointerException on it. Waiting here lets AdMob.initialize() mean what callers
67
+ * already read it as: ready to show a banner.
68
+ */
69
+ public void awaitViewGroup(final Consumer<Boolean> onResult) {
70
+ if (resolveViewGroup() != null) {
71
+ onResult.accept(true);
72
+ return;
73
+ }
74
+
75
+ Activity activity = liveActivity();
76
+ View content = activity == null ? null : activity.findViewById(android.R.id.content);
77
+ if (!(content instanceof ViewGroup)) {
78
+ Log.w(logTag, "Banner parent unavailable: no usable android.R.id.content");
79
+ onResult.accept(false);
80
+ return;
81
+ }
82
+
83
+ final View contentView = content;
84
+ final Handler handler = new Handler(Looper.getMainLooper());
85
+ final boolean[] settled = { false };
86
+ final ViewTreeObserver.OnGlobalLayoutListener[] listener = new ViewTreeObserver.OnGlobalLayoutListener[1];
87
+ final Runnable[] timeout = new Runnable[1];
88
+
89
+ listener[0] = () -> {
90
+ if (settled[0] || resolveViewGroup() == null) {
91
+ return;
92
+ }
93
+ settled[0] = true;
94
+ handler.removeCallbacks(timeout[0]);
95
+ removeGlobalLayoutListener(contentView, listener[0]);
96
+ onResult.accept(true);
97
+ };
98
+
99
+ timeout[0] = () -> {
100
+ if (settled[0]) {
101
+ return;
102
+ }
103
+ settled[0] = true;
104
+ removeGlobalLayoutListener(contentView, listener[0]);
105
+ Log.w(logTag, "Banner parent never appeared within " + PARENT_TIMEOUT_MS + "ms");
106
+ onResult.accept(false);
107
+ };
108
+
109
+ contentView.getViewTreeObserver().addOnGlobalLayoutListener(listener[0]);
110
+
111
+ // A child added between the first attempt and registering the listener would not
112
+ // fire it, so try once more now that we are listening.
113
+ if (resolveViewGroup() != null) {
114
+ settled[0] = true;
115
+ removeGlobalLayoutListener(contentView, listener[0]);
116
+ onResult.accept(true);
117
+ return;
118
+ }
119
+
120
+ handler.postDelayed(timeout[0], PARENT_TIMEOUT_MS);
49
121
  }
50
122
 
51
- public void showBanner(final PluginCall call) {
123
+ /**
124
+ * The banner's parent, or null while it is unavailable. Never caches a null: the
125
+ * lookup is retried on each use so a later call can succeed once layout has settled.
126
+ */
127
+ private ViewGroup resolveViewGroup() {
128
+ if (mViewGroup != null) {
129
+ return mViewGroup;
130
+ }
131
+ Activity activity = liveActivity();
132
+ if (activity == null) {
133
+ return null;
134
+ }
135
+ View content = activity.findViewById(android.R.id.content);
136
+ if (!(content instanceof ViewGroup)) {
137
+ return null;
138
+ }
139
+ // Must be content's first child: showBanner builds CoordinatorLayout.LayoutParams,
140
+ // which android.R.id.content (a FrameLayout) rejects with a ClassCastException
141
+ // while measuring.
142
+ View child = ((ViewGroup) content).getChildAt(0);
143
+ if (child instanceof ViewGroup) {
144
+ mViewGroup = (ViewGroup) child;
145
+ }
146
+ return mViewGroup;
147
+ }
148
+
149
+ /** The current activity, or null when it is gone or on its way out. */
150
+ private Activity liveActivity() {
151
+ Activity activity = activitySupplier.get();
152
+ if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
153
+ return null;
154
+ }
155
+ return activity;
156
+ }
157
+
158
+ private static void removeGlobalLayoutListener(View view, ViewTreeObserver.OnGlobalLayoutListener listener) {
159
+ ViewTreeObserver observer = view.getViewTreeObserver();
160
+ if (observer.isAlive()) {
161
+ observer.removeOnGlobalLayoutListener(listener);
162
+ }
163
+ }
164
+
165
+ public void showBanner(final PluginCall call, boolean systemBarsHandlesInsets) {
52
166
  final AdOptions adOptions = AdOptions.getFactory().createBannerOptions(call);
53
167
  float density = contextSupplier.get().getResources().getDisplayMetrics().density;
54
168
 
@@ -130,23 +244,27 @@ public class BannerExecutor extends Executor {
130
244
  mAdViewLayoutParams.setMargins(sideMargin, densityMargin, sideMargin, densityMargin);
131
245
  }
132
246
 
133
- // set Safe Area only for Android 15+
134
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
135
- View rootView = activitySupplier.get().getWindow().getDecorView();
136
- rootView.setOnApplyWindowInsetsListener((v, insets) -> {
137
- int bottomInset = insets.getSystemWindowInsetBottom();
138
- int topInset = insets.getSystemWindowInsetTop();
139
-
140
- if ("TOP_CENTER".equals(adOptions.position)) {
141
- mAdViewLayoutParams.setMargins(margins[0], margins[1] + topInset, margins[2], margins[3]);
142
- } else {
143
- mAdViewLayoutParams.setMargins(margins[0], margins[1], margins[2], margins[3] + bottomInset);
144
- }
247
+ // Use the insets remaining after Capacitor's parent layout has handled them.
248
+ // Replacing the decor-view listener would override SystemBars' safe-area handling.
249
+ mAdViewLayout.setOnApplyWindowInsetsListener((v, insets) -> {
250
+ WindowInsetsCompat compat = WindowInsetsCompat.toWindowInsetsCompat(insets, v);
251
+ Insets safeArea = compat.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
252
+ boolean keyboardVisible =
253
+ compat.isVisible(WindowInsetsCompat.Type.ime()) && compat.getInsets(WindowInsetsCompat.Type.ime()).bottom > 0;
254
+ // SystemBars already pads the parent for the keyboard. Neither the IME
255
+ // height nor the navigation bar belongs in the banner's margin then.
256
+ // With insetsHandling=disable, the host app owns keyboard avoidance.
257
+ int bottomInset = keyboardVisible && systemBarsHandlesInsets ? 0 : safeArea.bottom;
258
+
259
+ if ("TOP_CENTER".equals(adOptions.position)) {
260
+ mAdViewLayoutParams.setMargins(margins[0], margins[1] + safeArea.top, margins[2], margins[3]);
261
+ } else {
262
+ mAdViewLayoutParams.setMargins(margins[0], margins[1], margins[2], margins[3] + bottomInset);
263
+ }
145
264
 
146
- mAdViewLayout.setLayoutParams(mAdViewLayoutParams);
147
- return insets;
148
- });
149
- }
265
+ v.setLayoutParams(mAdViewLayoutParams);
266
+ return insets;
267
+ });
150
268
 
151
269
  createNewAdView(adOptions);
152
270
 
@@ -163,20 +281,18 @@ public class BannerExecutor extends Executor {
163
281
  }
164
282
 
165
283
  try {
166
- activitySupplier
167
- .get()
168
- .runOnUiThread(() -> {
169
- if (mAdViewLayout != null) {
170
- mAdViewLayout.setVisibility(View.GONE);
171
- mAdView.pause();
284
+ activitySupplier.get().runOnUiThread(() -> {
285
+ if (mAdViewLayout != null) {
286
+ mAdViewLayout.setVisibility(View.GONE);
287
+ mAdView.pause();
172
288
 
173
- final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(0, 0);
289
+ final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(0, 0);
174
290
 
175
- notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
291
+ notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
176
292
 
177
- call.resolve();
178
- }
179
- });
293
+ call.resolve();
294
+ }
295
+ });
180
296
  } catch (Exception ex) {
181
297
  call.reject(ex.getLocalizedMessage(), ex);
182
298
  }
@@ -184,19 +300,17 @@ public class BannerExecutor extends Executor {
184
300
 
185
301
  public void resumeBanner(final PluginCall call) {
186
302
  try {
187
- activitySupplier
188
- .get()
189
- .runOnUiThread(() -> {
190
- if (mAdViewLayout != null && mAdView != null) {
191
- mAdViewLayout.setVisibility(View.VISIBLE);
192
- mAdView.resume();
193
-
194
- final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(mAdView);
195
- notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
303
+ activitySupplier.get().runOnUiThread(() -> {
304
+ if (mAdViewLayout != null && mAdView != null) {
305
+ mAdViewLayout.setVisibility(View.VISIBLE);
306
+ mAdView.resume();
196
307
 
197
- Log.d(logTag, "Banner AD Resumed");
198
- }
199
- });
308
+ final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(mAdView);
309
+ notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
310
+
311
+ Log.d(logTag, "Banner AD Resumed");
312
+ }
313
+ });
200
314
 
201
315
  call.resolve();
202
316
  } catch (Exception ex) {
@@ -207,19 +321,20 @@ public class BannerExecutor extends Executor {
207
321
  public void removeBanner(final PluginCall call) {
208
322
  try {
209
323
  if (mAdView != null) {
210
- activitySupplier
211
- .get()
212
- .runOnUiThread(() -> {
213
- if (mAdView != null) {
214
- mViewGroup.removeView(mAdViewLayout);
215
- mAdViewLayout.removeView(mAdView);
216
- mAdView.destroy();
217
- mAdView = null;
218
- Log.d(logTag, "Banner AD Removed");
219
- final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(0, 0);
220
- notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
324
+ activitySupplier.get().runOnUiThread(() -> {
325
+ if (mAdView != null) {
326
+ final ViewGroup bannerParent = resolveViewGroup();
327
+ if (bannerParent != null) {
328
+ bannerParent.removeView(mAdViewLayout);
221
329
  }
222
- });
330
+ mAdViewLayout.removeView(mAdView);
331
+ mAdView.destroy();
332
+ mAdView = null;
333
+ Log.d(logTag, "Banner AD Removed");
334
+ final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(0, 0);
335
+ notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
336
+ }
337
+ });
223
338
  }
224
339
 
225
340
  call.resolve();
@@ -233,16 +348,14 @@ public class BannerExecutor extends Executor {
233
348
  // shared field that another UI-thread task can null before this one
234
349
  // runs; using the captured reference avoids a NullPointerException.
235
350
  final AdView adView = mAdView;
236
- activitySupplier
237
- .get()
238
- .runOnUiThread(() -> {
239
- if (adView != mAdView) {
240
- // Banner was removed or replaced before this task ran.
241
- return;
242
- }
243
- final AdRequest adRequest = RequestHelper.createRequest(adOptions);
244
- adView.loadAd(adRequest);
245
- });
351
+ activitySupplier.get().runOnUiThread(() -> {
352
+ if (adView != mAdView) {
353
+ // Banner was removed or replaced before this task ran.
354
+ return;
355
+ }
356
+ final AdRequest adRequest = RequestHelper.createRequest(adOptions);
357
+ adView.loadAd(adRequest);
358
+ });
246
359
  }
247
360
 
248
361
  /**
@@ -258,96 +371,103 @@ public class BannerExecutor extends Executor {
258
371
  final AdView adView = mAdView;
259
372
 
260
373
  // Run AdMob In Main UI Thread
261
- activitySupplier
262
- .get()
263
- .runOnUiThread(() -> {
264
- if (adView != mAdView) {
265
- // Banner was removed or replaced before this task ran.
266
- return;
267
- }
268
- final AdRequest adRequest = RequestHelper.createRequest(adOptions);
269
- // Assign the correct id needed
270
- AdViewIdHelper.assignIdToAdView(adView, adOptions, adRequest, logTag, contextSupplier.get());
271
- // Add the AdView to the view hierarchy.
272
- mAdViewLayout.addView(adView);
273
- // Start loading the ad.
274
- adView.loadAd(adRequest);
275
- adView.setAdListener(
276
- new AdListener() {
277
- @Override
278
- public void onAdLoaded() {
279
- if (adView != mAdView) {
280
- return;
281
- }
282
- final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(adView);
283
-
284
- notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
285
- notifyListeners(BannerAdPluginEvents.Loaded.getWebEventName(), emptyObject);
286
- super.onAdLoaded();
374
+ activitySupplier.get().runOnUiThread(() -> {
375
+ if (adView != mAdView) {
376
+ // Banner was removed or replaced before this task ran.
377
+ return;
378
+ }
379
+ final AdRequest adRequest = RequestHelper.createRequest(adOptions);
380
+ // Assign the correct id needed
381
+ AdViewIdHelper.assignIdToAdView(adView, adOptions, adRequest, logTag, contextSupplier.get());
382
+ // Add the AdView to the view hierarchy.
383
+ mAdViewLayout.addView(adView);
384
+ // Start loading the ad.
385
+ adView.loadAd(adRequest);
386
+ adView.setAdListener(
387
+ new AdListener() {
388
+ @Override
389
+ public void onAdLoaded() {
390
+ if (adView != mAdView) {
391
+ return;
287
392
  }
393
+ final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(adView);
288
394
 
289
- @Override
290
- public void onAdFailedToLoad(@NonNull LoadAdError adError) {
291
- if (adView != mAdView) {
292
- // Stale callback from a banner that was already removed or
293
- // replaced. Do not touch the current banner or emit teardown
294
- // events for a view the JS layer has already discarded.
295
- super.onAdFailedToLoad(adError);
296
- return;
297
- }
298
-
299
- mViewGroup.removeView(mAdViewLayout);
300
- mAdViewLayout.removeView(adView);
301
- adView.destroy();
302
- mAdView = null;
303
-
304
- final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(0, 0);
305
- notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
306
-
307
- final AdMobPluginError adMobPluginError = new AdMobPluginError(adError);
308
- notifyListeners(BannerAdPluginEvents.FailedToLoad.getWebEventName(), adMobPluginError);
395
+ notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
396
+ notifyListeners(BannerAdPluginEvents.Loaded.getWebEventName(), emptyObject);
397
+ super.onAdLoaded();
398
+ }
309
399
 
400
+ @Override
401
+ public void onAdFailedToLoad(@NonNull LoadAdError adError) {
402
+ if (adView != mAdView) {
403
+ // Stale callback from a banner that was already removed or
404
+ // replaced. Do not touch the current banner or emit teardown
405
+ // events for a view the JS layer has already discarded.
310
406
  super.onAdFailedToLoad(adError);
407
+ return;
311
408
  }
312
409
 
313
- @Override
314
- public void onAdOpened() {
315
- notifyListeners(BannerAdPluginEvents.Opened.getWebEventName(), emptyObject);
316
- super.onAdOpened();
410
+ final ViewGroup bannerParent = resolveViewGroup();
411
+ if (bannerParent != null) {
412
+ bannerParent.removeView(mAdViewLayout);
317
413
  }
414
+ mAdViewLayout.removeView(adView);
415
+ adView.destroy();
416
+ mAdView = null;
318
417
 
319
- @Override
320
- public void onAdClosed() {
321
- notifyListeners(BannerAdPluginEvents.Closed.getWebEventName(), emptyObject);
322
- super.onAdClosed();
323
- }
418
+ final BannerAdSizeInfo sizeInfo = new BannerAdSizeInfo(0, 0);
419
+ notifyListeners(BannerAdPluginEvents.SizeChanged.getWebEventName(), sizeInfo);
324
420
 
325
- @Override
326
- public void onAdImpression() {
327
- notifyListeners(BannerAdPluginEvents.AdImpression.getWebEventName(), emptyObject);
328
- super.onAdImpression();
329
- }
421
+ final AdMobPluginError adMobPluginError = new AdMobPluginError(adError);
422
+ notifyListeners(BannerAdPluginEvents.FailedToLoad.getWebEventName(), adMobPluginError);
423
+
424
+ super.onAdFailedToLoad(adError);
330
425
  }
331
- );
332
426
 
333
- adView.setOnPaidEventListener((adValue) -> {
334
- if (adView != mAdView) {
335
- return;
427
+ @Override
428
+ public void onAdOpened() {
429
+ notifyListeners(BannerAdPluginEvents.Opened.getWebEventName(), emptyObject);
430
+ super.onAdOpened();
336
431
  }
337
- String networkName = "";
338
- String impressionId = "";
339
- if (adView.getResponseInfo() != null) {
340
- networkName = adView.getResponseInfo().getMediationAdapterClassName();
341
- if (networkName == null) networkName = "";
342
- impressionId = adView.getResponseInfo().getResponseId();
343
- if (impressionId == null) impressionId = "";
432
+
433
+ @Override
434
+ public void onAdClosed() {
435
+ notifyListeners(BannerAdPluginEvents.Closed.getWebEventName(), emptyObject);
436
+ super.onAdClosed();
344
437
  }
345
- AdMobRevenueData revenueData = new AdMobRevenueData(adValue, adView.getAdUnitId(), networkName, impressionId);
346
- notifyListeners(BannerAdPluginEvents.AdPaid.getWebEventName(), revenueData);
347
- });
348
438
 
349
- // Add AdViewLayout top of the WebView
350
- mViewGroup.addView(mAdViewLayout);
439
+ @Override
440
+ public void onAdImpression() {
441
+ notifyListeners(BannerAdPluginEvents.AdImpression.getWebEventName(), emptyObject);
442
+ super.onAdImpression();
443
+ }
444
+ }
445
+ );
446
+
447
+ adView.setOnPaidEventListener((adValue) -> {
448
+ if (adView != mAdView) {
449
+ return;
450
+ }
451
+ String networkName = "";
452
+ String impressionId = "";
453
+ if (adView.getResponseInfo() != null) {
454
+ networkName = adView.getResponseInfo().getMediationAdapterClassName();
455
+ if (networkName == null) networkName = "";
456
+ impressionId = adView.getResponseInfo().getResponseId();
457
+ if (impressionId == null) impressionId = "";
458
+ }
459
+ AdMobRevenueData revenueData = new AdMobRevenueData(adValue, adView.getAdUnitId(), networkName, impressionId);
460
+ notifyListeners(BannerAdPluginEvents.AdPaid.getWebEventName(), revenueData);
351
461
  });
462
+
463
+ // Add AdViewLayout top of the WebView
464
+ final ViewGroup bannerParent = resolveViewGroup();
465
+ if (bannerParent != null) {
466
+ bannerParent.addView(mAdViewLayout);
467
+ mAdViewLayout.requestApplyInsets();
468
+ } else {
469
+ Log.w(logTag, "Banner not attached: parent unavailable");
470
+ }
471
+ });
352
472
  }
353
473
  }
@@ -13,6 +13,10 @@ class FullscreenPluginCallback(
13
13
  private val onCompleted: Runnable? = null
14
14
  ): FullScreenContentCallback() {
15
15
 
16
+ override fun onAdClicked() {
17
+ loadPluginObject.adClicked?.let { notifyListenersFunction.accept(it, JSObject()) }
18
+ }
19
+
16
20
  override fun onAdShowedFullScreenContent() {
17
21
  notifyListenersFunction.accept(loadPluginObject.Showed, JSObject())
18
22
  }
@@ -29,4 +33,4 @@ class FullscreenPluginCallback(
29
33
  onCompleted?.run()
30
34
  notifyListenersFunction.accept(loadPluginObject.Dismissed, JSObject())
31
35
  }
32
- }
36
+ }
@@ -40,18 +40,16 @@ public class AdInterstitialExecutor extends Executor {
40
40
  final AdOptions adOptions = factory.createInterstitialOptions(call);
41
41
 
42
42
  try {
43
- activitySupplier
44
- .get()
45
- .runOnUiThread(() -> {
46
- final AdRequest adRequest = RequestHelper.createRequest(adOptions);
47
- final String id = AdViewIdHelper.getFinalAdId(adOptions, adRequest, logTag, contextSupplier.get());
48
- InterstitialAd.load(
49
- activitySupplier.get(),
50
- id,
51
- adRequest,
52
- adCallbackAndListeners.getInterstitialAdLoadCallback(call, notifyListenersFunction)
53
- );
54
- });
43
+ activitySupplier.get().runOnUiThread(() -> {
44
+ final AdRequest adRequest = RequestHelper.createRequest(adOptions);
45
+ final String id = AdViewIdHelper.getFinalAdId(adOptions, adRequest, logTag, contextSupplier.get());
46
+ InterstitialAd.load(
47
+ activitySupplier.get(),
48
+ id,
49
+ adRequest,
50
+ adCallbackAndListeners.getInterstitialAdLoadCallback(call, notifyListenersFunction)
51
+ );
52
+ });
55
53
  } catch (Exception ex) {
56
54
  call.reject(ex.getLocalizedMessage(), ex);
57
55
  }
@@ -71,24 +69,22 @@ public class AdInterstitialExecutor extends Executor {
71
69
  }
72
70
 
73
71
  final InterstitialAd adToShow = ad;
74
- activitySupplier
75
- .get()
76
- .runOnUiThread(() -> {
77
- try {
78
- adToShow.setFullScreenContentCallback(
79
- new FullscreenPluginCallback(InterstitialAdPluginPluginEvent.INSTANCE, notifyListenersFunction, () -> {
80
- preparedAds.remove(adId);
81
- if (adId != null && adId.equals(lastPreparedAdId)) {
82
- lastPreparedAdId = null;
83
- for (String remainingAdId : preparedAds.keySet()) lastPreparedAdId = remainingAdId;
84
- }
85
- })
86
- );
87
- adToShow.show(activitySupplier.get());
88
- call.resolve();
89
- } catch (Exception ex) {
90
- call.reject(ex.getLocalizedMessage(), ex);
91
- }
92
- });
72
+ activitySupplier.get().runOnUiThread(() -> {
73
+ try {
74
+ adToShow.setFullScreenContentCallback(
75
+ new FullscreenPluginCallback(InterstitialAdPluginPluginEvent.INSTANCE, notifyListenersFunction, () -> {
76
+ preparedAds.remove(adId);
77
+ if (adId != null && adId.equals(lastPreparedAdId)) {
78
+ lastPreparedAdId = null;
79
+ for (String remainingAdId : preparedAds.keySet()) lastPreparedAdId = remainingAdId;
80
+ }
81
+ })
82
+ );
83
+ adToShow.show(activitySupplier.get());
84
+ call.resolve();
85
+ } catch (Exception ex) {
86
+ call.reject(ex.getLocalizedMessage(), ex);
87
+ }
88
+ });
93
89
  }
94
90
  }
@@ -43,7 +43,7 @@ object InterstitialAdCallbackAndListeners {
43
43
  val adMobError = AdMobPluginError(adError)
44
44
 
45
45
  notifyListenersFunction.accept(InterstitialAdPluginPluginEvent.FailedToLoad, adMobError)
46
- call.reject(adError.message)
46
+ call.reject(adError.message, adError.code.toString())
47
47
  }
48
48
  }
49
49
  }
@@ -1,7 +1,8 @@
1
1
  package com.getcapacitor.community.admob.models
2
2
 
3
3
  interface LoadPluginEventNames {
4
+ val adClicked: String? get() = null
4
5
  val Showed: String
5
6
  val FailedToShow: String
6
7
  val Dismissed: String
7
- }
8
+ }