@capacitor-community/admob 8.2.0 → 8.2.1-beta.pr490.shaf4a3cab095f4

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 (81) hide show
  1. package/CapacitorCommunityAdmob.podspec +1 -1
  2. package/Package.swift +1 -1
  3. package/README.md +8 -7
  4. package/android/build.gradle +5 -1
  5. package/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +88 -0
  6. package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdExecutor.kt +626 -0
  7. package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdFeedSessions.kt +35 -0
  8. package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdPlacementValue.kt +44 -0
  9. package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdPluginEvents.kt +11 -0
  10. package/android/src/main/java/com/getcapacitor/community/admob/nativeads/PluginNativeAdView.kt +187 -0
  11. package/dist/esm/index.d.ts +1 -0
  12. package/dist/esm/index.js +5 -1
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/native-ads/index.d.ts +6 -0
  15. package/dist/esm/native-ads/index.js +4 -0
  16. package/dist/esm/native-ads/index.js.map +1 -0
  17. package/dist/esm/native-ads/native-ad-definitions.interface.d.ts +20 -0
  18. package/dist/esm/native-ads/native-ad-definitions.interface.js +2 -0
  19. package/dist/esm/native-ads/native-ad-definitions.interface.js.map +1 -0
  20. package/dist/esm/native-ads/native-ad-element.d.ts +9 -0
  21. package/dist/esm/native-ads/native-ad-element.js +53 -0
  22. package/dist/esm/native-ads/native-ad-element.js.map +1 -0
  23. package/dist/esm/native-ads/native-ad-event.interface.d.ts +5 -0
  24. package/dist/esm/native-ads/native-ad-event.interface.js +2 -0
  25. package/dist/esm/native-ads/native-ad-event.interface.js.map +1 -0
  26. package/dist/esm/native-ads/native-ad-feed.d.ts +72 -0
  27. package/dist/esm/native-ads/native-ad-feed.js +643 -0
  28. package/dist/esm/native-ads/native-ad-feed.js.map +1 -0
  29. package/dist/esm/native-ads/native-ad-geometry.d.ts +8 -0
  30. package/dist/esm/native-ads/native-ad-geometry.js +92 -0
  31. package/dist/esm/native-ads/native-ad-geometry.js.map +1 -0
  32. package/dist/esm/native-ads/native-ad-options.interface.d.ts +40 -0
  33. package/dist/esm/native-ads/native-ad-options.interface.js +2 -0
  34. package/dist/esm/native-ads/native-ad-options.interface.js.map +1 -0
  35. package/dist/esm/native-ads/native-ad-placement.interface.d.ts +28 -0
  36. package/dist/esm/native-ads/native-ad-placement.interface.js +2 -0
  37. package/dist/esm/native-ads/native-ad-placement.interface.js.map +1 -0
  38. package/dist/esm/native-ads/native-ad-plugin-events.enum.d.ts +9 -0
  39. package/dist/esm/native-ads/native-ad-plugin-events.enum.js +11 -0
  40. package/dist/esm/native-ads/native-ad-plugin-events.enum.js.map +1 -0
  41. package/dist/esm/native-ads/native-ad-style.interface.d.ts +30 -0
  42. package/dist/esm/native-ads/native-ad-style.interface.js +2 -0
  43. package/dist/esm/native-ads/native-ad-style.interface.js.map +1 -0
  44. package/dist/esm/native-ads/native-ad-template.enum.d.ts +11 -0
  45. package/dist/esm/native-ads/native-ad-template.enum.js +13 -0
  46. package/dist/esm/native-ads/native-ad-template.enum.js.map +1 -0
  47. package/dist/esm/web.d.ts +9 -1
  48. package/dist/esm/web.js +15 -0
  49. package/dist/esm/web.js.map +1 -1
  50. package/dist/plugin.cjs.js +883 -1
  51. package/dist/plugin.cjs.js.map +1 -1
  52. package/dist/plugin.js +883 -1
  53. package/dist/plugin.js.map +1 -1
  54. package/docs/configuration.md +2 -2
  55. package/docs/consent.md +6 -6
  56. package/docs/events.md +3 -2
  57. package/docs/migration.md +22 -2
  58. package/docs/native-ads.md +187 -0
  59. package/docs/rewarded.md +13 -0
  60. package/ios/Sources/AdMobPlugin/AdMobPlugin.swift +103 -41
  61. package/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdPluginEvents.swift +3 -0
  62. package/ios/Sources/AdMobPlugin/Banner/BannerAdPluginEvents.swift +3 -0
  63. package/ios/Sources/AdMobPlugin/Banner/BannerAdSizeEnum.swift +3 -0
  64. package/ios/Sources/AdMobPlugin/Banner/BannerExecutor.swift +4 -4
  65. package/ios/Sources/AdMobPlugin/Helper/AuthorizationStatusEnum.swift +3 -0
  66. package/ios/Sources/AdMobPlugin/Interstitial/AdInterstitialExecutor.swift +3 -0
  67. package/ios/Sources/AdMobPlugin/Interstitial/InterstitialAdPluginEvents.swift +3 -0
  68. package/ios/Sources/AdMobPlugin/NativeAds/NativeAdExecutor.swift +354 -0
  69. package/ios/Sources/AdMobPlugin/NativeAds/NativeAdFeedSessions.swift +34 -0
  70. package/ios/Sources/AdMobPlugin/NativeAds/NativeAdPlacementValue.swift +50 -0
  71. package/ios/Sources/AdMobPlugin/NativeAds/NativeAdPluginEvents.swift +11 -0
  72. package/ios/Sources/AdMobPlugin/NativeAds/NativeAdScrollTracker.swift +66 -0
  73. package/ios/Sources/AdMobPlugin/NativeAds/NativeAdValues.swift +27 -0
  74. package/ios/Sources/AdMobPlugin/NativeAds/PluginNativeAdView.swift +187 -0
  75. package/ios/Sources/AdMobPlugin/Rewarded/AdRewardExecutor.swift +3 -0
  76. package/ios/Sources/AdMobPlugin/Rewarded/RewardAdPluginEvents.swift +3 -0
  77. package/ios/Sources/AdMobPlugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift +3 -0
  78. package/ios/Sources/AdMobPlugin/RewardedInterstitial/RewardInterstitialAdPluginEvents.swift +3 -0
  79. package/ios/Tests/AdMobPluginTests/AdMobPluginTests.swift +82 -10
  80. package/ios/Tests/AdMobPluginTests/NativeAdScrollTrackerTests.swift +47 -0
  81. package/package.json +6 -4
