From e3c2d249b4f1e223fc275b0fc0193e3e8c986960 Mon Sep 17 00:00:00 2001 From: beto Date: Sat, 11 Oct 2025 02:13:24 +0000 Subject: [PATCH 01/26] feat: soporte para App Open Ad en Android e iOS --- README.md | 128 +++++++++++++++++- .../getcapacitor/community/admob/AdMob.java | 16 +++ .../admob/appopen/AppOpenAdManager.java | 72 ++++++++++ .../admob/appopen/AppOpenAdPlugin.java | 59 ++++++++ demo/angular/src/app/app.component.ts | 35 ++++- ios/Sources/AdMobPlugin/AdMobPlugin.swift | 17 ++- .../AppOpen/AppOpenAdManager.swift | 60 ++++++++ .../AdMobPlugin/AppOpen/AppOpenAdPlugin.swift | 52 +++++++ package-lock.json | 3 +- src/app-open/app-open-ad-options.interface.ts | 5 + .../app-open-ad-plugin-events.enum.ts | 7 + .../app-open-definitions.interface.ts | 34 +++++ src/app-open/index.ts | 3 + src/definitions.ts | 4 +- src/index.ts | 1 + src/web.ts | 22 +++ 16 files changed, 511 insertions(+), 7 deletions(-) create mode 100644 android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java create mode 100644 android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java create mode 100644 ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift create mode 100644 ios/Sources/AdMobPlugin/AppOpen/AppOpenAdPlugin.swift create mode 100644 src/app-open/app-open-ad-options.interface.ts create mode 100644 src/app-open/app-open-ad-plugin-events.enum.ts create mode 100644 src/app-open/app-open-definitions.interface.ts create mode 100644 src/app-open/index.ts diff --git a/README.md b/README.md index dea1fb0e..9f4e297f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,42 @@ +### Mostrar App Open Ad + +```ts +import { + AdMob, + AppOpenAdPluginEvents, + AppOpenAdOptions, +} from '@capacitor-community/admob'; + +export async function showAppOpenAd(): Promise { + // Escuchar eventos + AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { + console.log('App Open Ad cargado'); + }); + AdMob.addListener(AppOpenAdPluginEvents.FailedToLoad, () => { + console.log('Fallo al cargar App Open Ad'); + }); + AdMob.addListener(AppOpenAdPluginEvents.Opened, () => { + console.log('App Open Ad abierto'); + }); + AdMob.addListener(AppOpenAdPluginEvents.Closed, () => { + console.log('App Open Ad cerrado'); + }); + AdMob.addListener(AppOpenAdPluginEvents.FailedToShow, () => { + console.log('Fallo al mostrar App Open Ad'); + }); + + const options: AppOpenAdOptions = { + adUnitId: 'TU_AD_UNIT_ID', + showOnColdStart: true, // Opcional + showOnForeground: true, // Opcional + }; + await AdMob.loadAppOpen(options); + const { value } = await AdMob.isAppOpenLoaded(); + if (value) { + await AdMob.showAppOpen(); + } +} +


AdMob

@capacitor-community/admob

