From fbb5b9b77e6478c4e9d76dd81b30fc776bd4e4e6 Mon Sep 17 00:00:00 2001
From: lightning1377 <71348134+lightning1377@users.noreply.github.com>
Date: Wed, 27 Nov 2024 10:26:47 +0330
Subject: [PATCH] Add reward interstitial support (#330)
---
README.md | 323 ++++++++++++++----
.../getcapacitor/community/admob/AdMob.java | 17 +
.../community/admob/models/AdOptions.java | 10 +
.../AdRewardInterstitialExecutor.java | 84 +++++
.../RewardInterstitialAdPluginEvents.kt | 12 +
...ardedInterstitialAdCallbackAndListeners.kt | 50 +++
.../rewardedinterstitial/models/SsvInfo.kt | 20 ++
demo/angular/src/app/shared/ad.options.ts | 12 +-
ios/Plugin.xcodeproj/project.pbxproj | 64 +++-
ios/Plugin/Plugin.m | 2 +
ios/Plugin/Plugin.swift | 24 +-
.../AdRewardInterstitialExecutor.swift | 86 +++++
.../RewardInterstitialAdPluginEvents.swift | 8 +
ios/Podfile.lock | 33 ++
src/definitions.ts | 2 +
src/index.ts | 1 +
src/reward-interstitial/index.ts | 4 +
...eward-interstitial-ad-options.interface.ts | 26 ++
...ward-interstitial-ad-plugin-events.enum.ts | 33 ++
...ward-interstitial-definitions.interface.ts | 66 ++++
.../reward-interstitial-item.interface.ts | 15 +
src/web.ts | 14 +
22 files changed, 833 insertions(+), 73 deletions(-)
create mode 100644 android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/AdRewardInterstitialExecutor.java
create mode 100644 android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardInterstitialAdPluginEvents.kt
create mode 100644 android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardedInterstitialAdCallbackAndListeners.kt
create mode 100644 android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/models/SsvInfo.kt
create mode 100644 ios/Plugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift
create mode 100644 ios/Plugin/RewardedInterstitial/RewardInterstitialAdPluginEvents.swift
create mode 100644 ios/Podfile.lock
create mode 100644 src/reward-interstitial/index.ts
create mode 100644 src/reward-interstitial/reward-interstitial-ad-options.interface.ts
create mode 100644 src/reward-interstitial/reward-interstitial-ad-plugin-events.enum.ts
create mode 100644 src/reward-interstitial/reward-interstitial-definitions.interface.ts
create mode 100644 src/reward-interstitial/reward-interstitial-item.interface.ts
diff --git a/README.md b/README.md
index f4cc417f..7757adb8 100644
--- a/README.md
+++ b/README.md
@@ -16,14 +16,15 @@
## Maintainers
-| Maintainer | GitHub | Social | Sponsoring Company |
-| ------------------- | ----------------------------------- | ------------------------------------- | ---------------------------------------------- |
-| Masahiko Sakakibara | [rdlabo](https://github.com/rdlabo) | [@rdlabo](https://twitter.com/rdlabo) | RELATION DESIGN LABO, GENERAL INC. ASSOCIATION |
-| Saninn Salas Diaz | [Saninn Salas Diaz](https://github.com/distante) | [@SaninnSalas](https://twitter.com/SaninnSalas) | |
+| Maintainer | GitHub | Social | Sponsoring Company |
+| ------------------- | ------------------------------------------------ | ----------------------------------------------- | ---------------------------------------------- |
+| Masahiko Sakakibara | [rdlabo](https://github.com/rdlabo) | [@rdlabo](https://twitter.com/rdlabo) | RELATION DESIGN LABO, GENERAL INC. ASSOCIATION |
+| Saninn Salas Diaz | [Saninn Salas Diaz](https://github.com/distante) | [@SaninnSalas](https://twitter.com/SaninnSalas) | |
Maintenance Status: Actively Maintained
## Contributors ✨
+
@@ -42,6 +43,7 @@ Made with [contributors-img](https://contrib.rocks).
| **Android** | ![](demo/screenshots/md_banner.png) | ![](demo/screenshots/md_interstitial.png) | ![](demo/screenshots/md_reward.png) |
## Installation
+
If you use capacitor 5:
```
@@ -119,34 +121,34 @@ import { AdMob } from '@capacitor-community/admob';
export async function initialize(): Promise {
await AdMob.initialize();
-
+
const [trackingInfo, consentInfo] = await Promise.all([
- AdMob.trackingAuthorizationStatus(),
- AdMob.requestConsentInfo(),
+ AdMob.trackingAuthorizationStatus(),
+ AdMob.requestConsentInfo(),
]);
-
+
if (trackingInfo.status === 'notDetermined') {
- /**
- * If you want to explain TrackingAuthorization before showing the iOS dialog,
- * you can show the modal here.
- * ex)
- * const modal = await this.modalCtrl.create({
- * component: RequestTrackingPage,
- * });
- * await modal.present();
- * await modal.onDidDismiss(); // Wait for close modal
- **/
-
- await AdMob.requestTrackingAuthorization();
+ /**
+ * If you want to explain TrackingAuthorization before showing the iOS dialog,
+ * you can show the modal here.
+ * ex)
+ * const modal = await this.modalCtrl.create({
+ * component: RequestTrackingPage,
+ * });
+ * await modal.present();
+ * await modal.onDidDismiss(); // Wait for close modal
+ **/
+
+ await AdMob.requestTrackingAuthorization();
}
const authorizationStatus = await AdMob.trackingAuthorizationStatus();
if (
- authorizationStatus.status === 'authorized' &&
- consentInfo.isConsentFormAvailable &&
- consentInfo.status === AdmobConsentStatus.REQUIRED
+ authorizationStatus.status === 'authorized' &&
+ consentInfo.isConsentFormAvailable &&
+ consentInfo.status === AdmobConsentStatus.REQUIRED
) {
- await AdMob.showConsentForm();
+ await AdMob.showConsentForm();
}
}
```
@@ -180,10 +182,10 @@ async showConsent() {
If you testing on real device, you have to set `debugGeography` and add your device ID to `testDeviceIdentifiers`. You can find your device ID with logcat (Android) or XCode (iOS).
```ts
- const consentInfo = await AdMob.requestConsentInfo({
- debugGeography: AdmobConsentDebugGeography.EEA,
- testDeviceIdentifiers: ['YOUR_DEVICE_ID']
- });
+const consentInfo = await AdMob.requestConsentInfo({
+ debugGeography: AdmobConsentDebugGeography.EEA,
+ testDeviceIdentifiers: ['YOUR_DEVICE_ID'],
+});
```
**Note**: When testing, if you choose not consent (Manage -> Confirm Choices). The ads may not load/show. Even on testing enviroment. This is normal. It will work on Production so don't worry.
@@ -193,38 +195,53 @@ If you testing on real device, you have to set `debugGeography` and add your dev
1. AdMob.initialize
2. AdMob.requestConsentInfo
3. AdMob.showConsentForm (If consent form required )
-3/ AdMob.showBanner
+ 3/ AdMob.showBanner
### Show Banner
```ts
-import { AdMob, BannerAdOptions, BannerAdSize, BannerAdPosition, BannerAdPluginEvents, AdMobBannerSize } from '@capacitor-community/admob';
+import {
+ AdMob,
+ BannerAdOptions,
+ BannerAdSize,
+ BannerAdPosition,
+ BannerAdPluginEvents,
+ AdMobBannerSize,
+} from '@capacitor-community/admob';
export async function banner(): Promise {
- AdMob.addListener(BannerAdPluginEvents.Loaded, () => {
- // Subscribe Banner Event Listener
- });
+ AdMob.addListener(BannerAdPluginEvents.Loaded, () => {
+ // Subscribe Banner Event Listener
+ });
- AdMob.addListener(BannerAdPluginEvents.SizeChanged, (size: AdMobBannerSize) => {
+ AdMob.addListener(
+ BannerAdPluginEvents.SizeChanged,
+ (size: AdMobBannerSize) => {
// Subscribe Change Banner Size
- });
-
- const options: BannerAdOptions = {
- adId: 'YOUR ADID',
- adSize: BannerAdSize.BANNER,
- position: BannerAdPosition.BOTTOM_CENTER,
- margin: 0,
- // isTesting: true
- // npa: true
- };
- AdMob.showBanner(options);
+ },
+ );
+
+ const options: BannerAdOptions = {
+ adId: 'YOUR ADID',
+ adSize: BannerAdSize.BANNER,
+ position: BannerAdPosition.BOTTOM_CENTER,
+ margin: 0,
+ // isTesting: true
+ // npa: true
+ };
+ AdMob.showBanner(options);
}
```
### Show Interstitial
```ts
-import { AdMob, AdOptions, AdLoadInfo, InterstitialAdPluginEvents } from '@capacitor-community/admob';
+import {
+ AdMob,
+ AdOptions,
+ AdLoadInfo,
+ InterstitialAdPluginEvents,
+} from '@capacitor-community/admob';
export async function interstitial(): Promise {
AdMob.addListener(InterstitialAdPluginEvents.Loaded, (info: AdLoadInfo) => {
@@ -244,17 +261,26 @@ export async function interstitial(): Promise {
### Show RewardVideo
```ts
-import { AdMob, RewardAdOptions, AdLoadInfo, RewardAdPluginEvents, AdMobRewardItem } from '@capacitor-community/admob';
+import {
+ AdMob,
+ RewardAdOptions,
+ AdLoadInfo,
+ RewardAdPluginEvents,
+ AdMobRewardItem,
+} from '@capacitor-community/admob';
export async function rewardVideo(): Promise {
AdMob.addListener(RewardAdPluginEvents.Loaded, (info: AdLoadInfo) => {
// Subscribe prepared rewardVideo
});
- AdMob.addListener(RewardAdPluginEvents.Rewarded, (rewardItem: AdMobRewardItem) => {
- // Subscribe user rewarded
- console.log(rewardItem);
- });
+ AdMob.addListener(
+ RewardAdPluginEvents.Rewarded,
+ (rewardItem: AdMobRewardItem) => {
+ // Subscribe user rewarded
+ console.log(rewardItem);
+ },
+ );
const options: RewardAdOptions = {
adId: 'YOUR ADID',
@@ -271,27 +297,31 @@ export async function rewardVideo(): Promise {
```
## Server-side Verification Notice
+
SSV callbacks are only fired on Production Adverts, therefore test Ads will not fire off your SSV callback.
For E2E tests or just for validating the data in your `RewardAdOptions` work as expected, you can add a custom GET
request to your mock endpoint after the `RewardAdPluginEvents.Rewarded` similar to this:
+
```ts
AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => {
// ...
if (ENVIRONMENT_IS_DEVELOPMENT) {
try {
- const url = `https://your-staging-ssv-endpoint` + new URLSearchParams({
- 'ad_network': 'TEST',
- 'ad_unit': 'TEST',
- 'custom_data': customData, // <-- passed CustomData
- 'reward_amount': 'TEST',
- 'reward_item': 'TEST',
- 'timestamp': 'TEST',
- 'transaction_id': 'TEST',
- 'user_id': userId, // <-- Passed UserID
- 'signature': 'TEST',
- 'key_id': 'TEST'
- });
+ const url =
+ `https://your-staging-ssv-endpoint` +
+ new URLSearchParams({
+ ad_network: 'TEST',
+ ad_unit: 'TEST',
+ custom_data: customData, // <-- passed CustomData
+ reward_amount: 'TEST',
+ reward_item: 'TEST',
+ timestamp: 'TEST',
+ transaction_id: 'TEST',
+ user_id: userId, // <-- Passed UserID
+ signature: 'TEST',
+ key_id: 'TEST',
+ });
await fetch(url);
} catch (err) {
console.error(err);
@@ -301,8 +331,8 @@ AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => {
});
```
-
## Index
+
* [`initialize(...)`](#initialize)
@@ -338,6 +368,14 @@ AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => {
* [`addListener(RewardAdPluginEvents.Dismissed, ...)`](#addlistenerrewardadplugineventsdismissed-)
* [`addListener(RewardAdPluginEvents.FailedToShow, ...)`](#addlistenerrewardadplugineventsfailedtoshow-)
* [`addListener(RewardAdPluginEvents.Showed, ...)`](#addlistenerrewardadplugineventsshowed-)
+* [`prepareRewardInterstitialAd(...)`](#preparerewardinterstitialad)
+* [`showRewardInterstitialAd()`](#showrewardinterstitialad)
+* [`addListener(RewardInterstitialAdPluginEvents.FailedToLoad, ...)`](#addlistenerrewardinterstitialadplugineventsfailedtoload-)
+* [`addListener(RewardInterstitialAdPluginEvents.Loaded, ...)`](#addlistenerrewardinterstitialadplugineventsloaded-)
+* [`addListener(RewardInterstitialAdPluginEvents.Rewarded, ...)`](#addlistenerrewardinterstitialadplugineventsrewarded-)
+* [`addListener(RewardInterstitialAdPluginEvents.Dismissed, ...)`](#addlistenerrewardinterstitialadplugineventsdismissed-)
+* [`addListener(RewardInterstitialAdPluginEvents.FailedToShow, ...)`](#addlistenerrewardinterstitialadplugineventsfailedtoshow-)
+* [`addListener(RewardInterstitialAdPluginEvents.Showed, ...)`](#addlistenerrewardinterstitialadplugineventsshowed-)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)
* [Enums](#enums)
@@ -345,6 +383,7 @@ AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => {
## API
+
@@ -847,6 +886,132 @@ addListener(eventName: RewardAdPluginEvents.Showed, listenerFunc: () => void) =>
--------------------
+### prepareRewardInterstitialAd(...)
+
+```typescript
+prepareRewardInterstitialAd(options: RewardInterstitialAdOptions) => Promise
+```
+
+Prepare a reward video ad
+
+| Param | Type | Description |
+| ------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------- |
+| **`options`** | RewardInterstitialAdOptions
| RewardAdOptions |
+
+**Returns:** Promise<AdLoadInfo>
+
+--------------------
+
+
+### showRewardInterstitialAd()
+
+```typescript
+showRewardInterstitialAd() => Promise
+```
+
+Show a reward video ad
+
+**Returns:** Promise<AdMobRewardInterstitialItem>
+
+--------------------
+
+
+### addListener(RewardInterstitialAdPluginEvents.FailedToLoad, ...)
+
+```typescript
+addListener(eventName: RewardInterstitialAdPluginEvents.FailedToLoad, listenerFunc: (error: AdMobError) => void) => Promise
+```
+
+| Param | Type |
+| ------------------ | ---------------------------------------------------------------------------------------------------------- |
+| **`eventName`** | RewardInterstitialAdPluginEvents.FailedToLoad
|
+| **`listenerFunc`** | (error: AdMobError) => void
|
+
+**Returns:** Promise<PluginListenerHandle>
+
+--------------------
+
+
+### addListener(RewardInterstitialAdPluginEvents.Loaded, ...)
+
+```typescript
+addListener(eventName: RewardInterstitialAdPluginEvents.Loaded, listenerFunc: (info: AdLoadInfo) => void) => Promise
+```
+
+| Param | Type |
+| ------------------ | ---------------------------------------------------------------------------------------------------- |
+| **`eventName`** | RewardInterstitialAdPluginEvents.Loaded
|
+| **`listenerFunc`** | (info: AdLoadInfo) => void
|
+
+**Returns:** Promise<PluginListenerHandle>
+
+--------------------
+
+
+### addListener(RewardInterstitialAdPluginEvents.Rewarded, ...)
+
+```typescript
+addListener(eventName: RewardInterstitialAdPluginEvents.Rewarded, listenerFunc: (reward: AdMobRewardInterstitialItem) => void) => Promise
+```
+
+| Param | Type |
+| ------------------ | -------------------------------------------------------------------------------------------------------- |
+| **`eventName`** | RewardInterstitialAdPluginEvents.Rewarded
|
+| **`listenerFunc`** | (reward: AdMobRewardInterstitialItem) => void
|
+
+**Returns:** Promise<PluginListenerHandle>
+
+--------------------
+
+
+### addListener(RewardInterstitialAdPluginEvents.Dismissed, ...)
+
+```typescript
+addListener(eventName: RewardInterstitialAdPluginEvents.Dismissed, listenerFunc: () => void) => Promise
+```
+
+| Param | Type |
+| ------------------ | ------------------------------------------------------------------------------------------------------- |
+| **`eventName`** | RewardInterstitialAdPluginEvents.Dismissed
|
+| **`listenerFunc`** | () => void
|
+
+**Returns:** Promise<PluginListenerHandle>
+
+--------------------
+
+
+### addListener(RewardInterstitialAdPluginEvents.FailedToShow, ...)
+
+```typescript
+addListener(eventName: RewardInterstitialAdPluginEvents.FailedToShow, listenerFunc: (error: AdMobError) => void) => Promise
+```
+
+| Param | Type |
+| ------------------ | ---------------------------------------------------------------------------------------------------------- |
+| **`eventName`** | RewardInterstitialAdPluginEvents.FailedToShow
|
+| **`listenerFunc`** | (error: AdMobError) => void
|
+
+**Returns:** Promise<PluginListenerHandle>
+
+--------------------
+
+
+### addListener(RewardInterstitialAdPluginEvents.Showed, ...)
+
+```typescript
+addListener(eventName: RewardInterstitialAdPluginEvents.Showed, listenerFunc: () => void) => Promise
+```
+
+| Param | Type |
+| ------------------ | ---------------------------------------------------------------------------------------------------- |
+| **`eventName`** | RewardInterstitialAdPluginEvents.Showed
|
+| **`listenerFunc`** | () => void
|
+
+**Returns:** Promise<PluginListenerHandle>
+
+--------------------
+
+
### Interfaces
@@ -972,6 +1137,24 @@ https://developers.google.com/admob/android/rewarded-video-adapters?hl=en
| **`amount`** | number
| Rewarded amount user got |
+#### RewardInterstitialAdOptions
+
+| Prop | Type | Description |
+| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **`ssv`** | AtLeastOne<{ /** * An optional UserId to pass to your SSV callback function. */ userId: string; /** * An optional custom set of data to pass to your SSV callback function. */ customData: string; }>
| If you have enabled SSV in your AdMob Application. You can provide customData or a userId be passed to your callback to do further processing on. *Important* You *HAVE* to define one of them. |
+
+
+#### AdMobRewardInterstitialItem
+
+For more information
+https://developers.google.com/admob/android/rewarded-video-adapters?hl=en
+
+| Prop | Type | Description |
+| ------------ | ------------------- | ------------------------ |
+| **`type`** | string
| Rewarded type user got |
+| **`amount`** | number
| Rewarded amount user got |
+
+
### Type Aliases
@@ -1077,6 +1260,18 @@ From T, pick a set of properties whose keys are in the union K
| **`Dismissed`** | 'onRewardedVideoAdDismissed'
| Emits when the AdReward video is not visible to the user anymore. **Important**: This has nothing to do with the reward it self. This event will emits in this two cases: 1. The user starts the video ad but close it before the reward emit. 2. The user start the video and see it until end, then gets the reward and after that the ad is closed. |
| **`Rewarded`** | 'onRewardedVideoAdReward'
| Emits when user get rewarded from AdReward |
+
+#### RewardInterstitialAdPluginEvents
+
+| Members | Value | Description |
+| ------------------ | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **`Loaded`** | 'onRewardedInterstitialAdLoaded'
| Emits after trying to prepare a RewardAd and the Video is loaded and ready to be show |
+| **`FailedToLoad`** | 'onRewardedInterstitialAdFailedToLoad'
| Emits after trying to prepare a RewardAd when it could not be loaded |
+| **`Showed`** | 'onRewardedInterstitialAdShowed'
| Emits when the AdReward video is visible to the user |
+| **`FailedToShow`** | 'onRewardedInterstitialAdFailedToShow'
| Emits when the AdReward video is failed to show |
+| **`Dismissed`** | 'onRewardedInterstitialAdDismissed'
| Emits when the AdReward video is not visible to the user anymore. **Important**: This has nothing to do with the reward it self. This event will emits in this two cases: 1. The user starts the video ad but close it before the reward emit. 2. The user start the video and see it until end, then gets the reward and after that the ad is closed. |
+| **`Rewarded`** | 'onRewardedInterstitialAdReward'
| Emits when user get rewarded from AdReward |
+
## TROUBLE SHOOTING
diff --git a/android/src/main/java/com/getcapacitor/community/admob/AdMob.java b/android/src/main/java/com/getcapacitor/community/admob/AdMob.java
index a63193b9..61b99492 100644
--- a/android/src/main/java/com/getcapacitor/community/admob/AdMob.java
+++ b/android/src/main/java/com/getcapacitor/community/admob/AdMob.java
@@ -14,6 +14,7 @@
import com.getcapacitor.community.admob.interstitial.AdInterstitialExecutor;
import com.getcapacitor.community.admob.interstitial.InterstitialAdCallbackAndListeners;
import com.getcapacitor.community.admob.rewarded.AdRewardExecutor;
+import com.getcapacitor.community.admob.rewardedinterstitial.AdRewardInterstitialExecutor;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.RequestConfiguration;
import com.google.android.gms.ads.initialization.InitializationStatus;
@@ -39,6 +40,12 @@ public class AdMob extends Plugin {
this::notifyListeners,
getLogTag()
);
+ private final AdRewardInterstitialExecutor adRewardInterstitialExecutor = new AdRewardInterstitialExecutor(
+ this::getContext,
+ this::getActivity,
+ this::notifyListeners,
+ getLogTag()
+ );
private final AdInterstitialExecutor adInterstitialExecutor = new AdInterstitialExecutor(
this::getContext,
this::getActivity,
@@ -169,6 +176,16 @@ public void showRewardVideoAd(final PluginCall call) {
adRewardExecutor.showRewardVideoAd(call, this::notifyListeners);
}
+ @PluginMethod
+ public void prepareRewardInterstitialAd(final PluginCall call) {
+ adRewardInterstitialExecutor.prepareRewardInterstitialAd(call, this::notifyListeners);
+ }
+
+ @PluginMethod
+ public void showRewardInterstitialAd(final PluginCall call) {
+ adRewardInterstitialExecutor.showRewardInterstitialAd(call, this::notifyListeners);
+ }
+
/**
* @see Test Devices
* @see Target Settings
diff --git a/android/src/main/java/com/getcapacitor/community/admob/models/AdOptions.java b/android/src/main/java/com/getcapacitor/community/admob/models/AdOptions.java
index d81f36ee..9fdaf8b4 100644
--- a/android/src/main/java/com/getcapacitor/community/admob/models/AdOptions.java
+++ b/android/src/main/java/com/getcapacitor/community/admob/models/AdOptions.java
@@ -27,6 +27,7 @@ public abstract class AdOptions {
public static final String BANNER_TESTER_ID = "ca-app-pub-3940256099942544/6300978111";
public static final String INTERSTITIAL_TESTER_ID = "ca-app-pub-3940256099942544/1033173712";
public static final String REWARD_VIDEO_TESTER_ID = "ca-app-pub-3940256099942544/5224354917";
+ public static final String REWARD_INTERSTITIAL_TESTER_ID = "ca-app-pub-3940256099942544/5354046379";
/**
* The position of the ad, it can be TOP_CENTER,
@@ -138,6 +139,15 @@ public String getTestingId() {
};
}
+ public AdOptions createRewardInterstitialOptions(PluginCall call) {
+ return new AdOptions(call) {
+ @Override
+ public String getTestingId() {
+ return AdOptions.REWARD_INTERSTITIAL_TESTER_ID;
+ }
+ };
+ }
+
public AdOptions createGenericOptions(PluginCall call, final String testingID) {
return new AdOptions(call) {
@Override
diff --git a/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/AdRewardInterstitialExecutor.java b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/AdRewardInterstitialExecutor.java
new file mode 100644
index 00000000..a315d61b
--- /dev/null
+++ b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/AdRewardInterstitialExecutor.java
@@ -0,0 +1,84 @@
+package com.getcapacitor.community.admob.rewardedinterstitial;
+
+import android.app.Activity;
+import android.content.Context;
+import androidx.core.util.Supplier;
+import com.getcapacitor.JSObject;
+import com.getcapacitor.PluginCall;
+import com.getcapacitor.PluginMethod;
+import com.getcapacitor.community.admob.helpers.AdViewIdHelper;
+import com.getcapacitor.community.admob.helpers.RequestHelper;
+import com.getcapacitor.community.admob.models.AdMobPluginError;
+import com.getcapacitor.community.admob.models.AdOptions;
+import com.getcapacitor.community.admob.models.Executor;
+import com.google.android.gms.ads.AdRequest;
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd;
+import com.google.android.gms.common.util.BiConsumer;
+
+public class AdRewardInterstitialExecutor extends Executor {
+
+ public static RewardedInterstitialAd mRewardedInterstitialAd;
+
+ public AdRewardInterstitialExecutor(
+ Supplier contextSupplier,
+ Supplier activitySupplier,
+ BiConsumer notifyListenersFunction,
+ String pluginLogTag
+ ) {
+ super(contextSupplier, activitySupplier, notifyListenersFunction, pluginLogTag, "AdRewardExecutor");
+ }
+
+ @PluginMethod
+ public void prepareRewardInterstitialAd(final PluginCall call, BiConsumer notifyListenersFunction) {
+ final AdOptions adOptions = AdOptions.getFactory().createRewardInterstitialOptions(call);
+
+ activitySupplier
+ .get()
+ .runOnUiThread(
+ () -> {
+ try {
+ final AdRequest adRequest = RequestHelper.createRequest(adOptions);
+ final String id = AdViewIdHelper.getFinalAdId(adOptions, adRequest, logTag, contextSupplier.get());
+ RewardedInterstitialAd.load(
+ contextSupplier.get(),
+ id,
+ adRequest,
+ RewardedInterstitialAdCallbackAndListeners.INSTANCE.getRewardedAdLoadCallback(
+ call,
+ notifyListenersFunction,
+ adOptions
+ )
+ );
+ } catch (Exception ex) {
+ call.reject(ex.getLocalizedMessage(), ex);
+ }
+ }
+ );
+ }
+
+ @PluginMethod
+ public void showRewardInterstitialAd(final PluginCall call, BiConsumer notifyListenersFunction) {
+ if (mRewardedInterstitialAd == null) {
+ String errorMessage = "No Reward Video Ad can be show. It was not prepared or maybe it failed to be prepared.";
+ call.reject(errorMessage);
+ AdMobPluginError errorObject = new AdMobPluginError(-1, errorMessage);
+ notifyListenersFunction.accept(RewardInterstitialAdPluginEvents.FailedToLoad, errorObject);
+ return;
+ }
+
+ try {
+ activitySupplier
+ .get()
+ .runOnUiThread(
+ () -> {
+ mRewardedInterstitialAd.show(
+ activitySupplier.get(),
+ RewardedInterstitialAdCallbackAndListeners.INSTANCE.getOnUserEarnedRewardListener(call, notifyListenersFunction)
+ );
+ }
+ );
+ } catch (Exception ex) {
+ call.reject(ex.getLocalizedMessage(), ex);
+ }
+ }
+}
diff --git a/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardInterstitialAdPluginEvents.kt b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardInterstitialAdPluginEvents.kt
new file mode 100644
index 00000000..ce1c2ee8
--- /dev/null
+++ b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardInterstitialAdPluginEvents.kt
@@ -0,0 +1,12 @@
+package com.getcapacitor.community.admob.rewardedinterstitial
+
+import com.getcapacitor.community.admob.models.LoadPluginEventNames
+
+object RewardInterstitialAdPluginEvents: LoadPluginEventNames {
+ const val Loaded = "onRewardedInterstitialAdLoaded"
+ const val FailedToLoad = "onRewardedInterstitialAdFailedToLoad"
+ const val Rewarded = "onRewardedInterstitialAdReward"
+ override val Showed = "onRewardedInterstitialAdShowed"
+ override val FailedToShow = "onRewardedInterstitialAdFailedToShow"
+ override val Dismissed = "onRewardedInterstitialAdDismissed"
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardedInterstitialAdCallbackAndListeners.kt b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardedInterstitialAdCallbackAndListeners.kt
new file mode 100644
index 00000000..06488327
--- /dev/null
+++ b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/RewardedInterstitialAdCallbackAndListeners.kt
@@ -0,0 +1,50 @@
+package com.getcapacitor.community.admob.rewardedinterstitial
+
+import com.getcapacitor.JSObject
+import com.getcapacitor.PluginCall
+import com.getcapacitor.community.admob.helpers.FullscreenPluginCallback
+import com.getcapacitor.community.admob.models.AdMobPluginError
+import com.getcapacitor.community.admob.models.AdOptions
+import com.google.android.gms.ads.LoadAdError
+import com.google.android.gms.ads.OnUserEarnedRewardListener
+import com.google.android.gms.ads.rewarded.RewardItem
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback
+import com.google.android.gms.common.util.BiConsumer
+
+object RewardedInterstitialAdCallbackAndListeners {
+
+ fun getOnUserEarnedRewardListener(call: PluginCall, notifyListenersFunction: BiConsumer): OnUserEarnedRewardListener {
+ return OnUserEarnedRewardListener { item: RewardItem ->
+ val response = JSObject()
+ response.put("type", item.type)
+ .put("amount", item.amount)
+ notifyListenersFunction.accept(RewardInterstitialAdPluginEvents.Rewarded, response)
+ call.resolve(response)
+ }
+ }
+
+ fun getRewardedAdLoadCallback(call: PluginCall, notifyListenersFunction: BiConsumer, adOptions: AdOptions): RewardedInterstitialAdLoadCallback {
+ return object : RewardedInterstitialAdLoadCallback() {
+ override fun onAdLoaded(ad: RewardedInterstitialAd) {
+ AdRewardInterstitialExecutor.mRewardedInterstitialAd = ad
+ AdRewardInterstitialExecutor.mRewardedInterstitialAd.fullScreenContentCallback = FullscreenPluginCallback(
+ RewardInterstitialAdPluginEvents, notifyListenersFunction)
+
+ val adInfo = JSObject()
+ adInfo.put("adUnitId", ad.adUnitId)
+ call.resolve(adInfo)
+
+ notifyListenersFunction.accept(RewardInterstitialAdPluginEvents.Loaded, adInfo)
+ }
+
+ override fun onAdFailedToLoad(adError: LoadAdError) {
+ val adMobError = AdMobPluginError(adError)
+
+ notifyListenersFunction.accept(RewardInterstitialAdPluginEvents.FailedToLoad, adMobError)
+ call.reject(adError.message)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/models/SsvInfo.kt b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/models/SsvInfo.kt
new file mode 100644
index 00000000..34532edc
--- /dev/null
+++ b/android/src/main/java/com/getcapacitor/community/admob/rewardedinterstitial/models/SsvInfo.kt
@@ -0,0 +1,20 @@
+package com.getcapacitor.community.admob.rewardedinterstitial.models
+
+import com.getcapacitor.PluginCall
+
+class SsvInfo(
+ val customData: String? = null,
+ val userId: String? = null) {
+
+ constructor(pluginCall: PluginCall?) : this(
+ pluginCall?.getObject("ssv")?.getString("customData"),
+ pluginCall?.getObject("ssv")?.getString("userId")
+ )
+
+ constructor() : this(null, null)
+
+ val hasInfo
+ get(): Boolean {
+ return customData != null || userId != null
+ }
+}
\ No newline at end of file
diff --git a/demo/angular/src/app/shared/ad.options.ts b/demo/angular/src/app/shared/ad.options.ts
index b563bf6c..a25a79d0 100644
--- a/demo/angular/src/app/shared/ad.options.ts
+++ b/demo/angular/src/app/shared/ad.options.ts
@@ -1,5 +1,9 @@
-import {BannerAdOptions, BannerAdPosition, BannerAdSize} from '../../../../../dist/esm/banner';
-import {AdOptions} from '../../../../../dist/esm/shared';
+import {
+ BannerAdOptions,
+ BannerAdPosition,
+ BannerAdSize,
+} from '../../../../../dist/esm/banner';
+import { AdOptions } from '../../../../../dist/esm/shared';
export const bannerTopOptions: BannerAdOptions = {
adId: 'ca-app-pub-3940256099942544/2934735716',
@@ -19,6 +23,10 @@ export const rewardOptions: AdOptions = {
adId: 'ca-app-pub-3940256099942544/5224354917',
};
+export const rewardInterstitialOptions: AdOptions = {
+ adId: 'ca-app-pub-3940256099942544/5354046379',
+};
+
export const interstitialOptions: AdOptions = {
adId: 'ca-app-pub-3940256099942544/1033173712',
};
diff --git a/ios/Plugin.xcodeproj/project.pbxproj b/ios/Plugin.xcodeproj/project.pbxproj
index c0c59952..f2f4514a 100644
--- a/ios/Plugin.xcodeproj/project.pbxproj
+++ b/ios/Plugin.xcodeproj/project.pbxproj
@@ -25,6 +25,8 @@
DB061C5526394B5800538D26 /* AdInterstitialExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB061C5326394B5800538D26 /* AdInterstitialExecutor.swift */; };
DB061C5926394B7900538D26 /* FullScreenAdEventName.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB061C5826394B7900538D26 /* FullScreenAdEventName.swift */; };
DBBC90F22697F1F400F2A663 /* AuthorizationStatusEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBC90F12697F1F400F2A663 /* AuthorizationStatusEnum.swift */; };
+ DC44B8892C0D9749001D8B37 /* AdRewardInterstitialExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC44B8882C0D9749001D8B37 /* AdRewardInterstitialExecutor.swift */; };
+ DC44B88B2C0D9764001D8B37 /* RewardInterstitialAdPluginEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC44B88A2C0D9764001D8B37 /* RewardInterstitialAdPluginEvents.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -61,6 +63,8 @@
DB061C5326394B5800538D26 /* AdInterstitialExecutor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdInterstitialExecutor.swift; sourceTree = ""; };
DB061C5826394B7900538D26 /* FullScreenAdEventName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullScreenAdEventName.swift; sourceTree = ""; };
DBBC90F12697F1F400F2A663 /* AuthorizationStatusEnum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthorizationStatusEnum.swift; path = Plugin/Helper/AuthorizationStatusEnum.swift; sourceTree = SOURCE_ROOT; };
+ DC44B8882C0D9749001D8B37 /* AdRewardInterstitialExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdRewardInterstitialExecutor.swift; sourceTree = ""; };
+ DC44B88A2C0D9764001D8B37 /* RewardInterstitialAdPluginEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardInterstitialAdPluginEvents.swift; sourceTree = ""; };
F44715726BB7665615BDA523 /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; };
FB011FA1245A5805FBC629D9 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -121,6 +125,7 @@
50ADFF8A201F53D600D50D53 /* Plugin */ = {
isa = PBXGroup;
children = (
+ DC44B8872C0D971A001D8B37 /* RewardedInterstitial */,
BD7969362A4EA77900B13412 /* Consent */,
DBBC90F32697F26000F2A663 /* Helper */,
DB061C4926394B0400538D26 /* Rewarded */,
@@ -206,6 +211,15 @@
path = Helper;
sourceTree = "";
};
+ DC44B8872C0D971A001D8B37 /* RewardedInterstitial */ = {
+ isa = PBXGroup;
+ children = (
+ DC44B8882C0D9749001D8B37 /* AdRewardInterstitialExecutor.swift */,
+ DC44B88A2C0D9764001D8B37 /* RewardInterstitialAdPluginEvents.swift */,
+ );
+ path = RewardedInterstitial;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -229,6 +243,7 @@
50ADFF84201F53D600D50D53 /* Frameworks */,
50ADFF85201F53D600D50D53 /* Headers */,
50ADFF86201F53D600D50D53 /* Resources */,
+ 2629298625CE1F6680C13FD9 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -248,6 +263,7 @@
50ADFF8E201F53D600D50D53 /* Frameworks */,
50ADFF8F201F53D600D50D53 /* Resources */,
A6BD04EB568905DCA6108727 /* [CP] Embed Pods Frameworks */,
+ 3A1D71941C67BCF50F106667 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -317,6 +333,46 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 2629298625CE1F6680C13FD9 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh",
+ "${PODS_CONFIGURATION_BUILD_DIR}/Google-Mobile-Ads-SDK/GoogleMobileAdsResources.bundle",
+ "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUserMessagingPlatform/UserMessagingPlatformResources.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMobileAdsResources.bundle",
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UserMessagingPlatformResources.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3A1D71941C67BCF50F106667 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh",
+ "${PODS_CONFIGURATION_BUILD_DIR}/Google-Mobile-Ads-SDK/GoogleMobileAdsResources.bundle",
+ "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUserMessagingPlatform/UserMessagingPlatformResources.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMobileAdsResources.bundle",
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UserMessagingPlatformResources.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
A6BD04EB568905DCA6108727 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -326,17 +382,11 @@
"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework",
"${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework",
- "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
- "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
- "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -396,7 +446,9 @@
files = (
DB061C5426394B5800538D26 /* InterstitialAdPluginEvents.swift in Sources */,
BD7969382A4EA78C00B13412 /* ConsentExecutor.swift in Sources */,
+ DC44B8892C0D9749001D8B37 /* AdRewardInterstitialExecutor.swift in Sources */,
DB061C5526394B5800538D26 /* AdInterstitialExecutor.swift in Sources */,
+ DC44B88B2C0D9764001D8B37 /* RewardInterstitialAdPluginEvents.swift in Sources */,
DB061C5126394B4100538D26 /* AdRewardExecutor.swift in Sources */,
DBBC90F22697F1F400F2A663 /* AuthorizationStatusEnum.swift in Sources */,
DB061C44263949D800538D26 /* BannerAdPluginEvents.swift in Sources */,
diff --git a/ios/Plugin/Plugin.m b/ios/Plugin/Plugin.m
index d13a3008..e4228b06 100644
--- a/ios/Plugin/Plugin.m
+++ b/ios/Plugin/Plugin.m
@@ -20,4 +20,6 @@
CAP_PLUGIN_METHOD(showInterstitial, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(prepareRewardVideoAd, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(showRewardVideoAd, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(prepareRewardInterstitialAd, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(showRewardInterstitialAd, CAPPluginReturnPromise);
)
diff --git a/ios/Plugin/Plugin.swift b/ios/Plugin/Plugin.swift
index d9a4c853..3944159f 100644
--- a/ios/Plugin/Plugin.swift
+++ b/ios/Plugin/Plugin.swift
@@ -13,6 +13,7 @@ public class AdMob: CAPPlugin {
private let bannerExecutor = BannerExecutor()
private let adInterstitialExecutor = AdInterstitialExecutor()
private let adRewardExecutor = AdRewardExecutor()
+ private let adRewardInterstitialExecutor = AdRewardInterstitialExecutor()
private let consentExecutor = ConsentExecutor()
/**
@@ -23,6 +24,8 @@ public class AdMob: CAPPlugin {
self.bannerExecutor.plugin = self
self.adInterstitialExecutor.plugin = self
self.adRewardExecutor.plugin = self
+ self.adRewardInterstitialExecutor.plugin = self
+ self.adInterstitialExecutor.plugin = self
self.consentExecutor.plugin = self
self.setRequestConfiguration(call)
@@ -123,7 +126,7 @@ public class AdMob: CAPPlugin {
/**
* AdMob: Rewarded Ads
- * https://developers.google.com/ad-manager/mobile-ads-sdk/ios/rewarded-ads?hl=ja
+ * https://developers.google.com/ad-manager/mobile-ads-sdk/ios/rewarded-ads
*/
@objc func prepareRewardVideoAd(_ call: CAPPluginCall) {
let adUnitID = getAdId(call, "ca-app-pub-3940256099942544/1712485313")
@@ -139,6 +142,25 @@ public class AdMob: CAPPlugin {
self.adRewardExecutor.showRewardVideoAd(call)
}
}
+
+ /**
+ * AdMob: Rewarded Interstitial Ads
+ * https://developers.google.com/ad-manager/mobile-ads-sdk/ios/rewarded-interstitial
+ */
+ @objc func prepareRewardInterstitialAd(_ call: CAPPluginCall) {
+ let adUnitID = getAdId(call, "ca-app-pub-3940256099942544/6978759866")
+ let request = self.GADRequestWithOption(call.getBool("npa") ?? false)
+
+ DispatchQueue.main.async {
+ self.adRewardInterstitialExecutor.prepareRewardInterstitialAd(call, request, adUnitID)
+ }
+ }
+
+ @objc func showRewardInterstitialAd(_ call: CAPPluginCall) {
+ DispatchQueue.main.async {
+ self.adRewardInterstitialExecutor.showRewardInterstitialAd(call)
+ }
+ }
@objc func trackingAuthorizationStatus(_ call: CAPPluginCall) {
DispatchQueue.main.async {
diff --git a/ios/Plugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift b/ios/Plugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift
new file mode 100644
index 00000000..afbb5768
--- /dev/null
+++ b/ios/Plugin/RewardedInterstitial/AdRewardInterstitialExecutor.swift
@@ -0,0 +1,86 @@
+import Foundation
+import Capacitor
+import GoogleMobileAds
+
+class AdRewardInterstitialExecutor: NSObject, GADFullScreenContentDelegate {
+ public weak var plugin: AdMob?
+ var rewardedInterstitialAd: GADRewardedInterstitialAd!
+
+ func prepareRewardInterstitialAd(_ call: CAPPluginCall, _ request: GADRequest, _ adUnitID: String) {
+ GADRewardedInterstitialAd.load(
+ withAdUnitID: adUnitID,
+ request: request,
+ completionHandler: { (ad, error) in
+ if let error = error {
+ NSLog("Rewarded ad failed to load with error: \(error.localizedDescription)")
+ self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.FailedToLoad.rawValue, data: [
+ "code": 0,
+ "message": error.localizedDescription
+ ])
+ call.reject("Loading failed")
+ return
+ }
+
+ self.rewardedInterstitialAd = ad
+
+ if let providedOptions = call.getObject("ssv") {
+ let ssvOptions = GADServerSideVerificationOptions()
+
+ if let customData = providedOptions["customData"] as? String {
+ NSLog("Sending Custom Data: \(customData) to SSV callback")
+ ssvOptions.customRewardString = customData
+ }
+
+ if let userId = providedOptions["userId"] as? String {
+ NSLog("Sending UserId: \(userId) to SSV callback")
+ ssvOptions.userIdentifier = userId
+ }
+
+ self.rewardedInterstitialAd?.serverSideVerificationOptions = ssvOptions
+ }
+
+ self.rewardedInterstitialAd?.fullScreenContentDelegate = self
+ self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.Loaded.rawValue, data: [
+ "adUnitId": adUnitID
+ ])
+ call.resolve([
+ "adUnitId": adUnitID
+ ])
+ }
+ )
+ }
+
+ func showRewardInterstitialAd(_ call: CAPPluginCall) {
+ if let rootViewController = plugin?.getRootVC() {
+ if let ad = self.rewardedInterstitialAd {
+ ad.present(fromRootViewController: rootViewController,
+ userDidEarnRewardHandler: {
+ let reward = ad.adReward
+ self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.Rewarded.rawValue, data: ["type": reward.type, "amount": reward.amount])
+ call.resolve(["type": reward.type, "amount": reward.amount])
+ }
+ )
+ } else {
+ call.reject("Reward Video is Not Ready Yet")
+ }
+ }
+ }
+
+ public func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
+ NSLog("RewardFullScreenDelegate Ad failed to present full screen content with error \(error.localizedDescription).")
+ self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.FailedToShow.rawValue, data: [
+ "code": 0,
+ "message": error.localizedDescription
+ ])
+ }
+
+ public func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
+ NSLog("RewardFullScreenDelegate Ad did present full screen content.")
+ self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.Showed.rawValue, data: [:])
+ }
+
+ public func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
+ NSLog("RewardFullScreenDelegate Ad did dismiss full screen content.")
+ self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.Dismissed.rawValue, data: [:])
+ }
+}
diff --git a/ios/Plugin/RewardedInterstitial/RewardInterstitialAdPluginEvents.swift b/ios/Plugin/RewardedInterstitial/RewardInterstitialAdPluginEvents.swift
new file mode 100644
index 00000000..8595de09
--- /dev/null
+++ b/ios/Plugin/RewardedInterstitial/RewardInterstitialAdPluginEvents.swift
@@ -0,0 +1,8 @@
+public enum RewardInterstitialAdPluginEvents: String {
+ case Loaded = "onRewardedInterstitialAdLoaded"
+ case FailedToLoad = "onRewardedInterstitialAdFailedToLoad"
+ case Showed = "onRewardedInterstitialAdShowed"
+ case FailedToShow = "onRewardedInterstitialAdFailedToShow"
+ case Dismissed = "onRewardedInterstitialAdDismissed"
+ case Rewarded = "onRewardedInterstitialAdReward"
+}
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
new file mode 100644
index 00000000..696b2829
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,33 @@
+PODS:
+ - Capacitor (5.0.0-rc.2):
+ - CapacitorCordova
+ - CapacitorCordova (5.0.0-rc.2)
+ - Google-Mobile-Ads-SDK (11.3.0):
+ - GoogleUserMessagingPlatform (>= 1.1)
+ - GoogleUserMessagingPlatform (2.4.0)
+
+DEPENDENCIES:
+ - "Capacitor (from `../node_modules/@capacitor/ios`)"
+ - "CapacitorCordova (from `../node_modules/@capacitor/ios`)"
+ - Google-Mobile-Ads-SDK (= 11.3.0)
+
+SPEC REPOS:
+ trunk:
+ - Google-Mobile-Ads-SDK
+ - GoogleUserMessagingPlatform
+
+EXTERNAL SOURCES:
+ Capacitor:
+ :path: "../node_modules/@capacitor/ios"
+ CapacitorCordova:
+ :path: "../node_modules/@capacitor/ios"
+
+SPEC CHECKSUMS:
+ Capacitor: 66adc61750eaca730fbb8d296d5e1027c1aef664
+ CapacitorCordova: 74c97b20c65078c395f4289f7acaaf59587124a2
+ Google-Mobile-Ads-SDK: 301a16c461c331ba34ff4dab40a22ab7c147af61
+ GoogleUserMessagingPlatform: f131fa7978d2ba88d7426702b057c2cc318e6595
+
+PODFILE CHECKSUM: fbc53a62ab9a762bed62d93a515099d2d070c536
+
+COCOAPODS: 1.15.2
diff --git a/src/definitions.ts b/src/definitions.ts
index d0dd8093..19e2a09c 100644
--- a/src/definitions.ts
+++ b/src/definitions.ts
@@ -2,10 +2,12 @@ import type { BannerDefinitions } from './banner';
import type { AdmobConsentDefinitions } from './consent';
import type { InterstitialDefinitions } from './interstitial';
import type { RewardDefinitions } from './reward';
+import type { RewardInterstitialDefinitions } from './reward-interstitial';
import type { TrackingAuthorizationStatusInterface } from './shared/tracking-authorization-status.interface';
type AdMobDefinitions = BannerDefinitions &
RewardDefinitions &
+ RewardInterstitialDefinitions &
InterstitialDefinitions &
AdmobConsentDefinitions;
diff --git a/src/index.ts b/src/index.ts
index e2fcdc5e..0f24d25d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,6 +9,7 @@ const AdMob = registerPlugin('AdMob', {
export * from './definitions';
export * from './banner/index';
export * from './interstitial/index';
+export * from './reward-interstitial/index';
export * from './reward/index';
export * from './consent/index';
export * from './shared/index';
diff --git a/src/reward-interstitial/index.ts b/src/reward-interstitial/index.ts
new file mode 100644
index 00000000..c42209a6
--- /dev/null
+++ b/src/reward-interstitial/index.ts
@@ -0,0 +1,4 @@
+export * from './reward-interstitial-ad-plugin-events.enum';
+export * from './reward-interstitial-definitions.interface';
+export * from './reward-interstitial-item.interface';
+export * from './reward-interstitial-ad-options.interface';
diff --git a/src/reward-interstitial/reward-interstitial-ad-options.interface.ts b/src/reward-interstitial/reward-interstitial-ad-options.interface.ts
new file mode 100644
index 00000000..20cdb20b
--- /dev/null
+++ b/src/reward-interstitial/reward-interstitial-ad-options.interface.ts
@@ -0,0 +1,26 @@
+import type { AdOptions } from '../shared/ad-options.interface';
+
+// This is a type to ensure that IF ssv is provided, at least one of userId or customData is required.
+type AtLeastOne = { [K in keyof T]: Pick }[keyof T];
+
+// Because only RewardedAds use SSV. This interface is only available for RewardedAds
+export interface RewardInterstitialAdOptions extends AdOptions {
+ /**
+ * If you have enabled SSV in your AdMob Application. You can provide customData or
+ * a userId be passed to your callback to do further processing on.
+ *
+ * *Important* You *HAVE* to define one of them.
+ *
+ * @see https://support.google.com/admob/answer/9603226?hl=en-GB
+ */
+ ssv?: AtLeastOne<{
+ /**
+ * An optional UserId to pass to your SSV callback function.
+ */
+ userId: string;
+ /**
+ * An optional custom set of data to pass to your SSV callback function.
+ */
+ customData: string;
+ }>;
+}
diff --git a/src/reward-interstitial/reward-interstitial-ad-plugin-events.enum.ts b/src/reward-interstitial/reward-interstitial-ad-plugin-events.enum.ts
new file mode 100644
index 00000000..0d6ffce2
--- /dev/null
+++ b/src/reward-interstitial/reward-interstitial-ad-plugin-events.enum.ts
@@ -0,0 +1,33 @@
+// This enum should be keep in sync with their native equivalents with the same name
+export enum RewardInterstitialAdPluginEvents {
+ /**
+ * Emits after trying to prepare a RewardAd and the Video is loaded and ready to be show
+ */
+ Loaded = 'onRewardedInterstitialAdLoaded',
+ /**
+ * Emits after trying to prepare a RewardAd when it could not be loaded
+ */
+ FailedToLoad = 'onRewardedInterstitialAdFailedToLoad',
+ /**
+ * Emits when the AdReward video is visible to the user
+ */
+ Showed = 'onRewardedInterstitialAdShowed',
+ /**
+ * Emits when the AdReward video is failed to show
+ */
+ FailedToShow = 'onRewardedInterstitialAdFailedToShow',
+ /**
+ * Emits when the AdReward video is not visible to the user anymore.
+ *
+ * **Important**: This has nothing to do with the reward it self. This event
+ * will emits in this two cases:
+ * 1. The user starts the video ad but close it before the reward emit.
+ * 2. The user start the video and see it until end, then gets the reward
+ * and after that the ad is closed.
+ */
+ Dismissed = 'onRewardedInterstitialAdDismissed',
+ /**
+ * Emits when user get rewarded from AdReward
+ */
+ Rewarded = 'onRewardedInterstitialAdReward',
+}
diff --git a/src/reward-interstitial/reward-interstitial-definitions.interface.ts b/src/reward-interstitial/reward-interstitial-definitions.interface.ts
new file mode 100644
index 00000000..50ffdd22
--- /dev/null
+++ b/src/reward-interstitial/reward-interstitial-definitions.interface.ts
@@ -0,0 +1,66 @@
+import type { PluginListenerHandle } from '@capacitor/core';
+
+import type { ValidateAllEventsEnumAreImplemented } from '../private/validate-all-events-implemented.type';
+import type { AdLoadInfo, AdMobError } from '../shared';
+
+import type { RewardInterstitialAdOptions } from './reward-interstitial-ad-options.interface';
+import type { RewardInterstitialAdPluginEvents } from './reward-interstitial-ad-plugin-events.enum';
+import type { AdMobRewardInterstitialItem } from './reward-interstitial-item.interface';
+
+// This is just to validate that we do not forget to implement any event name
+export type RewardInterstitialDefinitionsHasAllEvents =
+ ValidateAllEventsEnumAreImplemented<
+ RewardInterstitialAdPluginEvents,
+ RewardInterstitialDefinitions
+ >;
+
+export interface RewardInterstitialDefinitions {
+ /**
+ * Prepare a reward video ad
+ *
+ * @group RewardVideo
+ * @param options RewardAdOptions
+ * @since 1.1.2
+ */
+ prepareRewardInterstitialAd(
+ options: RewardInterstitialAdOptions,
+ ): Promise;
+
+ /**
+ * Show a reward video ad
+ *
+ * @group RewardVideo
+ * @since 1.1.2
+ */
+ showRewardInterstitialAd(): Promise;
+
+ addListener(
+ eventName: RewardInterstitialAdPluginEvents.FailedToLoad,
+ listenerFunc: (error: AdMobError) => void,
+ ): Promise;
+
+ addListener(
+ eventName: RewardInterstitialAdPluginEvents.Loaded,
+ listenerFunc: (info: AdLoadInfo) => void,
+ ): Promise;
+
+ addListener(
+ eventName: RewardInterstitialAdPluginEvents.Rewarded,
+ listenerFunc: (reward: AdMobRewardInterstitialItem) => void,
+ ): Promise;
+
+ addListener(
+ eventName: RewardInterstitialAdPluginEvents.Dismissed,
+ listenerFunc: () => void,
+ ): Promise;
+
+ addListener(
+ eventName: RewardInterstitialAdPluginEvents.FailedToShow,
+ listenerFunc: (error: AdMobError) => void,
+ ): Promise;
+
+ addListener(
+ eventName: RewardInterstitialAdPluginEvents.Showed,
+ listenerFunc: () => void,
+ ): Promise;
+}
diff --git a/src/reward-interstitial/reward-interstitial-item.interface.ts b/src/reward-interstitial/reward-interstitial-item.interface.ts
new file mode 100644
index 00000000..2feb5213
--- /dev/null
+++ b/src/reward-interstitial/reward-interstitial-item.interface.ts
@@ -0,0 +1,15 @@
+/**
+ * For more information
+ * https://developers.google.com/admob/android/rewarded-video-adapters?hl=en
+ */
+export interface AdMobRewardInterstitialItem {
+ /**
+ * Rewarded type user got
+ */
+ type: string;
+
+ /**
+ * Rewarded amount user got
+ */
+ amount: number;
+}
diff --git a/src/web.ts b/src/web.ts
index c0e9f534..622220cd 100644
--- a/src/web.ts
+++ b/src/web.ts
@@ -99,4 +99,18 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin {
amount: 0,
};
}
+
+ async prepareRewardInterstitialAd(options: AdOptions): Promise {
+ console.log(options);
+ return {
+ adUnitId: options.adId,
+ };
+ }
+
+ async showRewardInterstitialAd(): Promise {
+ return {
+ type: '',
+ amount: 0,
+ };
+ }
}