@@ -0,0 +1,187 @@
1
+ # Native Ads
2
+
3
+ Native ads are rendered with Google Mobile Ads SDK views owned by this plugin. Your app reserves an HTML slot and supplies a stable key; it does not implement a `NativeAdView` in Kotlin or Swift and does not render ad assets in JavaScript.
4
+
5
+ Native Ads is available on iOS and Android. Virtual scrolling is not yet supported. A native ad is placed above the WebView, so a pan beginning on the ad may not reach the WebView's scroll container. The implementation updates overlay coordinates as the WebView scrolls, but that cannot solve the initial gesture-routing problem.
6
+
7
+ Browsers and PWAs are unsupported. `NativeAdFeed.create()` rejects instead of leaving an empty ad slot.
8
+
9
+ ## Create a feed
10
+
11
+ Create one manager for each visible feed screen after initializing AdMob and obtaining consent.
12
+
13
+ ```ts
14
+ import { NativeAdFeed, NativeAdPluginEvents, NativeAdTemplate } from '@capacitor-community/admob';
15
+
16
+ const nativeAds = await NativeAdFeed.create({
17
+ feedId: 'home-feed',
18
+ template: NativeAdTemplate.Medium,
19
+ isTesting: true,
20
+ style: {
21
+ backgroundColor: '#ffffff',
22
+ cornerRadius: 12,
23
+ headlineColor: '#111827',
24
+ callToActionBackgroundColor: '#2563eb',
25
+ },
26
+ });
27
+
28
+ const paidHandle = await nativeAds.addListener(NativeAdPluginEvents.AdPaid, (event) => {
29
+ console.log(event.slotKey, event.valueMicros, event.currencyCode);
30
+ });
31
+ ```
32
+
33
+ Use the framework-independent element in your feed markup. `slot-key` must identify the logical ad item, not its array index and not the recycled DOM node.
34
+
35
+ ```html
36
+ <capacitor-admob-native feed-id="home-feed" slot-key="sponsored-after-article-42"></capacitor-admob-native>
37
+ ```
38
+
39
+ The element reserves `320px` for `Medium` and `120px` for `Small` only when author CSS does not provide a height. Author `display: none` and explicit height rules are respected. `Small` slots must be at least `120×120px`; `Medium` slots must be at least `144×300px`. Smaller slots are not loaded. These are lower bounds, not a guarantee that every creative and font size will fit. On Android, `Medium` stays hidden if its measured media area is smaller than `120×120dp`; increase the slot size to accommodate text, device font scaling, and pixel rounding. Do not animate or dynamically measure the slot height.
40
+
41
+ `Small` has no video area. If the SDK returns a video ad for this template, the load fails with `NativeAdPluginEvents.FailedToLoad` (code `-1`); it is not displayed or automatically retried. Use `Medium` for ad units that serve video ads.
42
+
43
+ If a framework does not accept custom elements, attach an ordinary element instead:
44
+
45
+ ```ts
46
+ nativeAds.attach('sponsored-after-article-42', element);
47
+
48
+ // Before the element is destroyed or reused for another logical item:
49
+ nativeAds.detach(element);
50
+ ```
51
+
52
+ Destroy the manager with its screen:
53
+
54
+ ```ts
55
+ await paidHandle.remove();
56
+ await nativeAds.destroy();
57
+ ```
58
+
59
+ ## Virtual-scroll integration experiments
60
+
61
+ The stable-key lifecycle is designed for later validation with Angular CDK virtual scroll, React Virtuoso, and Vue Virtual Scroller. These integrations are examples for experiments, not a support claim:
62
+
63
+ - Put the logical ad ID in `slot-key` (Angular binding, React prop/ref, or Vue `:slot-key`). Never use the rendered index.
64
+ - Keep the ad row at a fixed height and include that height in the virtual scroller's item-size calculation.
65
+ - When a library recycles a row, update the key or call `detach` before calling `attach` with the new key. The plugin hides the old generation before loading or showing the new one.
66
+ - Use a single vertical scroll root. Nested scroll containers, horizontal virtual lists, sticky/transformed ancestors, and animated row heights are not in the initial support profile.
67
+
68
+ For Angular, add `CUSTOM_ELEMENTS_SCHEMA` to the standalone component or NgModule that owns the feed, then bind attributes (not properties) inside the virtual row:
69
+
70
+ ```html
71
+ <capacitor-admob-native feed-id="home-feed" [attr.slot-key]="item.stableAdKey"></capacitor-admob-native>
72
+ ```
73
+
74
+ With React, `createElement` keeps custom dash-cased attributes type-safe without adding a framework dependency to the plugin:
75
+
76
+ ```tsx
77
+ import { createElement } from 'react';
78
+
79
+ const NativeAdRow = ({ slotKey }: { slotKey: string }) =>
80
+ createElement('capacitor-admob-native', {
81
+ 'feed-id': 'home-feed',
82
+ 'slot-key': slotKey,
83
+ });
84
+ ```
85
+
86
+ For Vue, mark the tag as a custom element in the Vue compiler and bind the stable key:
87
+
88
+ ```ts
89
+ // vite.config.ts
90
+ vue({
91
+ template: {
92
+ compilerOptions: {
93
+ isCustomElement: (tag) => tag === 'capacitor-admob-native',
94
+ },
95
+ },
96
+ });
97
+ ```
98
+
99
+ ```html
100
+ <capacitor-admob-native feed-id="home-feed" :slot-key="item.stableAdKey" />
101
+ ```
102
+
103
+ Use a normal HTML `overflow: auto` container or Ionic `ion-content`; Android requires no scroll-specific option. Ads follow the slot and its clipping bounds, but can lag behind fast scrolling. New ads load after scrolling settles.
104
+
105
+ A feed keeps at most three native ads, including attached offscreen slots; additional visible slots wait for capacity. At most two feed managers may be active. Ads are not automatically refreshed, and failed loads are not silently retried. Call `reload(slotKey)` only at an explicit product-defined retry or refresh point.
106
+
107
+ ## Layout and overlay lifecycle
108
+
109
+ Call `invalidateLayout()` after application-driven layout changes that can move a slot without scrolling, such as expanding an accordion. Slot size changes and captured image-load events are detected automatically.
110
+
111
+ Native ads sit above WebView content and cannot infer that an Ionic modal, popover, menu, loading indicator, or route transition covers the slot. Hide the feed before presenting an overlay and resume it after dismissal:
112
+
113
+ ```ts
114
+ await nativeAds.pause();
115
+ await modal.present();
116
+ await modal.onDidDismiss();
117
+ nativeAds.resume();
118
+ ```
119
+
120
+ Call `destroy()` when leaving a screen. Await `pause()` before presenting an overlay; it rejects if the native hide update fails. `resume()` waits for the viewport to settle before showing eligible placements again. `invalidateLayout()` likewise resolves only after the stale placement has been hidden.
121
+
122
+ ## API
123
+
124
+ | Member | Purpose |
125
+ | ---------------------------------------------- | -------------------------------------------------------------- |
126
+ | `NativeAdFeed.create(options)` | Starts a native feed session. Rejects outside iOS and Android. |
127
+ | `feedId` | Normalized feed identifier used by the custom element. |
128
+ | `addListener(event, listener)` | Adds a listener filtered to this feed session. |
129
+ | `attach(slotKey, element)` / `detach(element)` | Advanced lifecycle for an ordinary element. |
130
+ | `reload(slotKey)` | Explicitly removes and reloads one registered slot. |
131
+ | `pause(): Promise<void>` / `resume(): void` | Hides placements during Web overlays or inactive page states. |
132
+ | `invalidateLayout(): Promise<void>` | Hides and remeasures after an application-driven reflow. |
133
+ | `destroy()` | Removes listeners and all native resources for the session. |
134
+
135
+ `NativeAdFeedOptions` contains `feedId`, `adId`, `template`, `style`, `isTesting`, `npa`, the optional iOS `scrollElement`, and Android `androidScrollSnapshot`. Set `isTesting: true` to use Google's platform test ad unit. For production ads, set your platform-specific native ad unit ID in `adId` and omit `isTesting` or set it to `false`. `adId` is required unless `isTesting` is `true`. `feedId` and every `slotKey` must be non-empty and stable; reuse the same feed IDs across WebView reloads so stale native sessions can be replaced safely.
136
+
137
+ ## Rendering and policy boundary
138
+
139
+ The `Small` and `Medium` templates, attribution, AdChoices, media, and clickable asset registration are owned by the plugin. The public style API intentionally exposes a limited set of cross-platform tokens instead of arbitrary native layouts or HTML asset rendering. This keeps Google SDK impression and click handling inside the native SDK.
140
+
141
+ Style colors use CSS-style `#RRGGBB` or `#RRGGBBAA` values on both platforms. Dimensions use logical pixels; font sizes use points on iOS and `sp` on Android. Invalid colors fall back to the template defaults. Negative dimensions are clamped to zero; headline, body, and call-to-action fonts are clamped to `12–24`, `10–18`, and `12–18` respectively.
142
+
143
+ Follow Google's native ad policies and implementation guidance for [Android](https://developers.google.com/admob/android/native/advanced) and [iOS](https://developers.google.com/admob/ios/native/advanced).
144
+
145
+ ## iOS scroll container
146
+
147
+ For a single Ionic scroll container, pass its actual scroll element when creating the feed:
148
+
149
+ ```ts
150
+ const feed = await NativeAdFeed.create({
151
+ feedId: 'articles',
152
+ isTesting: true,
153
+ scrollElement: await ionContent.getScrollElement(),
154
+ });
155
+ ```
156
+
157
+ On iOS, specifying `scrollElement` enables native scroll tracking for that container. Layout changes still require remeasurement; call `invalidateLayout()` after application-driven changes. Clipping inside the scroll container and at its visible bounds is retained. `pause()` and `destroy()` stop tracking. Other platforms ignore this option and retain JavaScript coordinate updates.
158
+
159
+ If the specified container cannot be identified unambiguously, the update fails and its ads stay hidden. Use one untransformed, unzoomed overflow scroll container; nested scrolling, document scrolling through this option, and virtual-scroll integrations are not supported. This does not change gesture routing for swipes that begin on an ad.
160
+
161
+ ## Android scroll snapshots
162
+
163
+ Set `androidScrollSnapshot: true` to display a cached image in the web feed while
164
+ scrolling and restore the native ad when scrolling stops. The demo enables this
165
+ option by default on Android.
166
+
167
+ ```ts
168
+ const feed = await NativeAdFeed.create({
169
+ feedId: 'articles',
170
+ adId: 'YOUR_ANDROID_NATIVE_AD_UNIT_ID',
171
+ androidScrollSnapshot: true,
172
+ });
173
+ ```
174
+
175
+ Use `isTesting: true` during development to request Google's test ads. For production,
176
+ provide your native ad unit ID in `adId` and omit `isTesting` or set it to `false`.
177
+ Snapshots are available in either mode. The option is supported only on Android.
178
+
179
+ WebView Native UI Engine manages snapshot capture and switching between the image
180
+ and native view. Each ad slot has its own projection. Images are refreshed when the
181
+ ad is reloaded or its layout changes; pausing, detaching, or destroying the feed
182
+ hides or removes the corresponding projections.
183
+
184
+ Video creatives and translucent backgrounds retain native tracking. If a snapshot
185
+ is unavailable, not ready, or exceeds the supported dimensions, the native view
186
+ continues tracking. Native SDK views handle ad interactions; snapshots do not
187
+ forward clicks or synthesize impressions.
package/docs/rewarded.md CHANGED
@@ -55,6 +55,19 @@ console.log(rewardItem);
55
55
 
