@capacitor-community/admob 8.1.0 → 8.2.0-beta.pr454.sha49386d40323a
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/CapacitorCommunityAdmob.podspec +1 -1
- package/Package.swift +1 -1
- package/README.md +129 -323
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +86 -3
- package/android/src/main/java/com/getcapacitor/community/admob/banner/BannerExecutor.java +261 -141
- package/android/src/main/java/com/getcapacitor/community/admob/helpers/FullscreenPluginCallback.kt +5 -1
- package/android/src/main/java/com/getcapacitor/community/admob/interstitial/AdInterstitialExecutor.java +27 -31
- package/android/src/main/java/com/getcapacitor/community/admob/interstitial/InterstitialAdCallbackAndListeners.kt +1 -1
- package/android/src/main/java/com/getcapacitor/community/admob/models/LoadPluginEventNames.kt +2 -1
- package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdExecutor.kt +378 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdFeedSessions.kt +35 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdPlacementValue.kt +44 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativeads/NativeAdPluginEvents.kt +11 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativeads/PluginNativeAdView.kt +187 -0
- package/android/src/main/java/com/getcapacitor/community/admob/rewarded/AdRewardExecutor.java +29 -33
- package/android/src/main/java/com/getcapacitor/community/admob/rewarded/RewardAdPluginEvents.kt +2 -1
- package/android/src/main/java/com/getcapacitor/community/admob/rewarded/RewardedAdCallbackAndListeners.kt +1 -1
- package/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/AdRewardInterstitialExecutor.java +29 -37
- package/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardedInterstitialAdCallbackAndListeners.kt +1 -1
- package/dist/docs.json +33 -3
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +5 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interstitial/interstitial-definitions.interface.d.ts +3 -0
- package/dist/esm/native-ads/index.d.ts +6 -0
- package/dist/esm/native-ads/index.js +4 -0
- package/dist/esm/native-ads/index.js.map +1 -0
- package/dist/esm/native-ads/native-ad-definitions.interface.d.ts +14 -0
- package/dist/esm/native-ads/native-ad-definitions.interface.js +2 -0
- package/dist/esm/native-ads/native-ad-definitions.interface.js.map +1 -0
- package/dist/esm/native-ads/native-ad-element.d.ts +9 -0
- package/dist/esm/native-ads/native-ad-element.js +53 -0
- package/dist/esm/native-ads/native-ad-element.js.map +1 -0
- package/dist/esm/native-ads/native-ad-event.interface.d.ts +5 -0
- package/dist/esm/native-ads/native-ad-event.interface.js +2 -0
- package/dist/esm/native-ads/native-ad-event.interface.js.map +1 -0
- package/dist/esm/native-ads/native-ad-feed.d.ts +72 -0
- package/dist/esm/native-ads/native-ad-feed.js +568 -0
- package/dist/esm/native-ads/native-ad-feed.js.map +1 -0
- package/dist/esm/native-ads/native-ad-geometry.d.ts +8 -0
- package/dist/esm/native-ads/native-ad-geometry.js +91 -0
- package/dist/esm/native-ads/native-ad-geometry.js.map +1 -0
- package/dist/esm/native-ads/native-ad-options.interface.d.ts +36 -0
- package/dist/esm/native-ads/native-ad-options.interface.js +2 -0
- package/dist/esm/native-ads/native-ad-options.interface.js.map +1 -0
- package/dist/esm/native-ads/native-ad-placement.interface.d.ts +26 -0
- package/dist/esm/native-ads/native-ad-placement.interface.js +2 -0
- package/dist/esm/native-ads/native-ad-placement.interface.js.map +1 -0
- package/dist/esm/native-ads/native-ad-plugin-events.enum.d.ts +9 -0
- package/dist/esm/native-ads/native-ad-plugin-events.enum.js +11 -0
- package/dist/esm/native-ads/native-ad-plugin-events.enum.js.map +1 -0
- package/dist/esm/native-ads/native-ad-style.interface.d.ts +30 -0
- package/dist/esm/native-ads/native-ad-style.interface.js +2 -0
- package/dist/esm/native-ads/native-ad-style.interface.js.map +1 -0
- package/dist/esm/native-ads/native-ad-template.enum.d.ts +11 -0
- package/dist/esm/native-ads/native-ad-template.enum.js +13 -0
- package/dist/esm/native-ads/native-ad-template.enum.js.map +1 -0
- package/dist/esm/reward/reward-ad-plugin-events.enum.d.ts +4 -0
- package/dist/esm/reward/reward-ad-plugin-events.enum.js +4 -0
- package/dist/esm/reward/reward-ad-plugin-events.enum.js.map +1 -1
- package/dist/esm/reward/reward-definitions.interface.d.ts +8 -0
- package/dist/esm/reward-interstitial/reward-interstitial-definitions.interface.d.ts +3 -0
- package/dist/esm/web.d.ts +9 -1
- package/dist/esm/web.js +15 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +755 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +755 -1
- package/dist/plugin.js.map +1 -1
- package/docs/app-open.md +53 -0
- package/docs/banner.md +100 -0
- package/docs/configuration.md +30 -0
- package/docs/consent.md +80 -0
- package/docs/events.md +76 -0
- package/docs/interstitial.md +47 -0
- package/docs/migration.md +146 -0
- package/docs/native-ads.md +161 -0
- package/docs/rewarded.md +148 -0
- package/docs/testing.md +67 -0
- package/ios/Sources/AdMobPlugin/AdMobPlugin.swift +106 -41
- package/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift +1 -1
- package/ios/Sources/AdMobPlugin/Banner/BannerExecutor.swift +1 -1
- package/ios/Sources/AdMobPlugin/Interstitial/AdInterstitialExecutor.swift +6 -3
- package/ios/Sources/AdMobPlugin/NativeAds/NativeAdExecutor.swift +354 -0
- package/ios/Sources/AdMobPlugin/NativeAds/NativeAdFeedSessions.swift +34 -0
- package/ios/Sources/AdMobPlugin/NativeAds/NativeAdPlacementValue.swift +50 -0
- package/ios/Sources/AdMobPlugin/NativeAds/NativeAdPluginEvents.swift +11 -0
- package/ios/Sources/AdMobPlugin/NativeAds/NativeAdScrollTracker.swift +66 -0
- package/ios/Sources/AdMobPlugin/NativeAds/NativeAdValues.swift +27 -0
- package/ios/Sources/AdMobPlugin/NativeAds/PluginNativeAdView.swift +187 -0
- package/ios/Sources/AdMobPlugin/Rewarded/AdRewardExecutor.swift +10 -3
- package/ios/Sources/AdMobPlugin/Rewarded/RewardAdPluginEvents.swift +1 -0
- package/ios/Sources/AdMobPlugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift +6 -3
- package/ios/Tests/AdMobPluginTests/AdMobPluginTests.swift +82 -10
- package/ios/Tests/AdMobPluginTests/NativeAdScrollTrackerTests.swift +47 -0
- package/package.json +4 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Interstitial Ads
|
|
2
|
+
|
|
3
|
+
Interstitial ads are full-screen ads that cover the host app. Show them at a natural transition, such as between activities or game levels. The user can tap through or close the ad and return to the app. Google's interstitial guides for [Android](https://developers.google.com/admob/android/interstitial) and [iOS](https://developers.google.com/admob/ios/interstitial) explain the format.
|
|
4
|
+
|
|
5
|
+
Use an interstitial when the user should not receive an in-app reward. Call this after [initialize](https://docs.rdlabo.dev/projects/capacitor-admob/docs/configuration) and [consent](https://docs.rdlabo.dev/projects/capacitor-admob/docs/consent). Prepare the ad ahead of time, register listeners first, and show it only when it is ready.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { AdLoadInfo, AdMob, AdMobRevenueData, AdOptions, InterstitialAdPluginEvents } from '@capacitor-community/admob';
|
|
9
|
+
|
|
10
|
+
await AdMob.addListener(InterstitialAdPluginEvents.Loaded, (info: AdLoadInfo) => {
|
|
11
|
+
console.log('Interstitial loaded', info.adUnitId);
|
|
12
|
+
});
|
|
13
|
+
await AdMob.addListener(InterstitialAdPluginEvents.FailedToLoad, console.error);
|
|
14
|
+
await AdMob.addListener(InterstitialAdPluginEvents.AdImpression, (data: AdMobRevenueData) => {
|
|
15
|
+
console.log(data);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const options: AdOptions = {
|
|
19
|
+
adId: 'YOUR_AD_UNIT_ID',
|
|
20
|
+
// isTesting: true,
|
|
21
|
+
// npa: true,
|
|
22
|
+
// immersiveMode: true,
|
|
23
|
+
};
|
|
24
|
+
const { adUnitId } = await AdMob.prepareInterstitial(options);
|
|
25
|
+
await AdMob.showInterstitial({ adId: adUnitId });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
<!-- !::prepareInterstitial:: -->
|
|
29
|
+
|
|
30
|
+
<!-- !::showInterstitial:: -->
|
|
31
|
+
|
|
32
|
+
<!-- !::AdOptions:: -->
|
|
33
|
+
|
|
34
|
+
When no `adId` is passed to `showInterstitial()`, the most recently prepared ad is shown.
|
|
35
|
+
|
|
36
|
+
## Prepare more than one ad
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
await AdMob.prepareInterstitial({ adId: 'ca-app-pub-xxx/interstitial-1' });
|
|
40
|
+
await AdMob.prepareInterstitial({ adId: 'ca-app-pub-xxx/interstitial-2' });
|
|
41
|
+
|
|
42
|
+
await AdMob.showInterstitial({ adId: 'ca-app-pub-xxx/interstitial-1' });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
See [Testing](https://docs.rdlabo.dev/projects/capacitor-admob/docs/testing) for `isTesting`.
|
|
46
|
+
|
|
47
|
+
Load, show, dismissal, and failure events are listed on [Ad Events](https://docs.rdlabo.dev/projects/capacitor-admob/docs/events).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Migration Guide
|
|
2
|
+
|
|
3
|
+
## Changes in 8.2.0
|
|
4
|
+
|
|
5
|
+
### Capacitor 8.5 or later is required
|
|
6
|
+
|
|
7
|
+
Update `@capacitor/core`, `@capacitor/cli`, and the native platform packages you use (`@capacitor/android` / `@capacitor/ios`) to 8.5 or later within v8, then run `npx cap sync`.
|
|
8
|
+
|
|
9
|
+
Older Capacitor versions can consume Android window insets before they reach a banner. Update Capacitor to use its current safe-area handling. Follow the [Capacitor 8.5 update guide](https://capacitorjs.com/docs/updating/8-5) for native project migration steps.
|
|
10
|
+
|
|
11
|
+
### Android initialization can reject
|
|
12
|
+
|
|
13
|
+
`AdMob.initialize()` now waits for the native banner parent view. If it does not appear within 5 seconds, initialization rejects; an unavailable activity/content view can fail immediately. This also applies to apps that only use full-screen ads. Previously, a missing child view could let initialization resolve and cause later banner requests to crash.
|
|
14
|
+
|
|
15
|
+
Handle initialization errors without blocking app startup. Retry when the native view is available. See [Configuration](https://docs.rdlabo.dev/projects/capacitor-admob/docs/configuration) for an example.
|
|
16
|
+
|
|
17
|
+
### iOS revenue values now use micros
|
|
18
|
+
|
|
19
|
+
The `valueMicros` field now correctly reports millionths of a currency unit for banners, interstitials, rewarded ads, rewarded interstitials, and app-open ads. For example, a value of `0.0012` currency units previously produced `0`; it now produces `1200`. Android values and event names are unchanged.
|
|
20
|
+
|
|
21
|
+
Divide `valueMicros` by `1_000_000` to obtain currency units on either platform. Review any iOS-specific workarounds in your analytics pipeline. Historical iOS values were truncated before conversion, so multiplying those stored values cannot recover the lost fractional revenue.
|
|
22
|
+
|
|
23
|
+
### Consent before SDK initialization
|
|
24
|
+
|
|
25
|
+
On iOS, `showConsentForm()` and `showPrivacyOptionsForm()` can now be called before `AdMob.initialize()`, matching Android. Request consent information, present a form if required, then initialize the Mobile Ads SDK and load ads only when `canRequestAds` is true. See [Consent](https://docs.rdlabo.dev/projects/capacitor-admob/docs/consent) for the complete sequence. Existing initialize-first integrations remain callable, but should adopt this order.
|
|
26
|
+
|
|
27
|
+
### Ad load errors preserve native codes
|
|
28
|
+
|
|
29
|
+
`prepareInterstitial()`, `prepareRewardVideoAd()`, and `prepareRewardInterstitialAd()` now reject SDK load failures with a string `code` and the native error message. Codes are platform-specific, not normalized across Android and iOS. `FailedToLoad` event codes remain numbers; on iOS they now contain the actual SDK code instead of a fixed `0`.
|
|
30
|
+
|
|
31
|
+
Update error handlers that match the iOS message `Loading failed` or assume an event code of `0`. Use the platform's SDK error definitions when handling no-fill and other failures.
|
|
32
|
+
|
|
33
|
+
### Rewarded ad click event
|
|
34
|
+
|
|
35
|
+
`RewardAdPluginEvents.adClicked` is a new optional listener for rewarded ads on Android and iOS. Its event string is `onRewardedVideoAdClicked`. Clicks are separate from earned rewards; continue granting rewards only from the `Rewarded` event or the show result, once. See [Rewarded Ads](https://docs.rdlabo.dev/projects/capacitor-admob/docs/rewarded).
|
|
36
|
+
|
|
37
|
+
### AGP 9 build compatibility
|
|
38
|
+
|
|
39
|
+
The Android library now references `proguard-android-optimize.txt`, avoiding AGP 9's rejection of the older default file. This does not enable library minification or upgrade your project's AGP. Other AGP 9 migration steps still apply to the host app.
|
|
40
|
+
|
|
41
|
+
The version-by-version steps under “Breaking changes from earlier versions” apply to releases before v8.
|
|
42
|
+
|
|
43
|
+
## Google Mobile Ads SDK versions
|
|
44
|
+
|
|
45
|
+
This major version keeps Google Mobile Ads SDK APIs that are deprecated but still supported. Replacing them can change banner sizing and age-restricted treatment, so that work waits for the next major.
|
|
46
|
+
|
|
47
|
+
Google's [Next-Gen SDK for Android](https://developers.google.com/admob/android/next-gen) also waits for the next major: it changes SDK initialization, ad requests, and mediation.
|
|
48
|
+
|
|
49
|
+
Pinned versions: Android 25.4.x, iOS 13.11.0 (Swift Package Manager and CocoaPods). CocoaPods support is planned to be removed in the next major.
|
|
50
|
+
|
|
51
|
+
Android remains on 25.4.x because [SDK 25.5.0 raises the minimum Android API level](https://developers.google.com/admob/android/rel-notes). The iOS update from 13.6.0 to 13.11.0 keeps the plugin's existing platform and toolchain requirements; no public plugin API changes are required.
|
|
52
|
+
|
|
53
|
+
## Breaking changes from earlier versions
|
|
54
|
+
|
|
55
|
+
### 1.1.0
|
|
56
|
+
|
|
57
|
+
- Prepare for iOS 14+
|
|
58
|
+
- In file `ios/App/App/AppDelegate.swift` remove the following:
|
|
59
|
+
|
|
60
|
+
```diff
|
|
61
|
+
- import GoogleMobileAds
|
|
62
|
+
|
|
63
|
+
@UIApplicationMain
|
|
64
|
+
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
65
|
+
|
|
66
|
+
var window: UIWindow?
|
|
67
|
+
|
|
68
|
+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
69
|
+
- // Override point for customization after application launch.
|
|
70
|
+
- GADMobileAds.sharedInstance().start(completionHandler: nil)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 0.2.13
|
|
74
|
+
|
|
75
|
+
- isTest: 'LIVE' | 'TESTING' => boolean
|
|
76
|
+
|
|
77
|
+
### 0.2.12
|
|
78
|
+
|
|
79
|
+
**app.component.ts**
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { Plugins } from '@capacitor/core';
|
|
83
|
+
|
|
84
|
+
const { AdMob } = Plugins;
|
|
85
|
+
|
|
86
|
+
@Component({
|
|
87
|
+
selector: 'app-root',
|
|
88
|
+
templateUrl: 'app.component.html',
|
|
89
|
+
styleUrls: ['app.component.scss'],
|
|
90
|
+
})
|
|
91
|
+
export class AppComponent {
|
|
92
|
+
constructor() {
|
|
93
|
+
// Initialize AdMob for your Application
|
|
94
|
+
+AdMob.initialize('[APP_ID]');
|
|
95
|
+
-AdMob.initialize();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**admob.component.ts**
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { Plugins } from '@capacitor/core';
|
|
104
|
+
import { AdOptions, AdSize, AdPosition } from '@rdlabo/capacitor-admob';
|
|
105
|
+
|
|
106
|
+
const { AdMob } = Plugins;
|
|
107
|
+
|
|
108
|
+
@Component({
|
|
109
|
+
selector: 'admob',
|
|
110
|
+
templateUrl: 'admob.component.html',
|
|
111
|
+
styleUrls: ['admob.component.scss']
|
|
112
|
+
})
|
|
113
|
+
export class AdMobComponent {
|
|
114
|
+
|
|
115
|
+
const options: AdOptions = {
|
|
116
|
+
adId: 'YOUR ADID',
|
|
117
|
+
adSize: AdSize.BANNER,
|
|
118
|
+
position: AdPosition.BOTTOM_CENTER,
|
|
119
|
+
- margin: '0',
|
|
120
|
+
+ margin: 0,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
constructor(){
|
|
124
|
+
// Show Banner Ad
|
|
125
|
+
AdMob.showBanner(this.options)
|
|
126
|
+
.then(
|
|
127
|
+
(value) => {
|
|
128
|
+
console.log(value); // true
|
|
129
|
+
},
|
|
130
|
+
(error) => {
|
|
131
|
+
console.error(error); // show error
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Subscibe Banner Event Listener
|
|
136
|
+
AdMob.addListener('onAdLoaded', (info: boolean) => {
|
|
137
|
+
console.log("Banner Ad Loaded");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
+ // Get Banner Size
|
|
141
|
+
+ AdMob.addListener('onAdSize', (info: boolean) => {
|
|
142
|
+
+ console.log(info);
|
|
143
|
+
+ });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Native Ads (Preview)
|
|
2
|
+
|
|
3
|
+
**Native Ads is a Preview feature.** You can use it for testing and in production. Its API may change in a minor release while in Preview.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
Browsers and PWAs are unsupported. `NativeAdFeed.create()` rejects instead of leaving an empty ad slot.
|
|
10
|
+
|
|
11
|
+
## Create a feed
|
|
12
|
+
|
|
13
|
+
Create one manager for each visible feed screen after initializing AdMob and obtaining consent.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { NativeAdFeed, NativeAdPluginEvents, NativeAdTemplate } from '@capacitor-community/admob';
|
|
17
|
+
|
|
18
|
+
const nativeAds = await NativeAdFeed.create({
|
|
19
|
+
feedId: 'home-feed',
|
|
20
|
+
template: NativeAdTemplate.Medium,
|
|
21
|
+
isTesting: true,
|
|
22
|
+
style: {
|
|
23
|
+
backgroundColor: '#ffffff',
|
|
24
|
+
cornerRadius: 12,
|
|
25
|
+
headlineColor: '#111827',
|
|
26
|
+
callToActionBackgroundColor: '#2563eb',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const paidHandle = await nativeAds.addListener(NativeAdPluginEvents.AdPaid, (event) => {
|
|
31
|
+
console.log(event.slotKey, event.valueMicros, event.currencyCode);
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use the framework-independent element in ordinary or virtualized markup. `slot-key` must identify the logical ad item, not its array index and not the recycled DOM node.
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<capacitor-admob-native feed-id="home-feed" slot-key="sponsored-after-article-42"></capacitor-admob-native>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
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.
|
|
42
|
+
|
|
43
|
+
`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.
|
|
44
|
+
|
|
45
|
+
If a framework does not accept custom elements, attach an ordinary element instead:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
nativeAds.attach('sponsored-after-article-42', element);
|
|
49
|
+
|
|
50
|
+
// Before the element is destroyed or reused for another logical item:
|
|
51
|
+
nativeAds.detach(element);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Destroy the manager with its screen:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
await paidHandle.remove();
|
|
58
|
+
await nativeAds.destroy();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Virtual-scroll integration experiments
|
|
62
|
+
|
|
63
|
+
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:
|
|
64
|
+
|
|
65
|
+
- Put the logical ad ID in `slot-key` (Angular binding, React prop/ref, or Vue `:slot-key`). Never use the rendered index.
|
|
66
|
+
- Keep the ad row at a fixed height and include that height in the virtual scroller's item-size calculation.
|
|
67
|
+
- 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.
|
|
68
|
+
- 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.
|
|
69
|
+
|
|
70
|
+
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:
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
<capacitor-admob-native feed-id="home-feed" [attr.slot-key]="item.stableAdKey"></capacitor-admob-native>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
With React, `createElement` keeps custom dash-cased attributes type-safe without adding a framework dependency to the plugin:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { createElement } from 'react';
|
|
80
|
+
|
|
81
|
+
const NativeAdRow = ({ slotKey }: { slotKey: string }) =>
|
|
82
|
+
createElement('capacitor-admob-native', {
|
|
83
|
+
'feed-id': 'home-feed',
|
|
84
|
+
'slot-key': slotKey,
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For Vue, mark the tag as a custom element in the Vue compiler and bind the stable key:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// vite.config.ts
|
|
92
|
+
vue({
|
|
93
|
+
template: {
|
|
94
|
+
compilerOptions: {
|
|
95
|
+
isCustomElement: (tag) => tag === 'capacitor-admob-native',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<capacitor-admob-native feed-id="home-feed" :slot-key="item.stableAdKey" />
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
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.
|
|
106
|
+
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
## Layout and overlay lifecycle
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
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:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
await nativeAds.pause();
|
|
117
|
+
await modal.present();
|
|
118
|
+
await modal.onDidDismiss();
|
|
119
|
+
nativeAds.resume();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
## API
|
|
125
|
+
|
|
126
|
+
| Member | Purpose |
|
|
127
|
+
| ---------------------------------------------- | -------------------------------------------------------------- |
|
|
128
|
+
| `NativeAdFeed.create(options)` | Starts a native feed session. Rejects outside iOS and Android. |
|
|
129
|
+
| `feedId` | Normalized feed identifier used by the custom element. |
|
|
130
|
+
| `addListener(event, listener)` | Adds a listener filtered to this feed session. |
|
|
131
|
+
| `attach(slotKey, element)` / `detach(element)` | Advanced lifecycle for an ordinary element. |
|
|
132
|
+
| `reload(slotKey)` | Explicitly removes and reloads one registered slot. |
|
|
133
|
+
| `pause(): Promise<void>` / `resume(): void` | Hides placements during Web overlays or inactive page states. |
|
|
134
|
+
| `invalidateLayout(): Promise<void>` | Hides and remeasures after an application-driven reflow. |
|
|
135
|
+
| `destroy()` | Removes listeners and all native resources for the session. |
|
|
136
|
+
|
|
137
|
+
`NativeAdFeedOptions` contains `feedId`, `adId`, `template`, `style`, `isTesting`, `npa`, and the optional iOS `scrollElement`. 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.
|
|
138
|
+
|
|
139
|
+
## Rendering and policy boundary
|
|
140
|
+
|
|
141
|
+
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.
|
|
142
|
+
|
|
143
|
+
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.
|
|
144
|
+
|
|
145
|
+
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).
|
|
146
|
+
|
|
147
|
+
## iOS scroll container
|
|
148
|
+
|
|
149
|
+
For a single Ionic scroll container, pass its actual scroll element when creating the feed:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const feed = await NativeAdFeed.create({
|
|
153
|
+
feedId: 'articles',
|
|
154
|
+
isTesting: true,
|
|
155
|
+
scrollElement: await ionContent.getScrollElement(),
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
On iOS, specifying `scrollElement` enables experimental 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.
|
|
160
|
+
|
|
161
|
+
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.
|
package/docs/rewarded.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Rewarded Ads
|
|
2
|
+
|
|
3
|
+
Rewarded ads let you give in-app items for interacting with video ads, playable ads, or surveys. Google's rewarded guides for [Android](https://developers.google.com/admob/android/rewarded) and [iOS](https://developers.google.com/admob/ios/rewarded) explain the format.
|
|
4
|
+
|
|
5
|
+
Treat rewarded ads as a reward flow, not as another non-rewarded interstitial. Call this after [initialize](https://docs.rdlabo.dev/projects/capacitor-admob/docs/configuration) and [consent](https://docs.rdlabo.dev/projects/capacitor-admob/docs/consent). Grant the reward only from the returned result or the `Rewarded` event, not from `Dismissed`.
|
|
6
|
+
|
|
7
|
+
## Rewarded video
|
|
8
|
+
|
|
9
|
+
Use a rewarded ad for a dedicated reward flow.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
AdLoadInfo,
|
|
14
|
+
AdMob,
|
|
15
|
+
AdMobRevenueData,
|
|
16
|
+
AdMobRewardItem,
|
|
17
|
+
RewardAdOptions,
|
|
18
|
+
RewardAdPluginEvents,
|
|
19
|
+
} from '@capacitor-community/admob';
|
|
20
|
+
|
|
21
|
+
await AdMob.addListener(RewardAdPluginEvents.Loaded, (info: AdLoadInfo) => {
|
|
22
|
+
console.log('Rewarded ad loaded', info.adUnitId);
|
|
23
|
+
});
|
|
24
|
+
await AdMob.addListener(RewardAdPluginEvents.FailedToLoad, console.error);
|
|
25
|
+
await AdMob.addListener(RewardAdPluginEvents.Rewarded, (reward: AdMobRewardItem) => {
|
|
26
|
+
console.log('Reward earned', reward.amount, reward.type);
|
|
27
|
+
});
|
|
28
|
+
await AdMob.addListener(RewardAdPluginEvents.AdImpression, (data: AdMobRevenueData) => {
|
|
29
|
+
console.log(data);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const options: RewardAdOptions = {
|
|
33
|
+
adId: 'YOUR_AD_UNIT_ID',
|
|
34
|
+
// isTesting: true,
|
|
35
|
+
// npa: true,
|
|
36
|
+
// immersiveMode: true,
|
|
37
|
+
// ssv: {
|
|
38
|
+
// userId: 'USER_ID',
|
|
39
|
+
// customData: JSON.stringify({ placement: 'bonus' }),
|
|
40
|
+
// },
|
|
41
|
+
};
|
|
42
|
+
await AdMob.prepareRewardVideoAd(options);
|
|
43
|
+
const rewardItem = await AdMob.showRewardVideoAd();
|
|
44
|
+
// Grant the reward once, using this result or the Rewarded event — not both.
|
|
45
|
+
console.log(rewardItem);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
<!-- !::prepareRewardVideoAd:: -->
|
|
49
|
+
|
|
50
|
+
<!-- !::showRewardVideoAd:: -->
|
|
51
|
+
|
|
52
|
+
<!-- !::RewardAdOptions:: -->
|
|
53
|
+
|
|
54
|
+
<!-- !::AdMobRewardItem:: -->
|
|
55
|
+
|
|
56
|
+
When no `adId` is passed to `showRewardVideoAd()`, the most recently prepared ad is shown.
|
|
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
|
+
|
|
71
|
+
### Prepare more than one ad
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
await AdMob.prepareRewardVideoAd({ adId: 'ca-app-pub-xxx/reward-1' });
|
|
75
|
+
await AdMob.prepareRewardVideoAd({ adId: 'ca-app-pub-xxx/reward-2' });
|
|
76
|
+
|
|
77
|
+
const reward = await AdMob.showRewardVideoAd({ adId: 'ca-app-pub-xxx/reward-1' });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Rewarded interstitial
|
|
81
|
+
|
|
82
|
+
Rewarded interstitial ads are incentivized full-screen ads that appear during natural app transitions. Unlike rewarded video, the user does not opt in first. Google's rewarded interstitial guides for [Android](https://developers.google.com/admob/android/rewarded-interstitial) and [iOS](https://developers.google.com/admob/ios/rewarded-interstitial) explain the format.
|
|
83
|
+
|
|
84
|
+
Use a rewarded interstitial when the rewarded experience belongs at a natural transition in the app.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import {
|
|
88
|
+
AdMob,
|
|
89
|
+
AdMobRewardInterstitialItem,
|
|
90
|
+
RewardInterstitialAdOptions,
|
|
91
|
+
RewardInterstitialAdPluginEvents,
|
|
92
|
+
} from '@capacitor-community/admob';
|
|
93
|
+
|
|
94
|
+
await AdMob.addListener(RewardInterstitialAdPluginEvents.FailedToLoad, console.error);
|
|
95
|
+
|
|
96
|
+
const options: RewardInterstitialAdOptions = {
|
|
97
|
+
adId: 'YOUR_AD_UNIT_ID',
|
|
98
|
+
};
|
|
99
|
+
const { adUnitId } = await AdMob.prepareRewardInterstitialAd(options);
|
|
100
|
+
const rewardItem: AdMobRewardInterstitialItem = await AdMob.showRewardInterstitialAd({
|
|
101
|
+
adId: adUnitId,
|
|
102
|
+
});
|
|
103
|
+
console.log(rewardItem);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
<!-- !::prepareRewardInterstitialAd:: -->
|
|
107
|
+
|
|
108
|
+
<!-- !::showRewardInterstitialAd:: -->
|
|
109
|
+
|
|
110
|
+
<!-- !::RewardInterstitialAdOptions:: -->
|
|
111
|
+
|
|
112
|
+
<!-- !::AdMobRewardInterstitialItem:: -->
|
|
113
|
+
|
|
114
|
+
See [Testing](https://docs.rdlabo.dev/projects/capacitor-admob/docs/testing) for `isTesting`.
|
|
115
|
+
|
|
116
|
+
## Server-side verification
|
|
117
|
+
|
|
118
|
+
Server-side verification (SSV) lets your backend confirm that a reward was earned. See Google's [SSV documentation](https://support.google.com/admob/answer/9603226). Callbacks fire only for production ads; test ads do not invoke your SSV endpoint.
|
|
119
|
+
|
|
120
|
+
For local validation of the `ssv` payload, you can send a mock request after `RewardAdPluginEvents.Rewarded`. Replace `ENVIRONMENT_IS_DEVELOPMENT` with your own development flag:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const userId = 'USER_ID';
|
|
124
|
+
const customData = JSON.stringify({ placement: 'bonus' });
|
|
125
|
+
|
|
126
|
+
await AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => {
|
|
127
|
+
if (!ENVIRONMENT_IS_DEVELOPMENT) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const params = new URLSearchParams({
|
|
132
|
+
ad_network: 'TEST',
|
|
133
|
+
ad_unit: 'TEST',
|
|
134
|
+
custom_data: customData,
|
|
135
|
+
reward_amount: 'TEST',
|
|
136
|
+
reward_item: 'TEST',
|
|
137
|
+
timestamp: 'TEST',
|
|
138
|
+
transaction_id: 'TEST',
|
|
139
|
+
user_id: userId,
|
|
140
|
+
signature: 'TEST',
|
|
141
|
+
key_id: 'TEST',
|
|
142
|
+
});
|
|
143
|
+
await fetch(`https://your-staging-ssv-endpoint?${params.toString()}`);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(err);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
```
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
Use test ads during development so you can click ads without charging advertisers or flagging the account for invalid traffic. Google's test ads guides for [Android](https://developers.google.com/admob/android/test-ads) and [iOS](https://developers.google.com/admob/ios/test-ads) explain demo ad units and test devices.
|
|
4
|
+
|
|
5
|
+
This plugin can request those demo units or register a device through `initialize`.
|
|
6
|
+
|
|
7
|
+
## Demo ad units
|
|
8
|
+
|
|
9
|
+
Google provides [demo ad units](https://developers.google.com/admob/android/test-ads#demo_ad_units) that always return test ads. Prefer these during development. This plugin supports native iOS and Android only—use the matching platform ID.
|
|
10
|
+
|
|
11
|
+
Banner demo units used by this plugin and its demo:
|
|
12
|
+
|
|
13
|
+
| Platform | Banner `adId` |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Android | `ca-app-pub-3940256099942544/6300978111` |
|
|
16
|
+
| iOS | `ca-app-pub-3940256099942544/2934735716` |
|
|
17
|
+
|
|
18
|
+
Other format demo IDs are listed in Google's test-ads guides linked above. You can also set `isTesting: true` on banner, interstitial, rewarded, and rewarded interstitial requests. App Open ads have no `isTesting` option; pass a demo ad unit as `adId`.
|
|
19
|
+
|
|
20
|
+
## Test devices
|
|
21
|
+
|
|
22
|
+
To request production-like ads on a physical device without generating invalid traffic, register the device with `AdMob.initialize()`:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
await AdMob.initialize({
|
|
26
|
+
testingDevices: ['YOUR_TEST_DEVICE_ID'],
|
|
27
|
+
initializeForTesting: true,
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
<!-- !::initialize:: -->
|
|
32
|
+
|
|
33
|
+
<!-- !::AdMobInitializationOptions:: -->
|
|
34
|
+
|
|
35
|
+
Find the device ID in the native logs after the first ad request:
|
|
36
|
+
|
|
37
|
+
- Android: Logcat, typically on the `Ads` tag (`Use RequestConfiguration.Builder.setTestDeviceIds(...)`).
|
|
38
|
+
- iOS: Xcode console (`To get test ads on this device, set:`).
|
|
39
|
+
|
|
40
|
+
See Google's [enable test devices](https://developers.google.com/admob/android/test-ads#enable_test_devices) guide.
|
|
41
|
+
|
|
42
|
+
## Consent debug geography
|
|
43
|
+
|
|
44
|
+
On a real device, set `debugGeography` and include the device ID in `testDeviceIdentifiers`. `EEA` makes the form behave as if the device were in the European Economic Area, so you can test GDPR messaging. Use this only on registered test devices.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { AdMob, AdmobConsentDebugGeography } from '@capacitor-community/admob';
|
|
48
|
+
|
|
49
|
+
const consentInfo = await AdMob.requestConsentInfo({
|
|
50
|
+
debugGeography: AdmobConsentDebugGeography.EEA,
|
|
51
|
+
testDeviceIdentifiers: ['YOUR_TEST_DEVICE_ID'],
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
<!-- !::requestConsentInfo:: -->
|
|
56
|
+
|
|
57
|
+
<!-- !::AdmobConsentRequestOptions:: -->
|
|
58
|
+
|
|
59
|
+
<!-- !::AdmobConsentDebugGeography:: -->
|
|
60
|
+
|
|
61
|
+
If you decline consent in the test form (Manage → Confirm Choices), ads may not load. That is expected in a test environment and does not predict production behavior after a user consents.
|
|
62
|
+
|
|
63
|
+
`resetConsentInfo()` is for tests only. See [Consent](https://docs.rdlabo.dev/projects/capacitor-admob/docs/consent).
|
|
64
|
+
|
|
65
|
+
## Server-side verification
|
|
66
|
+
|
|
67
|
+
Server-side verification (SSV) callbacks fire only for production ads. Test ads will not hit your SSV endpoint. For a mock request example, see [Rewarded Ads](https://docs.rdlabo.dev/projects/capacitor-admob/docs/rewarded).
|