@@ -371,6 +410,10 @@ AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => { * [`addListener(RewardInterstitialAdPluginEvents.Dismissed, ...)`](#addlistenerrewardinterstitialadplugineventsdismissed-) * [`addListener(RewardInterstitialAdPluginEvents.FailedToShow, ...)`](#addlistenerrewardinterstitialadplugineventsfailedtoshow-) * [`addListener(RewardInterstitialAdPluginEvents.Showed, ...)`](#addlistenerrewardinterstitialadplugineventsshowed-) +* [`loadAppOpen(...)`](#loadappopen) +* [`showAppOpen()`](#showappopen) +* [`isAppOpenLoaded()`](#isappopenloaded) +* [`addListener(AppOpenAdPluginEvents, ...)`](#addlistenerappopenadpluginevents-) * [Interfaces](#interfaces) * [Type Aliases](#type-aliases) * [Enums](#enums) @@ -1067,6 +1110,63 @@ addListener(eventName: RewardInterstitialAdPluginEvents.Showed, listenerFunc: () -------------------- +### loadAppOpen(...) + +```typescript +loadAppOpen(options: AppOpenAdOptions) => Promise +``` + +Carga un anuncio App Open + +| Param | Type | +| ------------- | ------------------------------------------------------------- | +| **`options`** | AppOpenAdOptions | + +-------------------- + + +### showAppOpen() + +```typescript +showAppOpen() => Promise +``` + +Muestra el anuncio App Open si está cargado + +-------------------- + + +### isAppOpenLoaded() + +```typescript +isAppOpenLoaded() => Promise<{ value: boolean; }> +``` + +Verifica si el anuncio App Open está cargado + +**Returns:** Promise<{ value: boolean; }> + +-------------------- + + +### addListener(AppOpenAdPluginEvents, ...) + +```typescript +addListener(eventName: AppOpenAdPluginEvents, listenerFunc: (...args: any[]) => void) => Promise +``` + +Agrega listeners para eventos de App Open + +| Param | Type | +| ------------------ | ----------------------------------------------------------------------- | +| **`eventName`** | AppOpenAdPluginEvents | +| **`listenerFunc`** | (...args: any[]) => void | + +**Returns:** Promise<PluginListenerHandle> + +-------------------- + + ### Interfaces @@ -1136,7 +1236,7 @@ When notice listener of OnAdLoaded, you can get banner size. #### AdMobError -For more information +For more information https://developers.google.com/android/reference/com/google/android/gms/ads/AdError | Prop | Type | Description | @@ -1196,7 +1296,7 @@ https://developers.google.com/android/reference/com/google/android/gms/ads/AdErr #### AdMobRewardItem -For more information +For more information https://developers.google.com/admob/android/rewarded-video-adapters?hl=en | Prop | Type | Description | @@ -1228,6 +1328,15 @@ https://developers.google.com/admob/android/rewarded-video-adapters?hl=en | **`amount`** | number | Rewarded amount user got | +#### AppOpenAdOptions + +| Prop | Type | +| ---------------------- | -------------------- | +| **`adUnitId`** | string | +| **`showOnColdStart`** | boolean | +| **`showOnForeground`** | boolean | + + ### Type Aliases @@ -1240,7 +1349,9 @@ https://developers.google.com/admob/android/rewarded-video-adapters?hl=en From T, pick a set of properties whose keys are in the union K -{ [P in K]: T[P]; } +{ + [P in K]: T[P]; + } ### Enums @@ -1354,6 +1465,17 @@ From T, pick a set of properties whose keys are in the union K | **`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 | + +#### AppOpenAdPluginEvents + +| Members | Value | +| ------------------ | ------------------------------------ | +| **`Loaded`** | 'appOpenAdLoaded' | +| **`FailedToLoad`** | 'appOpenAdFailedToLoad' | +| **`Opened`** | 'appOpenAdOpened' | +| **`Closed`** | 'appOpenAdClosed' | +| **`FailedToShow`** | 'appOpenAdFailedToShow' | + ## 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 b8b36c79..efff6b8c 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +++ b/android/src/main/java/com/getcapacitor/community/admob/AdMob.java @@ -19,6 +19,22 @@ import com.google.android.gms.ads.RequestConfiguration; import com.google.android.gms.ads.initialization.InitializationStatus; import com.google.android.gms.ads.initialization.OnInitializationCompleteListener; +import com.getcapacitor.community.admob.appopen.AppOpenAdPlugin; + private final AppOpenAdPlugin appOpenAdPlugin = new AppOpenAdPlugin(); + @PluginMethod + public void loadAppOpen(final PluginCall call) { + appOpenAdPlugin.loadAppOpen(call); + } + + @PluginMethod + public void showAppOpen(final PluginCall call) { + appOpenAdPlugin.showAppOpen(call); + } + + @PluginMethod + public void isAppOpenLoaded(final PluginCall call) { + appOpenAdPlugin.isAppOpenLoaded(call); + } import org.json.JSONException; @CapacitorPlugin( diff --git a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java new file mode 100644 index 00000000..2f6b6966 --- /dev/null +++ b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java @@ -0,0 +1,72 @@ +package com.getcapacitor.community.admob.appopen; + +import android.app.Activity; +import android.content.Context; +import androidx.annotation.NonNull; +import com.google.android.gms.ads.appopen.AppOpenAd; +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.LoadAdError; +import com.google.android.gms.ads.FullScreenContentCallback; + +public class AppOpenAdManager { + private AppOpenAd appOpenAd = null; + private boolean isLoadingAd = false; + private boolean isShowingAd = false; + private String adUnitId; + + public AppOpenAdManager(String adUnitId) { + this.adUnitId = adUnitId; + } + + public void loadAd(Context context, final Runnable onLoaded, final Runnable onFailed) { + if (isLoadingAd || appOpenAd != null) return; + isLoadingAd = true; + AdRequest request = new AdRequest.Builder().build(); + AppOpenAd.load( + context, + adUnitId, + request, + AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, + new AppOpenAd.AppOpenAdLoadCallback() { + @Override + public void onAdLoaded(@NonNull AppOpenAd ad) { + appOpenAd = ad; + isLoadingAd = false; + if (onLoaded != null) onLoaded.run(); + } + @Override + public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { + isLoadingAd = false; + if (onFailed != null) onFailed.run(); + } + } + ); + } + + public void showAdIfAvailable(Activity activity, final Runnable onClosed, final Runnable onFailedToShow) { + if (appOpenAd == null || isShowingAd) { + if (onFailedToShow != null) onFailedToShow.run(); + return; + } + isShowingAd = true; + appOpenAd.setFullScreenContentCallback(new FullScreenContentCallback() { + @Override + public void onAdDismissedFullScreenContent() { + appOpenAd = null; + isShowingAd = false; + if (onClosed != null) onClosed.run(); + } + @Override + public void onAdFailedToShowFullScreenContent(com.google.android.gms.ads.AdError adError) { + appOpenAd = null; + isShowingAd = false; + if (onFailedToShow != null) onFailedToShow.run(); + } + }); + appOpenAd.show(activity); + } + + public boolean isAdLoaded() { + return appOpenAd != null; + } +} diff --git a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java new file mode 100644 index 00000000..e0ab7b4b --- /dev/null +++ b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java @@ -0,0 +1,59 @@ +package com.getcapacitor.community.admob.appopen; + +import android.app.Activity; +import android.content.Context; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import com.getcapacitor.community.admob.AdMobPlugin; +import org.json.JSONException; +import org.json.JSONObject; + +@CapacitorPlugin(name = "AppOpenAd") +public class AppOpenAdPlugin extends AdMobPlugin { + private AppOpenAdManager appOpenAdManager; + + @PluginMethod + public void loadAppOpen(PluginCall call) { + String adUnitId = call.getString("adUnitId"); + if (adUnitId == null) { + call.reject("adUnitId is required"); + return; + } + if (appOpenAdManager == null) { + appOpenAdManager = new AppOpenAdManager(adUnitId); + } + Context context = getContext(); + appOpenAdManager.loadAd(context, () -> { + notifyListeners("appOpenAdLoaded", new JSONObject()); + call.resolve(); + }, () -> { + notifyListeners("appOpenAdFailedToLoad", new JSONObject()); + call.reject("Failed to load App Open Ad"); + }); + } + + @PluginMethod + public void showAppOpen(PluginCall call) { + Activity activity = getActivity(); + appOpenAdManager.showAdIfAvailable(activity, () -> { + notifyListeners("appOpenAdClosed", new JSONObject()); + call.resolve(); + }, () -> { + notifyListeners("appOpenAdFailedToShow", new JSONObject()); + call.reject("Failed to show App Open Ad"); + }); + } + + @PluginMethod + public void isAppOpenLoaded(PluginCall call) { + boolean loaded = appOpenAdManager != null && appOpenAdManager.isAdLoaded(); + try { + JSONObject result = new JSONObject(); + result.put("value", loaded); + call.resolve(result); + } catch (JSONException e) { + call.reject("JSON error"); + } + } +} diff --git a/demo/angular/src/app/app.component.ts b/demo/angular/src/app/app.component.ts index 4aeaebb2..5c686035 100644 --- a/demo/angular/src/app/app.component.ts +++ b/demo/angular/src/app/app.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { Platform } from '@ionic/angular'; -import { AdMob, AdMobInitializationOptions } from '@capacitor-community/admob'; +import { AdMob, AppOpenAdPluginEvents, AppOpenAdOptions } from '@capacitor-community/admob'; @Component({ selector: 'app-root', @@ -32,6 +32,39 @@ export class AppComponent { AdMob.setApplicationVolume({ volume: 0.5, }); + + // Ejemplo de App Open Ad + this.showAppOpenAd(); + }); + } + + async showAppOpenAd() { + // Escuchar eventos + AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { + console.log('App Open Ad cargado'); + }); + AdMob.addListener(AppOpenAdPluginEvents.FailedToLoad, () => { + console.log('Fallo al cargar App Open Ad'); }); + AdMob.addListener(AppOpenAdPluginEvents.Opened, () => { + console.log('App Open Ad abierto'); + }); + AdMob.addListener(AppOpenAdPluginEvents.Closed, () => { + console.log('App Open Ad cerrado'); + }); + AdMob.addListener(AppOpenAdPluginEvents.FailedToShow, () => { + console.log('Fallo al mostrar App Open Ad'); + }); + + const options: AppOpenAdOptions = { + adUnitId: 'TU_AD_UNIT_ID', // Reemplaza por tu ID real + showOnColdStart: true, + showOnForeground: true, + }; + await AdMob.loadAppOpen(options); + const { value } = await AdMob.isAppOpenLoaded(); + if (value) { + await AdMob.showAppOpen(); + } } } diff --git a/ios/Sources/AdMobPlugin/AdMobPlugin.swift b/ios/Sources/AdMobPlugin/AdMobPlugin.swift index 529fbaac..a71a362d 100644 --- a/ios/Sources/AdMobPlugin/AdMobPlugin.swift +++ b/ios/Sources/AdMobPlugin/AdMobPlugin.swift @@ -28,8 +28,23 @@ public class AdMobPlugin: CAPPlugin, CAPBridgedPlugin { CAPPluginMethod(name: "prepareRewardVideoAd", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "showRewardVideoAd", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "prepareRewardInterstitialAd", returnType: CAPPluginReturnPromise), - CAPPluginMethod(name: "showRewardInterstitialAd", returnType: CAPPluginReturnPromise) + CAPPluginMethod(name: "showRewardInterstitialAd", returnType: CAPPluginReturnPromise), + CAPPluginMethod(name: "loadAppOpen", returnType: CAPPluginReturnPromise), + CAPPluginMethod(name: "showAppOpen", returnType: CAPPluginReturnPromise), + CAPPluginMethod(name: "isAppOpenLoaded", returnType: CAPPluginReturnPromise) ] + private let appOpenAdPlugin = AppOpenAdPlugin() + @objc func loadAppOpen(_ call: CAPPluginCall) { + appOpenAdPlugin.loadAppOpen(call) + } + + @objc func showAppOpen(_ call: CAPPluginCall) { + appOpenAdPlugin.showAppOpen(call) + } + + @objc func isAppOpenLoaded(_ call: CAPPluginCall) { + appOpenAdPlugin.isAppOpenLoaded(call) + } var testingDevices: [String] = [] diff --git a/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift b/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift new file mode 100644 index 00000000..fedbc9c8 --- /dev/null +++ b/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift @@ -0,0 +1,60 @@ +import Foundation +import GoogleMobileAds +import UIKit + +@objc public class AppOpenAdManager: NSObject { + private var appOpenAd: GADAppOpenAd? + private var isLoadingAd = false + private var isShowingAd = false + private var adUnitId: String + + public init(adUnitId: String) { + self.adUnitId = adUnitId + } + + public func loadAd(rootViewController: UIViewController, onLoaded: @escaping () -> Void, onFailed: @escaping () -> Void) { + if isLoadingAd || appOpenAd != nil { return } + isLoadingAd = true + GADAppOpenAd.load(withAdUnitID: adUnitId, request: GADRequest(), orientation: .portrait) { [weak self] ad, error in + self?.isLoadingAd = false + if let ad = ad { + self?.appOpenAd = ad + onLoaded() + } else { + onFailed() + } + } + } + + public func showAdIfAvailable(rootViewController: UIViewController, onClosed: @escaping () -> Void, onFailedToShow: @escaping () -> Void) { + guard let ad = appOpenAd, !isShowingAd else { + onFailedToShow() + return + } + isShowingAd = true + ad.fullScreenContentDelegate = self + ad.present(fromRootViewController: rootViewController) + self.onClosed = onClosed + self.onFailedToShow = onFailedToShow + } + + public func isAdLoaded() -> Bool { + return appOpenAd != nil + } + + private var onClosed: (() -> Void)? + private var onFailedToShow: (() -> Void)? +} + +extension AppOpenAdManager: GADFullScreenContentDelegate { + public func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) { + appOpenAd = nil + isShowingAd = false + onClosed?() + } + public func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { + appOpenAd = nil + isShowingAd = false + onFailedToShow?() + } +} diff --git a/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdPlugin.swift b/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdPlugin.swift new file mode 100644 index 00000000..05bb2fd4 --- /dev/null +++ b/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdPlugin.swift @@ -0,0 +1,52 @@ +import Foundation +import Capacitor +import UIKit + +@objc(AppOpenAdPlugin) +public class AppOpenAdPlugin: CAPPlugin { + private var appOpenAdManager: AppOpenAdManager? + + @objc func loadAppOpen(_ call: CAPPluginCall) { + guard let adUnitId = call.getString("adUnitId") else { + call.reject("adUnitId is required") + return + } + if appOpenAdManager == nil { + appOpenAdManager = AppOpenAdManager(adUnitId: adUnitId) + } + DispatchQueue.main.async { + if let rootVC = UIApplication.shared.keyWindow?.rootViewController { + self.appOpenAdManager?.loadAd(rootViewController: rootVC, onLoaded: { + self.notifyListeners("appOpenAdLoaded", data: [:]) + call.resolve() + }, onFailed: { + self.notifyListeners("appOpenAdFailedToLoad", data: [:]) + call.reject("Failed to load App Open Ad") + }) + } else { + call.reject("No rootViewController") + } + } + } + + @objc func showAppOpen(_ call: CAPPluginCall) { + DispatchQueue.main.async { + if let rootVC = UIApplication.shared.keyWindow?.rootViewController { + self.appOpenAdManager?.showAdIfAvailable(rootViewController: rootVC, onClosed: { + self.notifyListeners("appOpenAdClosed", data: [:]) + call.resolve() + }, onFailedToShow: { + self.notifyListeners("appOpenAdFailedToShow", data: [:]) + call.reject("Failed to show App Open Ad") + }) + } else { + call.reject("No rootViewController") + } + } + } + + @objc func isAppOpenLoaded(_ call: CAPPluginCall) { + let loaded = appOpenAdManager?.isAdLoaded() ?? false + call.resolve(["value": loaded]) + } +} diff --git a/package-lock.json b/package-lock.json index 23a5610c..a68d02b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8277,8 +8277,9 @@ }, "node_modules/rimraf": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, - "license": "ISC", "dependencies": { "glob": "^11.0.0", "package-json-from-dist": "^1.0.0" diff --git a/src/app-open/app-open-ad-options.interface.ts b/src/app-open/app-open-ad-options.interface.ts new file mode 100644 index 00000000..01cd3835 --- /dev/null +++ b/src/app-open/app-open-ad-options.interface.ts @@ -0,0 +1,5 @@ +export interface AppOpenAdOptions { + adUnitId: string; + showOnColdStart?: boolean; + showOnForeground?: boolean; +} diff --git a/src/app-open/app-open-ad-plugin-events.enum.ts b/src/app-open/app-open-ad-plugin-events.enum.ts new file mode 100644 index 00000000..327b1e7c --- /dev/null +++ b/src/app-open/app-open-ad-plugin-events.enum.ts @@ -0,0 +1,7 @@ +export enum AppOpenAdPluginEvents { + Loaded = 'appOpenAdLoaded', + FailedToLoad = 'appOpenAdFailedToLoad', + Opened = 'appOpenAdOpened', + Closed = 'appOpenAdClosed', + FailedToShow = 'appOpenAdFailedToShow', +} diff --git a/src/app-open/app-open-definitions.interface.ts b/src/app-open/app-open-definitions.interface.ts new file mode 100644 index 00000000..4f2d1196 --- /dev/null +++ b/src/app-open/app-open-definitions.interface.ts @@ -0,0 +1,34 @@ +import type { PluginListenerHandle } from '@capacitor/core'; +import type { ValidateAllEventsEnumAreImplemented } from '../private/validate-all-events-implemented.type'; +import type { AppOpenAdPluginEvents } from './app-open-ad-plugin-events.enum'; +import type { AppOpenAdOptions } from './app-open-ad-options.interface'; + +export type AppOpenDefinitionsHasAllEvents = ValidateAllEventsEnumAreImplemented< + AppOpenAdPluginEvents, + AppOpenAdPlugin +>; + +export interface AppOpenAdPlugin { + /** + * Carga un anuncio App Open + */ + loadAppOpen(options: AppOpenAdOptions): Promise; + + /** + * Muestra el anuncio App Open si está cargado + */ + showAppOpen(): Promise; + + /** + * Verifica si el anuncio App Open está cargado + */ + isAppOpenLoaded(): Promise<{ value: boolean }>; + + /** + * Agrega listeners para eventos de App Open + */ + addListener( + eventName: AppOpenAdPluginEvents, + listenerFunc: (...args: any[]) => void, + ): Promise; +} diff --git a/src/app-open/index.ts b/src/app-open/index.ts new file mode 100644 index 00000000..9b1c9536 --- /dev/null +++ b/src/app-open/index.ts @@ -0,0 +1,3 @@ +export * from './app-open-ad-options.interface'; +export * from './app-open-ad-plugin-events.enum'; +export * from './app-open-definitions.interface'; diff --git a/src/definitions.ts b/src/definitions.ts index 08361cf4..89090a9c 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -4,12 +4,14 @@ 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'; +import type { AppOpenAdPlugin } from './app-open'; type AdMobDefinitions = BannerDefinitions & RewardDefinitions & RewardInterstitialDefinitions & InterstitialDefinitions & - AdmobConsentDefinitions; + AdmobConsentDefinitions & + AppOpenAdPlugin; export interface AdMobPlugin extends AdMobDefinitions { /** diff --git a/src/index.ts b/src/index.ts index fef4da63..3ac3e079 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,4 +13,5 @@ export * from './reward-interstitial/index'; export * from './reward/index'; export * from './consent/index'; export * from './shared/index'; +export * from './app-open/index'; export { AdMob }; diff --git a/src/web.ts b/src/web.ts index 938e0fb4..f20c1530 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,3 +1,5 @@ + + import { WebPlugin } from '@capacitor/core'; import type { @@ -18,6 +20,8 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { console.log('initialize'); } + // Métodos stub para App Open Ad + async requestTrackingAuthorization(): Promise { console.log('requestTrackingAuthorization'); } @@ -120,4 +124,22 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { amount: 0, }; } + + // Métodos stub para App Open Ad + async loadAppOpen(options: any): Promise { + console.log('loadAppOpen', options); + } + + async showAppOpen(): Promise { + console.log('showAppOpen'); + } + + async isAppOpenLoaded(): Promise<{ value: boolean }> { + return { value: false }; + } + + addListener(eventName: string, _listenerFunc: (...args: any[]) => void): any { + console.log('addListener', eventName); + return { remove: async () => {} }; + } } From ded6452384205064b7acdf0ec7ec8ea7f86e0acf Mon Sep 17 00:00:00 2001 From: beto Date: Mon, 13 Oct 2025 12:00:21 -0300 Subject: [PATCH 02/26] Update src/web.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/web.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/web.ts b/src/web.ts index f20c1530..31cfc233 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,5 +1,3 @@ - - import { WebPlugin } from '@capacitor/core'; import type { From d1bbba17492ccfc362f3fc1b4a36cf63e61ecec3 Mon Sep 17 00:00:00 2001 From: beto Date: Mon, 13 Oct 2025 12:00:48 -0300 Subject: [PATCH 03/26] Update src/web.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/web.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/web.ts b/src/web.ts index 31cfc233..e5c7d97a 100644 --- a/src/web.ts +++ b/src/web.ts @@ -18,7 +18,6 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { console.log('initialize'); } - // Métodos stub para App Open Ad async requestTrackingAuthorization(): Promise { console.log('requestTrackingAuthorization'); From 9494d2e6f8d860bfa1664713760913948d102948 Mon Sep 17 00:00:00 2001 From: beto Date: Mon, 13 Oct 2025 12:01:23 -0300 Subject: [PATCH 04/26] Update demo/angular/src/app/app.component.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- demo/angular/src/app/app.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/angular/src/app/app.component.ts b/demo/angular/src/app/app.component.ts index 5c686035..0357a3ce 100644 --- a/demo/angular/src/app/app.component.ts +++ b/demo/angular/src/app/app.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { Platform } from '@ionic/angular'; -import { AdMob, AppOpenAdPluginEvents, AppOpenAdOptions } from '@capacitor-community/admob'; +import { AdMob, AppOpenAdPluginEvents, AppOpenAdOptions, AdMobInitializationOptions } from '@capacitor-community/admob'; @Component({ selector: 'app-root', From e65274f6b9840ac19b9f4a3f160f4d3141e0fba2 Mon Sep 17 00:00:00 2001 From: beto Date: Mon, 13 Oct 2025 12:01:47 -0300 Subject: [PATCH 05/26] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 9f4e297f..1edaf2f1 100644 --- a/README.md +++ b/README.md @@ -1349,9 +1349,7 @@ https://developers.google.com/admob/android/rewarded-video-adapters?hl=en From T, pick a set of properties whose keys are in the union K -{ - [P in K]: T[P]; - } +{ [P in K]: T[P] } ### Enums From a1d73cf572ef93744789fe86edc63a2b6fc6595c Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 12:09:30 -0300 Subject: [PATCH 06/26] Update README.md --- README.md | 77 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 1edaf2f1..5a248ad1 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,3 @@ -### Mostrar App Open Ad - -```ts -import { - AdMob, - AppOpenAdPluginEvents, - AppOpenAdOptions, -} from '@capacitor-community/admob'; - -export async function showAppOpenAd(): Promise { - // Escuchar eventos - AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { - console.log('App Open Ad cargado'); - }); - AdMob.addListener(AppOpenAdPluginEvents.FailedToLoad, () => { - console.log('Fallo al cargar App Open Ad'); - }); - AdMob.addListener(AppOpenAdPluginEvents.Opened, () => { - console.log('App Open Ad abierto'); - }); - AdMob.addListener(AppOpenAdPluginEvents.Closed, () => { - console.log('App Open Ad cerrado'); - }); - AdMob.addListener(AppOpenAdPluginEvents.FailedToShow, () => { - console.log('Fallo al mostrar App Open Ad'); - }); - - const options: AppOpenAdOptions = { - adUnitId: 'TU_AD_UNIT_ID', - showOnColdStart: true, // Opcional - showOnForeground: true, // Opcional - }; - await AdMob.loadAppOpen(options); - const { value } = await AdMob.isAppOpenLoaded(); - if (value) { - await AdMob.showAppOpen(); - } -}


AdMob

@@ -227,7 +189,46 @@ const consentInfo = await AdMob.requestConsentInfo({ 2. AdMob.requestConsentInfo 3. AdMob.showConsentForm (If consent form required ) 3/ AdMob.showBanner + +### Show App Open Ad +```ts +import { + AdMob, + AppOpenAdPluginEvents, + AppOpenAdOptions, +} from '@capacitor-community/admob'; + +export async function showAppOpenAd(): Promise { + // Escuchar eventos + AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { + console.log('App Open Ad cargado'); + }); + AdMob.addListener(AppOpenAdPluginEvents.FailedToLoad, () => { + console.log('Fallo al cargar App Open Ad'); + }); + AdMob.addListener(AppOpenAdPluginEvents.Opened, () => { + console.log('App Open Ad abierto'); + }); + AdMob.addListener(AppOpenAdPluginEvents.Closed, () => { + console.log('App Open Ad cerrado'); + }); + AdMob.addListener(AppOpenAdPluginEvents.FailedToShow, () => { + console.log('Fallo al mostrar App Open Ad'); + }); + + const options: AppOpenAdOptions = { + adUnitId: 'TU_AD_UNIT_ID', + showOnColdStart: true, // Opcional + showOnForeground: true, // Opcional + }; + await AdMob.loadAppOpen(options); + const { value } = await AdMob.isAppOpenLoaded(); + if (value) { + await AdMob.showAppOpen(); + } +} +``` ### Show Banner ```ts From 2549a0a7e2c9a438e5238c70c07751517335a7fe Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 12:13:57 -0300 Subject: [PATCH 07/26] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5a248ad1..7cb8310e 100644 --- a/README.md +++ b/README.md @@ -202,19 +202,19 @@ import { export async function showAppOpenAd(): Promise { // Escuchar eventos AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { - console.log('App Open Ad cargado'); + console.log('App Open Ad loaded'); }); AdMob.addListener(AppOpenAdPluginEvents.FailedToLoad, () => { - console.log('Fallo al cargar App Open Ad'); + console.log('Failed to load App Open Ad'); }); AdMob.addListener(AppOpenAdPluginEvents.Opened, () => { - console.log('App Open Ad abierto'); + console.log('App Open Ad open'); }); AdMob.addListener(AppOpenAdPluginEvents.Closed, () => { - console.log('App Open Ad cerrado'); + console.log('App Open Ad close'); }); AdMob.addListener(AppOpenAdPluginEvents.FailedToShow, () => { - console.log('Fallo al mostrar App Open Ad'); + console.log('Failed to load App Open Ad'); }); const options: AppOpenAdOptions = { From 8f33ad7f0ec039d6be4359e45815feeba7253a72 Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 12:15:57 -0300 Subject: [PATCH 08/26] Update AppOpenAdManager.java --- .../community/admob/appopen/AppOpenAdManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java index 2f6b6966..46ad9dce 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java +++ b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java @@ -19,7 +19,9 @@ public AppOpenAdManager(String adUnitId) { } public void loadAd(Context context, final Runnable onLoaded, final Runnable onFailed) { - if (isLoadingAd || appOpenAd != null) return; + if (isLoadingAd || appOpenAd != null) { + return; + } isLoadingAd = true; AdRequest request = new AdRequest.Builder().build(); AppOpenAd.load( From 4aac09becf9d5bf6f62465e5dc949dec4d4bcf46 Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 12:19:29 -0300 Subject: [PATCH 09/26] Update AppOpenAdManager.java --- .../community/admob/appopen/AppOpenAdManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java index 46ad9dce..3d81e7bd 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java +++ b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java @@ -34,12 +34,16 @@ public void loadAd(Context context, final Runnable onLoaded, final Runnable onFa public void onAdLoaded(@NonNull AppOpenAd ad) { appOpenAd = ad; isLoadingAd = false; - if (onLoaded != null) onLoaded.run(); + if (onLoaded != null) { + onLoaded.run(); + } } @Override public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { isLoadingAd = false; - if (onFailed != null) onFailed.run(); + if (onFailed != null) { + onFailed.run(); + } } } ); From 23ab476a110914f40a9cb42e8b6a649b1c907eec Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 12:22:51 -0300 Subject: [PATCH 10/26] Update AppOpenAdManager.java no single line ifs please --- .../admob/appopen/AppOpenAdManager.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java index 3d81e7bd..d897292d 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java +++ b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdManager.java @@ -20,10 +20,12 @@ public AppOpenAdManager(String adUnitId) { public void loadAd(Context context, final Runnable onLoaded, final Runnable onFailed) { if (isLoadingAd || appOpenAd != null) { - return; + return; } + isLoadingAd = true; AdRequest request = new AdRequest.Builder().build(); + AppOpenAd.load( context, adUnitId, @@ -34,13 +36,16 @@ public void loadAd(Context context, final Runnable onLoaded, final Runnable onFa public void onAdLoaded(@NonNull AppOpenAd ad) { appOpenAd = ad; isLoadingAd = false; + if (onLoaded != null) { onLoaded.run(); } } + @Override public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { isLoadingAd = false; + if (onFailed != null) { onFailed.run(); } @@ -51,24 +56,35 @@ public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { public void showAdIfAvailable(Activity activity, final Runnable onClosed, final Runnable onFailedToShow) { if (appOpenAd == null || isShowingAd) { - if (onFailedToShow != null) onFailedToShow.run(); + if (onFailedToShow != null) { + onFailedToShow.run(); + } return; } + isShowingAd = true; appOpenAd.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdDismissedFullScreenContent() { appOpenAd = null; isShowingAd = false; - if (onClosed != null) onClosed.run(); + + if (onClosed != null) { + onClosed.run(); + } } + @Override public void onAdFailedToShowFullScreenContent(com.google.android.gms.ads.AdError adError) { appOpenAd = null; isShowingAd = false; - if (onFailedToShow != null) onFailedToShow.run(); + + if (onFailedToShow != null) { + onFailedToShow.run(); + } } }); + appOpenAd.show(activity); } From eee34dee28b781b0b624bebcf9dacc19e6fc08e9 Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 12:37:23 -0300 Subject: [PATCH 11/26] Update AdMob.java this should be inside the AdMob class. --- .../getcapacitor/community/admob/AdMob.java | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) 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 efff6b8c..cb5a064c 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +++ b/android/src/main/java/com/getcapacitor/community/admob/AdMob.java @@ -8,6 +8,7 @@ import com.getcapacitor.PluginMethod; import com.getcapacitor.annotation.CapacitorPlugin; import com.getcapacitor.annotation.Permission; + import com.getcapacitor.community.admob.banner.BannerExecutor; import com.getcapacitor.community.admob.consent.AdConsentExecutor; import com.getcapacitor.community.admob.helpers.AuthorizationStatusEnum; @@ -15,30 +16,19 @@ import com.getcapacitor.community.admob.interstitial.InterstitialAdCallbackAndListeners; import com.getcapacitor.community.admob.rewarded.AdRewardExecutor; import com.getcapacitor.community.admob.rewardedinterstitial.AdRewardInterstitialExecutor; +import com.getcapacitor.community.admob.appopen.AppOpenAdPlugin; + import com.google.android.gms.ads.MobileAds; import com.google.android.gms.ads.RequestConfiguration; import com.google.android.gms.ads.initialization.InitializationStatus; import com.google.android.gms.ads.initialization.OnInitializationCompleteListener; -import com.getcapacitor.community.admob.appopen.AppOpenAdPlugin; - private final AppOpenAdPlugin appOpenAdPlugin = new AppOpenAdPlugin(); - @PluginMethod - public void loadAppOpen(final PluginCall call) { - appOpenAdPlugin.loadAppOpen(call); - } - @PluginMethod - public void showAppOpen(final PluginCall call) { - appOpenAdPlugin.showAppOpen(call); - } - - @PluginMethod - public void isAppOpenLoaded(final PluginCall call) { - appOpenAdPlugin.isAppOpenLoaded(call); - } import org.json.JSONException; @CapacitorPlugin( - permissions = { @Permission(alias = "network", strings = { Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.INTERNET }) } + permissions = { + @Permission(alias = "network", strings = { Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.INTERNET }) + } ) public class AdMob extends Plugin { @@ -50,18 +40,21 @@ public class AdMob extends Plugin { this::notifyListeners, getLogTag() ); + private final AdRewardExecutor adRewardExecutor = new AdRewardExecutor( this::getContext, this::getActivity, 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, @@ -77,7 +70,28 @@ public class AdMob extends Plugin { getLogTag() ); - // Initialize AdMob with appId + // ✅ AppOpenAd Plugin block now inside the class + private final AppOpenAdPlugin appOpenAdPlugin = new AppOpenAdPlugin(); + + @PluginMethod + public void loadAppOpen(final PluginCall call) { + appOpenAdPlugin.loadAppOpen(call); + } + + @PluginMethod + public void showAppOpen(final PluginCall call) { + appOpenAdPlugin.showAppOpen(call); + } + + @PluginMethod + public void isAppOpenLoaded(final PluginCall call) { + appOpenAdPlugin.isAppOpenLoaded(call); + } + + // --------------------------------------------------------- + // MAIN METHODS + // --------------------------------------------------------- + @PluginMethod public void initialize(final PluginCall call) { this.setRequestConfiguration(call); @@ -109,7 +123,10 @@ public void trackingAuthorizationStatus(final PluginCall call) { call.resolve(response); } - // User Consent + // --------------------------------------------------------- + // USER CONSENT + // --------------------------------------------------------- + @PluginMethod public void requestConsentInfo(final PluginCall call) { adConsentExecutor.requestConsentInfo(call, this::notifyListeners); @@ -130,6 +147,10 @@ public void resetConsentInfo(final PluginCall call) { adConsentExecutor.resetConsentInfo(call, this::notifyListeners); } + // --------------------------------------------------------- + // APP SETTINGS + // --------------------------------------------------------- + @PluginMethod public void setApplicationMuted(final PluginCall call) { Boolean muted = call.getBoolean("muted"); @@ -152,41 +173,48 @@ public void setApplicationVolume(final PluginCall call) { call.resolve(); } - // Show a banner Ad + // --------------------------------------------------------- + // BANNER ADS + // --------------------------------------------------------- + @PluginMethod public void showBanner(final PluginCall call) { bannerExecutor.showBanner(call); } - // Hide the banner, remove it from screen, but can show it later @PluginMethod public void hideBanner(final PluginCall call) { bannerExecutor.hideBanner(call); } - // Resume the banner, show it after hide @PluginMethod public void resumeBanner(final PluginCall call) { bannerExecutor.resumeBanner(call); } - // Destroy the banner, remove it from screen. @PluginMethod public void removeBanner(final PluginCall call) { bannerExecutor.removeBanner(call); } + // --------------------------------------------------------- + // INTERSTITIAL ADS + // --------------------------------------------------------- + @PluginMethod public void prepareInterstitial(final PluginCall call) { adInterstitialExecutor.prepareInterstitial(call, this::notifyListeners); } - // Show interstitial Ad @PluginMethod public void showInterstitial(final PluginCall call) { adInterstitialExecutor.showInterstitial(call, this::notifyListeners); } + // --------------------------------------------------------- + // REWARDED ADS + // --------------------------------------------------------- + @PluginMethod public void prepareRewardVideoAd(final PluginCall call) { adRewardExecutor.prepareRewardVideoAd(call, this::notifyListeners); @@ -207,10 +235,10 @@ public void showRewardInterstitialAd(final PluginCall call) { adRewardInterstitialExecutor.showRewardInterstitialAd(call, this::notifyListeners); } - /** - * @see Test Devices - * @see Target Settings - */ + // --------------------------------------------------------- + // REQUEST CONFIGURATION + // --------------------------------------------------------- + private void setRequestConfiguration(final PluginCall call) { // Testing Devices final boolean initializeForTesting = call.getBoolean("initializeForTesting", false); From 5359b1670c13d4f5ec51f3e78afd6aca85e0196a Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 13:24:41 -0300 Subject: [PATCH 12/26] Update app.component.ts comments in English. --- demo/angular/src/app/app.component.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/demo/angular/src/app/app.component.ts b/demo/angular/src/app/app.component.ts index 0357a3ce..59dc5779 100644 --- a/demo/angular/src/app/app.component.ts +++ b/demo/angular/src/app/app.component.ts @@ -33,7 +33,7 @@ export class AppComponent { volume: 0.5, }); - // Ejemplo de App Open Ad + // example of App Open Ad this.showAppOpenAd(); }); } @@ -41,23 +41,23 @@ export class AppComponent { async showAppOpenAd() { // Escuchar eventos AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { - console.log('App Open Ad cargado'); + console.log('App Open Ad loaded'); }); AdMob.addListener(AppOpenAdPluginEvents.FailedToLoad, () => { - console.log('Fallo al cargar App Open Ad'); + console.log('Failed to load App Open Ad'); }); AdMob.addListener(AppOpenAdPluginEvents.Opened, () => { - console.log('App Open Ad abierto'); + console.log('App Open Ad open'); }); AdMob.addListener(AppOpenAdPluginEvents.Closed, () => { - console.log('App Open Ad cerrado'); + console.log('App Open Ad close'); }); AdMob.addListener(AppOpenAdPluginEvents.FailedToShow, () => { - console.log('Fallo al mostrar App Open Ad'); + console.log('Failed to load App Open Ad'); }); const options: AppOpenAdOptions = { - adUnitId: 'TU_AD_UNIT_ID', // Reemplaza por tu ID real + adUnitId: 'TU_AD_UNIT_ID', // Replace with your real ID showOnColdStart: true, showOnForeground: true, }; From 3548d4ff2fd39819922065ed3aa1e602571d9ec7 Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 13:27:30 -0300 Subject: [PATCH 13/26] Update AppOpenAdManager.swift no single line ifs please --- ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift b/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift index fedbc9c8..9c2302e7 100644 --- a/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift +++ b/ios/Sources/AdMobPlugin/AppOpen/AppOpenAdManager.swift @@ -13,10 +13,14 @@ import UIKit } public func loadAd(rootViewController: UIViewController, onLoaded: @escaping () -> Void, onFailed: @escaping () -> Void) { - if isLoadingAd || appOpenAd != nil { return } + if isLoadingAd || appOpenAd != nil { + return + } + isLoadingAd = true GADAppOpenAd.load(withAdUnitID: adUnitId, request: GADRequest(), orientation: .portrait) { [weak self] ad, error in self?.isLoadingAd = false + if let ad = ad { self?.appOpenAd = ad onLoaded() @@ -31,6 +35,7 @@ import UIKit onFailedToShow() return } + isShowingAd = true ad.fullScreenContentDelegate = self ad.present(fromRootViewController: rootViewController) @@ -52,6 +57,7 @@ extension AppOpenAdManager: GADFullScreenContentDelegate { isShowingAd = false onClosed?() } + public func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { appOpenAd = nil isShowingAd = false From a00d29269b527aba4d4eb70a7ba00e2c3c0a0b8f Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 13:29:41 -0300 Subject: [PATCH 14/26] Update app-open-definitions.interface.ts comments in English --- src/app-open/app-open-definitions.interface.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app-open/app-open-definitions.interface.ts b/src/app-open/app-open-definitions.interface.ts index 4f2d1196..acb1a663 100644 --- a/src/app-open/app-open-definitions.interface.ts +++ b/src/app-open/app-open-definitions.interface.ts @@ -10,22 +10,22 @@ export type AppOpenDefinitionsHasAllEvents = ValidateAllEventsEnumAreImplemented export interface AppOpenAdPlugin { /** - * Carga un anuncio App Open + * Load an ad App Open */ loadAppOpen(options: AppOpenAdOptions): Promise; /** - * Muestra el anuncio App Open si está cargado + * Shows the App Open ad if loaded */ showAppOpen(): Promise; /** - * Verifica si el anuncio App Open está cargado + * Check if the App Open ad is loaded */ isAppOpenLoaded(): Promise<{ value: boolean }>; /** - * Agrega listeners para eventos de App Open + * Add listeners for App Open events */ addListener( eventName: AppOpenAdPluginEvents, From f81c2d6fe2e0737151eac7863562e86ddffc4261 Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 13:34:49 -0300 Subject: [PATCH 15/26] Update web.ts Replace any type with proper AppOpenAdOptions type for better type safety and API consistency. --- src/web.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/web.ts b/src/web.ts index e5c7d97a..834be081 100644 --- a/src/web.ts +++ b/src/web.ts @@ -13,12 +13,18 @@ import type { AdMobRewardItem } from './reward'; import type { AdOptions, AdLoadInfo } from './shared'; import type { TrackingAuthorizationStatusInterface } from './shared/tracking-authorization-status.interface'; +// ✅ New: Define a specific type for App Open Ad options +export interface AppOpenAdOptions { + adId: string; // The AdMob unit ID for the app open ad + orientation?: 'portrait' | 'landscape'; // Optional orientation + npa?: boolean; // Optional: non-personalized ads flag +} + export class AdMobWeb extends WebPlugin implements AdMobPlugin { async initialize(): Promise { console.log('initialize'); } - async requestTrackingAuthorization(): Promise { console.log('requestTrackingAuthorization'); } @@ -68,17 +74,14 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { console.log('showBanner', options); } - // Hide the banner, remove it from screen, but can show it later async hideBanner(): Promise { console.log('hideBanner'); } - // Resume the banner, show it after hide async resumeBanner(): Promise { console.log('resumeBanner'); } - // Destroy the banner, remove it from screen. async removeBanner(): Promise { console.log('removeBanner'); } @@ -95,7 +98,7 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { } async prepareRewardVideoAd(options: AdOptions): Promise { - console.log(options); + console.log('prepareRewardVideoAd', options); return { adUnitId: options.adId, }; @@ -109,7 +112,7 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { } async prepareRewardInterstitialAd(options: AdOptions): Promise { - console.log(options); + console.log('prepareRewardInterstitialAd', options); return { adUnitId: options.adId, }; @@ -122,8 +125,8 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { }; } - // Métodos stub para App Open Ad - async loadAppOpen(options: any): Promise { + // ✅ Replaced "any" with AppOpenAdOptions type + async loadAppOpen(options: AppOpenAdOptions): Promise { console.log('loadAppOpen', options); } From a0eae03a708a280d2a7d78a219bf88ba382af159 Mon Sep 17 00:00:00 2001 From: beto Date: Tue, 14 Oct 2025 17:15:09 -0300 Subject: [PATCH 16/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cb8310e..60e84425 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ import { } from '@capacitor-community/admob'; export async function showAppOpenAd(): Promise { - // Escuchar eventos + // listen to events AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { console.log('App Open Ad loaded'); }); From b82471b46c5a83a6df9680509c7621f666b67c24 Mon Sep 17 00:00:00 2001 From: beto Date: Sat, 25 Oct 2025 16:43:47 -0300 Subject: [PATCH 17/26] Refactor AppOpenAd Plugin initialization Moved AppOpenAd Plugin initialization inside the AdMob class. --- .../src/main/java/com/getcapacitor/community/admob/AdMob.java | 1 - 1 file changed, 1 deletion(-) 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 cb5a064c..1ade459e 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +++ b/android/src/main/java/com/getcapacitor/community/admob/AdMob.java @@ -70,7 +70,6 @@ public class AdMob extends Plugin { getLogTag() ); - // ✅ AppOpenAd Plugin block now inside the class private final AppOpenAdPlugin appOpenAdPlugin = new AppOpenAdPlugin(); @PluginMethod From f7423407c7ccf02d8daef36d5529e317c337c7b6 Mon Sep 17 00:00:00 2001 From: beto Date: Sat, 25 Oct 2025 16:45:00 -0300 Subject: [PATCH 18/26] Define AppOpenAdOptions interface and update loadAppOpen delete comment --- src/web.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/web.ts b/src/web.ts index 834be081..3b0f798f 100644 --- a/src/web.ts +++ b/src/web.ts @@ -13,7 +13,6 @@ import type { AdMobRewardItem } from './reward'; import type { AdOptions, AdLoadInfo } from './shared'; import type { TrackingAuthorizationStatusInterface } from './shared/tracking-authorization-status.interface'; -// ✅ New: Define a specific type for App Open Ad options export interface AppOpenAdOptions { adId: string; // The AdMob unit ID for the app open ad orientation?: 'portrait' | 'landscape'; // Optional orientation @@ -125,7 +124,6 @@ export class AdMobWeb extends WebPlugin implements AdMobPlugin { }; } - // ✅ Replaced "any" with AppOpenAdOptions type async loadAppOpen(options: AppOpenAdOptions): Promise { console.log('loadAppOpen', options); } From 3b9d047882ebbf79a1a6e025d97bf0a3431c4a13 Mon Sep 17 00:00:00 2001 From: beto Date: Mon, 27 Oct 2025 12:24:22 -0300 Subject: [PATCH 19/26] Update AppOpenAdManager.java test appOpen --- .../community/admob/models/AdOptionsTest.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/android/src/test/java/com/getcapacitor/community/admob/models/AdOptionsTest.java b/android/src/test/java/com/getcapacitor/community/admob/models/AdOptionsTest.java index 197ba426..e6858647 100644 --- a/android/src/test/java/com/getcapacitor/community/admob/models/AdOptionsTest.java +++ b/android/src/test/java/com/getcapacitor/community/admob/models/AdOptionsTest.java @@ -114,6 +114,84 @@ public void ssv() { final AdOptions adOptions = AdOptions.getFactory().createGenericOptions(pluginCallMock, ""); + verify(pluginCallMock, atLeastOnce()).getObject(wantedProperty); + assertEquals(userId, adOptions.ssvInfo.getUserId()); + assertEquals(customData, adOptions.ssvInfo.getCustomData()); + } + @Test + public void appOpen_ad_Id() { + final String expected = "Some Given AppOpen Test Id"; + when(pluginCallMock.getString(eq("adId"), anyString())).thenReturn(expected); + + final AdOptions adOptions = AdOptions.getFactory().createAppOpenOptions(pluginCallMock); + + assertEquals(expected, adOptions.adId); + } + + @Test + public void appOpen_position() { + final String wantedProperty = "position"; + final String expected = "TOP_CENTER"; + final String defaultValue = "BOTTOM_CENTER"; + when(pluginCallMock.getString(eq(wantedProperty), anyString())).thenReturn(expected); + + final AdOptions adOptions = AdOptions.getFactory().createAppOpenOptions(pluginCallMock); + + verify(pluginCallMock).getString(wantedProperty, defaultValue); + assertEquals(expected, adOptions.position); + } + + @Test + public void appOpen_margin() { + final String wantedProperty = "margin"; + final int expected = 10; + final int defaultValue = 0; + when(pluginCallMock.getInt(eq(wantedProperty), anyInt())).thenReturn(expected); + + final AdOptions adOptions = AdOptions.getFactory().createAppOpenOptions(pluginCallMock); + + verify(pluginCallMock).getInt(wantedProperty, defaultValue); + assertEquals(expected, adOptions.margin); + } + + @Test + public void appOpen_isTesting() { + final String wantedProperty = "isTesting"; + final boolean expected = true; + final boolean defaultValue = false; + when(pluginCallMock.getBoolean(eq(wantedProperty), anyBoolean())).thenReturn(expected); + + final AdOptions adOptions = AdOptions.getFactory().createAppOpenOptions(pluginCallMock); + + verify(pluginCallMock).getBoolean(wantedProperty, defaultValue); + assertEquals(expected, adOptions.isTesting); + } + + @Test + public void appOpen_npa() { + final String wantedProperty = "npa"; + final boolean expected = true; + final boolean defaultValue = false; + lenient().when(pluginCallMock.getBoolean(eq(wantedProperty), anyBoolean())).thenReturn(expected); + + final AdOptions adOptions = AdOptions.getFactory().createAppOpenOptions(pluginCallMock); + + verify(pluginCallMock).getBoolean(wantedProperty, defaultValue); + assertEquals(expected, adOptions.npa); + } + + @Test + public void appOpen_ssv() { + final String customData = "customData"; + final String userId = "userId"; + final String wantedProperty = "ssv"; + final JSObject expected = new JSObject(); + expected.put(customData, customData); + expected.put(userId, userId); + lenient().when(pluginCallMock.getObject(eq(wantedProperty))).thenReturn(expected); + + final AdOptions adOptions = AdOptions.getFactory().createAppOpenOptions(pluginCallMock); + verify(pluginCallMock, atLeastOnce()).getObject(wantedProperty); assertEquals(userId, adOptions.ssvInfo.getUserId()); assertEquals(customData, adOptions.ssvInfo.getCustomData()); From 5b3f914e61beb256b566a585a569326e75946c8b Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:04:32 -0300 Subject: [PATCH 20/26] Update android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../getcapacitor/community/admob/appopen/AppOpenAdPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java index e0ab7b4b..551aaf38 100644 --- a/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java +++ b/android/src/main/java/com/getcapacitor/community/admob/appopen/AppOpenAdPlugin.java @@ -5,12 +5,12 @@ import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; import com.getcapacitor.annotation.CapacitorPlugin; -import com.getcapacitor.community.admob.AdMobPlugin; +import com.getcapacitor.Plugin; import org.json.JSONException; import org.json.JSONObject; @CapacitorPlugin(name = "AppOpenAd") -public class AppOpenAdPlugin extends AdMobPlugin { +public class AppOpenAdPlugin extends Plugin { private AppOpenAdManager appOpenAdManager; @PluginMethod From ed1c654d1744e1b20cbb5ef56be3cc7367fb8bce Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:05:03 -0300 Subject: [PATCH 21/26] Update src/web.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/web.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/web.ts b/src/web.ts index 3b0f798f..4b59eaca 100644 --- a/src/web.ts +++ b/src/web.ts @@ -12,12 +12,7 @@ import { PrivacyOptionsRequirementStatus } from './consent/privacy-options-requi import type { AdMobRewardItem } from './reward'; import type { AdOptions, AdLoadInfo } from './shared'; import type { TrackingAuthorizationStatusInterface } from './shared/tracking-authorization-status.interface'; - -export interface AppOpenAdOptions { - adId: string; // The AdMob unit ID for the app open ad - orientation?: 'portrait' | 'landscape'; // Optional orientation - npa?: boolean; // Optional: non-personalized ads flag -} +import type { AppOpenAdOptions } from './app-open/app-open-ad-options.interface'; export class AdMobWeb extends WebPlugin implements AdMobPlugin { async initialize(): Promise { From 5fcb155bf70cff5652df3cbe33750003b73050db Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:05:21 -0300 Subject: [PATCH 22/26] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 60e84425..0bc861df 100644 --- a/README.md +++ b/README.md @@ -1143,7 +1143,7 @@ Muestra el anuncio App Open si está cargado isAppOpenLoaded() => Promise<{ value: boolean; }> ``` -Verifica si el anuncio App Open está cargado +Check if the App Open ad is loaded **Returns:** Promise<{ value: boolean; }> From 88caf873238e6c837704fa7df1b565a8a791b334 Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:05:31 -0300 Subject: [PATCH 23/26] Update demo/angular/src/app/app.component.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- demo/angular/src/app/app.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/angular/src/app/app.component.ts b/demo/angular/src/app/app.component.ts index af825661..260ede16 100644 --- a/demo/angular/src/app/app.component.ts +++ b/demo/angular/src/app/app.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { IonApp, IonRouterOutlet, Platform } from '@ionic/angular/standalone'; -import { AdMob, AppOpenAdPluginEvents, AppOpenAdOptions, AdMobInitializationOptions } from '@capacitor-community/admob'; +import { AdMob, AppOpenAdPluginEvents, AppOpenAdOptions } from '@capacitor-community/admob'; @Component({ selector: 'app-root', From ef80ce715ddaa5461e6ca68e0afef4e53a864bc0 Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:05:46 -0300 Subject: [PATCH 24/26] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bc861df..d713d8b2 100644 --- a/README.md +++ b/README.md @@ -1132,7 +1132,7 @@ Carga un anuncio App Open showAppOpen() => Promise ``` -Muestra el anuncio App Open si está cargado +Shows the App Open ad if loaded -------------------- From 9d8c31baee821ae647c730876250e99f11c4b8ed Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:05:59 -0300 Subject: [PATCH 25/26] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d713d8b2..94876434 100644 --- a/README.md +++ b/README.md @@ -1156,7 +1156,7 @@ Check if the App Open ad is loaded addListener(eventName: AppOpenAdPluginEvents, listenerFunc: (...args: any[]) => void) => Promise ``` -Agrega listeners para eventos de App Open +Add listeners for App Open events | Param | Type | | ------------------ | ----------------------------------------------------------------------- | From 7c8c2449969672b5523c6e58415e0c389e702f12 Mon Sep 17 00:00:00 2001 From: beto Date: Thu, 4 Dec 2025 10:06:07 -0300 Subject: [PATCH 26/26] Update demo/angular/src/app/app.component.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- demo/angular/src/app/app.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/angular/src/app/app.component.ts b/demo/angular/src/app/app.component.ts index 260ede16..df84bf0a 100644 --- a/demo/angular/src/app/app.component.ts +++ b/demo/angular/src/app/app.component.ts @@ -39,7 +39,7 @@ export class AppComponent { } async showAppOpenAd() { - // Escuchar eventos + // Listen to events AdMob.addListener(AppOpenAdPluginEvents.Loaded, () => { console.log('App Open Ad loaded'); });