56
56
  When no `adId` is passed to `showRewardVideoAd()`, the most recently prepared ad is shown.
57
57
 
58
+ ### Click events (since 8.2.0)
59
+
60
+ Register `adClicked` before showing a rewarded ad. This forwards clicks recorded by the SDK, including after a reward is earned while the ad remains displayed. It does not indicate that a reward was earned and does not apply to rewarded interstitials.
61
+
62
+ ```ts
63
+ const clickListener = await AdMob.addListener(RewardAdPluginEvents.adClicked, () => {
64
+ console.log('Rewarded ad clicked');
65
+ });
66
+
67
+ // When the owning screen is destroyed:
68
+ await clickListener.remove();
69
+ ```
70
+
58
71
  ### Prepare more than one ad
59
72
 
60
73
  ```ts
@@ -23,6 +23,11 @@ public class AdMobPlugin: CAPPlugin, CAPBridgedPlugin {
23
23
  CAPPluginMethod(name: "resumeBanner", returnType: CAPPluginReturnPromise),
24
24
  CAPPluginMethod(name: "hideBanner", returnType: CAPPluginReturnPromise),
25
25
  CAPPluginMethod(name: "removeBanner", returnType: CAPPluginReturnPromise),
26
+ CAPPluginMethod(name: "startNativeAdFeed", returnType: CAPPluginReturnPromise),
27
+ CAPPluginMethod(name: "destroyNativeAdFeed", returnType: CAPPluginReturnPromise),
28
+ CAPPluginMethod(name: "loadNativeAd", returnType: CAPPluginReturnPromise),
29
+ CAPPluginMethod(name: "updateNativeAdPlacements", returnType: CAPPluginReturnPromise),
30
+ CAPPluginMethod(name: "removeNativeAd", returnType: CAPPluginReturnPromise),
26
31
  CAPPluginMethod(name: "prepareInterstitial", returnType: CAPPluginReturnPromise),
27
32
  CAPPluginMethod(name: "showInterstitial", returnType: CAPPluginReturnPromise),
28
33
  CAPPluginMethod(name: "prepareRewardVideoAd", returnType: CAPPluginReturnPromise),
@@ -64,25 +69,40 @@ public class AdMobPlugin: CAPPlugin, CAPBridgedPlugin {
64
69
  private let adRewardExecutor = AdRewardExecutor()
65
70
  private let adRewardInterstitialExecutor = AdRewardInterstitialExecutor()
66
71
  private let consentExecutor = ConsentExecutor()
72
+ private let nativeAdExecutor = NativeAdExecutor()
67
73
 
68
74
  public override func load() {
69
75
  self.consentExecutor.plugin = self
70
76
  }
71
77
 
72
- /**
73
- * Enable SKAdNetwork to track conversions
74
- * https://developers.google.com/admob/ios/ios14
75
- */
76
- @objc func initialize(_ call: CAPPluginCall) {
77
- self.bannerExecutor.plugin = self
78
- self.adInterstitialExecutor.plugin = self
79
- self.adRewardExecutor.plugin = self
80
- self.adRewardInterstitialExecutor.plugin = self
81
- self.adInterstitialExecutor.plugin = self
82
- self.setRequestConfiguration(call)
78
+ /// Full-screen ads that currently have web media suspended.
79
+ private var webMediaSuspenders = Set<ObjectIdentifier>()
80
+
81
+ /// Pauses all web media while a full-screen ad is up, so WebKit releases its audio before the ad plays
82
+ /// and sets it up again afterwards, instead of leaving web audio silent until the app restarts.
83
+ func suspendWebMedia(for owner: AnyObject) {
84
+ Task { @MainActor [weak self] in
85
+ guard let self, self.webMediaSuspenders.insert(ObjectIdentifier(owner)).inserted,
86
+ self.webMediaSuspenders.count == 1
87
+ else { return }
88
+ self.setWebMediaPlaybackSuspended(true)
89
+ }
90
+ }
83
91
 
84
- MobileAds.shared.start(completionHandler: nil)
85
- call.resolve([:])
92
+ /// Only resumes once every ad that suspended has resumed, so a failed present (which can arrive
93
+ /// without a preceding adWillPresentFullScreenContent) can't release another ad's suspension.
94
+ func resumeWebMedia(for owner: AnyObject) {
95
+ Task { @MainActor [weak self] in
96
+ guard let self, self.webMediaSuspenders.remove(ObjectIdentifier(owner)) != nil,
97
+ self.webMediaSuspenders.isEmpty
98
+ else { return }
99
+ self.setWebMediaPlaybackSuspended(false)
100
+ }
101
+ }
102
+
103
+ @MainActor
104
+ func setWebMediaPlaybackSuspended(_ suspended: Bool) {
105
+ self.bridge?.webView?.setAllMediaPlaybackSuspended(suspended, completionHandler: nil)
86
106
  }
87
107
 
88
108
  /**
@@ -126,34 +146,12 @@ public class AdMobPlugin: CAPPlugin, CAPBridgedPlugin {
126
146
  }
127
147
  }
128
148
 
129
- /**
130
- * AdMob: Banner
131
- * https://developers.google.com/ad-manager/mobile-ads-sdk/ios/banner?hl=ja
132
- */
133
- @objc func showBanner(_ call: CAPPluginCall) {
134
- let adUnitID = getAdId(call, "ca-app-pub-3940256099942544/6300978111")
135
- let request = self.GADRequestWithOption(call.getBool("npa") ?? false)
136
-
137
- DispatchQueue.main.async {
138
- self.bannerExecutor.showBanner(call, request, adUnitID)
139
- }
140
- }
141
-
142
- @objc func hideBanner(_ call: CAPPluginCall) {
143
- DispatchQueue.main.async {
144
- self.bannerExecutor.hideBanner(call)
145
- }
146
- }
147
-
148
- @objc func resumeBanner(_ call: CAPPluginCall) {
149
- DispatchQueue.main.async {
150
- self.bannerExecutor.resumeBanner(call)
151
- }
152
- }
153
-
154
- @objc func removeBanner(_ call: CAPPluginCall) {
155
- DispatchQueue.main.async {
156
- self.bannerExecutor.removeBanner(call)
149
+ deinit {
150
+ let executor = nativeAdExecutor
151
+ if Thread.isMainThread {
152
+ executor.destroyAll()
153
+ } else {
154
+ DispatchQueue.main.async { executor.destroyAll() }
157
155
  }
158
156
  }
159
157
 
@@ -349,3 +347,67 @@ public class AdMobPlugin: CAPPlugin, CAPBridgedPlugin {
349
347
  return window?.rootViewController
350
348
  }
351
349
  }
350
+
351
+ extension AdMobPlugin {
352
+ // MARK: SDK initialization
353
+
354
+ @objc func initialize(_ call: CAPPluginCall) {
355
+ bannerExecutor.plugin = self
356
+ adInterstitialExecutor.plugin = self
357
+ adRewardExecutor.plugin = self
358
+ adRewardInterstitialExecutor.plugin = self
359
+ adInterstitialExecutor.plugin = self
360
+ nativeAdExecutor.plugin = self
361
+ setRequestConfiguration(call)
362
+
363
+ MobileAds.shared.start(completionHandler: nil)
364
+ call.resolve([:])
365
+ }
366
+
367
+ // MARK: Banner ads
368
+
369
+ @objc func showBanner(_ call: CAPPluginCall) {
370
+ let adUnitID = getAdId(call, "ca-app-pub-3940256099942544/6300978111")
371
+ let request = GADRequestWithOption(call.getBool("npa") ?? false)
372
+ DispatchQueue.main.async {
373
+ self.bannerExecutor.showBanner(call, request, adUnitID)
374
+ }
375
+ }
376
+
377
+ @objc func hideBanner(_ call: CAPPluginCall) {
378
+ DispatchQueue.main.async { self.bannerExecutor.hideBanner(call) }
379
+ }
380
+
381
+ @objc func resumeBanner(_ call: CAPPluginCall) {
382
+ DispatchQueue.main.async { self.bannerExecutor.resumeBanner(call) }
383
+ }
384
+
385
+ @objc func removeBanner(_ call: CAPPluginCall) {
386
+ DispatchQueue.main.async { self.bannerExecutor.removeBanner(call) }
387
+ }
388
+
389
+ // MARK: Native ads research preview
390
+
391
+ @objc func startNativeAdFeed(_ call: CAPPluginCall) {
392
+ DispatchQueue.main.async { self.nativeAdExecutor.startFeed(call) }
393
+ }
394
+
395
+ @objc func destroyNativeAdFeed(_ call: CAPPluginCall) {
396
+ DispatchQueue.main.async { self.nativeAdExecutor.destroyFeed(call) }
397
+ }
398
+
399
+ @objc func loadNativeAd(_ call: CAPPluginCall) {
400
+ let request = GADRequestWithOption(call.getBool("npa") ?? false)
401
+ DispatchQueue.main.async {
402
+ self.nativeAdExecutor.load(call, request: request)
403
+ }
404
+ }
405
+
406
+ @objc func updateNativeAdPlacements(_ call: CAPPluginCall) {
407
+ DispatchQueue.main.async { self.nativeAdExecutor.updatePlacements(call) }
408
+ }
409
+
410
+ @objc func removeNativeAd(_ call: CAPPluginCall) {
411
+ DispatchQueue.main.async { self.nativeAdExecutor.remove(call) }
412
+ }
413
+ }
@@ -1,3 +1,5 @@
1
+ // Preserve existing public Swift API names.
2
+ // swiftlint:disable identifier_name
1
3
  public enum AppOpenAdPluginEvents: String {
2
4
  case Loaded = "appOpenAdLoaded"
3
5
  case FailedToLoad = "appOpenAdFailedToLoad"
@@ -6,3 +8,4 @@ public enum AppOpenAdPluginEvents: String {
6
8
  case FailedToShow = "appOpenAdFailedToShow"
7
9
  case AdImpression = "appOpenAdImpression"
8
10
  }
11
+ // swiftlint:enable identifier_name
@@ -1,3 +1,5 @@
1
+ // Preserve existing public Swift API names.
2
+ // swiftlint:disable identifier_name
1
3
  public enum BannerAdPluginEvents: String {
2
4
  case SizeChanged = "bannerAdSizeChanged"
3
5
  case Closed = "bannerAdClosed"
@@ -7,3 +9,4 @@ public enum BannerAdPluginEvents: String {
7
9
  case AdImpression = "bannerAdImpression"
8
10
  case AdPaid = "bannerAdPaid"
9
11
  }
12
+ // swiftlint:enable identifier_name
@@ -1,3 +1,5 @@
1
+ // Preserve existing public Swift API names.
2
+ // swiftlint:disable identifier_name
1
3
  public enum BannerAdSizeEnum: String {
2
4
  case BANNER = "BANNER"
3
5
  case FULL_BANNER = "FULL_BANNER"
@@ -8,3 +10,4 @@ public enum BannerAdSizeEnum: String {
8
10
  case ADAPTIVE_BANNER = "ADAPTIVE_BANNER"
9
11
  // case FLUID = "FLUID"
10
12
  }
13
+ // swiftlint:enable identifier_name
@@ -6,7 +6,7 @@ class BannerExecutor: NSObject, BannerViewDelegate {
6
6
  weak var plugin: AdMobPlugin?
7
7
  var bannerView: BannerView!
8
8
  var adPosition: String = ""
9
- var Margin: Int = 0
9
+ var margin: Int = 0
10
10
 
11
11
  func showBanner(_ call: CAPPluginCall, _ request: Request, _ adUnitID: String) {
12
12
  if let rootViewController = plugin?.getRootVC() {
@@ -112,11 +112,11 @@ class BannerExecutor: NSObject, BannerViewDelegate {
112
112
  call.resolve([:])
113
113
  }
114
114
 
115
- private func addBannerViewToView(_ bannerView: BannerView, _ adPosition: String, _ Margin: Int) {
115
+ private func addBannerViewToView(_ bannerView: BannerView, _ adPosition: String, _ margin: Int) {
116
116
  removeBannerViewToView()
117
117
  bannerView.translatesAutoresizingMaskIntoConstraints = false
118
118
  bannerView.tag = 2743243288699 // rand
119
- self.Margin = Margin
119
+ self.margin = margin
120
120
  self.adPosition = adPosition
121
121
  }
122
122
 
@@ -142,7 +142,7 @@ class BannerExecutor: NSObject, BannerViewDelegate {
142
142
  toItem: rootViewController.view.safeAreaLayoutGuide,
143
143
  attribute: self.adPosition == "TOP_CENTER" ? .top : .bottom,
144
144
  multiplier: 1,
145
- constant: CGFloat(Int(self.Margin) * -1)),
145
+ constant: CGFloat(Int(self.margin) * -1)),
146
146
  NSLayoutConstraint(item: bannerView,
147
147
  attribute: .centerX,
148
148
  relatedBy: .equal,
@@ -1,6 +1,9 @@
1
+ // Preserve existing public Swift API names.
2
+ // swiftlint:disable identifier_name
1
3
  public enum AuthorizationStatusEnum: String {
2
4
  case Authorized = "authorized"
3
5
  case Denied = "denied"
4
6
  case NotDetermined = "notDetermined"
5
7
  case Restricted = "restricted"
6
8
  }
9
+ // swiftlint:enable identifier_name
@@ -73,6 +73,7 @@ class AdInterstitialExecutor: NSObject, FullScreenContentDelegate {
73
73
  }
74
74
 
75
75
  func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
76
+ self.plugin?.resumeWebMedia(for: ad)
76
77
  removeCurrentlyShowingAd()
77
78
  NSLog("InterstitialFullScreenDelegate Ad failed to present full screen content with error \(error.localizedDescription).")
78
79
  self.plugin?.notifyListeners(InterstitialAdPluginEvents.FailedToShow.rawValue, data: [
@@ -82,11 +83,13 @@ class AdInterstitialExecutor: NSObject, FullScreenContentDelegate {
82
83
  }
83
84
 
84
85
  func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
86
+ self.plugin?.suspendWebMedia(for: ad)
85
87
  NSLog("InterstitialFullScreenDelegate Ad did present full screen content.")
86
88
  self.plugin?.notifyListeners(InterstitialAdPluginEvents.Showed.rawValue, data: [:])
87
89
  }
88
90
 
89
91
  func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
92
+ self.plugin?.resumeWebMedia(for: ad)
90
93
  removeCurrentlyShowingAd()
91
94
  NSLog("InterstitialFullScreenDelegate Ad did dismiss full screen content.")
92
95
  self.plugin?.notifyListeners(InterstitialAdPluginEvents.Dismissed.rawValue, data: [:])
@@ -1,3 +1,5 @@
1
+ // Preserve existing public Swift API names.
2
+ // swiftlint:disable identifier_name
1
3
  public enum InterstitialAdPluginEvents: String {
2
4
  case Loaded = "interstitialAdLoaded"
3
5
  case FailedToLoad = "interstitialAdFailedToLoad"
@@ -6,3 +8,4 @@ public enum InterstitialAdPluginEvents: String {
6
8
  case Dismissed = "interstitialAdDismissed"
7
9
  case AdImpression = "interstitialAdImpression"
8
10
  }
11
+ // swiftlint:enable identifier_name