Skip to content

Commit

Permalink
Update AdMob extension to handle Consent & Tracking Authorization on …
Browse files Browse the repository at this point in the history
…iOS (#7431)

* Admob will now initialize 2 seconds after the app loads, giving more control to when it starts. This is particularily helpful for App Store validations, with Tracking Authorization message.
* New conditions are available to know AdMob initialization status: "AdMob initializing" and "AdMob Initialized".
* New action to stop the auto-initialization: "Prevent AdMob auto initialization" and new action to trigger the initialization: "Initialize AdMob manually".
  * Typically, you'd prevent the initialization at the beginning of the scene, and trigger it manually when a user interacts with a button or something, so you can control when they'll see the consent messages.
  • Loading branch information
ClementPasteau authored Mar 3, 2025
1 parent 727f991 commit 828e6e0
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 10 deletions.
64 changes: 63 additions & 1 deletion Extensions/AdMob/JsExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = {
.setName('Consent Cordova plugin')
.setDependencyType('cordova')
.setExportName('cordova-plugin-consent')
.setVersion('3.0.0-alpha.8')
.setVersion('3.0.0-alpha.9')
.onlyIfOtherDependencyIsExported('AdMob Cordova plugin');

extension
Expand Down Expand Up @@ -93,6 +93,68 @@ module.exports = {
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.setTestMode');

extension
.addAction(
'PreventAdmobAutoInitialization',
_('Prevent AdMob auto initialization'),
_(
'Prevent AdMob from initializing automatically. You will need to call "Initialize AdMob" action manually.\n' +
'This is useful if you want to control when the consent dialog will be shown (for example, after the user has accepted your game terms).'
),
_('Prevent AdMob auto initialization'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.preventAdmobAutoInitialization');

extension
.addAction(
'InitializeAdmob',
_('Initialize AdMob manually'),
_(
'Initialize AdMob manually. This will trigger the consent dialog if needed, and then load the ads.\n' +
'Use this action if you have disabled the auto init and want to control when the consent dialog will be shown.'
),
_('Initialize AdMob'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.initializeAdmob');

extension
.addCondition(
'AdmobInitializing',
_('AdMob initializing'),
_('Check if AdMob is initializing.'),
_('AdMob is initializing'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAdmobInitializing');

extension
.addCondition(
'AdmobInitialized',
_('AdMob initialized'),
_('Check if AdMob has been initialized.'),
_('AdMob has been initialized'),
'',
'JsPlatform/Extensions/admobicon24.png',
'JsPlatform/Extensions/admobicon16.png'
)
.getCodeExtraInformation()
.setIncludeFile('Extensions/AdMob/admobtools.js')
.setFunctionName('gdjs.adMob.isAdmobInitialized');

// App Open
extension
.addCondition(
Expand Down
113 changes: 104 additions & 9 deletions Extensions/AdMob/admobtools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace gdjs {
declare var admob: any;
declare var cordova: any;
declare var consent: any;

export namespace adMob {
const logger = new gdjs.Logger('AdMob');
Expand Down Expand Up @@ -108,26 +109,119 @@ namespace gdjs {
let rewardedVideoRewardReceived = false; // Becomes true when the video is closed and the reward is received.
let rewardedVideoErrored = false; // Becomes true when the video fails to load.

let npaValue = '0'; // TODO: expose an API to change this and also an automatic way using the consent SDK.
let npaValue = '0'; // 0 means that the user has consented to personalized ads, 1 means that the user has not consented to personalized ads.

// Admob initialization listener
document.addEventListener(
'deviceready',
async () => {
// Obtain user consent ?
let setupTimeoutId: NodeJS.Timeout | null = null;

const askForConsentAndInitializeAdmob = async () => {
if (admobStarted) {
logger.warn('AdMob is already started.');
return;
}

if (isStarting) {
logger.warn('AdMob is already starting.');
return;
}

try {
logger.info('Starting AdMob.');
isStarting = true;

await admob.start();
if (cordova.platformId === 'ios') {
try {
/*
trackingStatus:
0 = notDetermined
1 = restricted
2 = denied
3 = authorized
*/
let trackingStatus = await consent.trackingAuthorizationStatus();

// If tracking is not determined, we ask the user for tracking authorization.
if (trackingStatus === 0) {
trackingStatus = await consent.requestTrackingAuthorization();
}

// If tracking is restricted or denied, we set npaValue to 1.
if (trackingStatus === 1 || trackingStatus === 2) {
npaValue = '1';
}

// otherwise, we set npaValue to 0.
npaValue = '0';
} catch (error) {
logger.error(
'Error while asking for tracking authorization, continuing:',
error
);
}
}

logger.info('AdMob successfully started.');
try {
// ConsentStatus:
// Unknown = 0,
// Required = 1,
// NotRequired = 2,
// Obtained = 3,
const consentStatus = await consent.getConsentStatus();
if (consentStatus === consent.ConsentStatus.Required) {
await consent.requestInfoUpdate();
}
await consent.loadAndShowIfRequired();
} catch (error) {
logger.error('Error while asking for consent, continuing:', error);
}

// We should be looking at canRequestAds to know if we can request ads or not.
// But as we want to be able to test ads in debug or if the consent didn't work,
// we ignore this value for now.
// const canRequestAds = await consent.canRequestAds();
if (true) {
await admob.start();

logger.info('AdMob successfully started.');
isStarting = false;
admobStarted = true;
}
} catch (error) {
logger.error('Error while starting AdMob:', error);
isStarting = false;
admobStarted = true;
admobStarted = false;
}
};

// Admob initialization listener
document.addEventListener(
'deviceready',
async () => {
isStarting = true;
setupTimeoutId = setTimeout(async () => {
isStarting = false; // Reset to false, as it will be set to true in askForConsentAndInitializeAdmob.
await askForConsentAndInitializeAdmob();
// Wait a bit before starting admob, to avoid the consent appearing too soon.
}, 2000);
},
false
);

export const preventAdmobAutoInitialization = () => {
if (setupTimeoutId) {
isStarting = false;
clearTimeout(setupTimeoutId);
setupTimeoutId = null;
}
};

export const initializeAdmob = async () => {
preventAdmobAutoInitialization();
await askForConsentAndInitializeAdmob();
};

export const isAdmobInitialized = () => admobStarted;
export const isAdmobInitializing = () => isStarting;

/**
* Helper to know if we are on mobile and admob is correctly initialized.
*/
Expand Down Expand Up @@ -334,6 +428,7 @@ namespace gdjs {
position: atTop ? 'top' : 'bottom',
size: bannerRequestedAdSizeType,
offset: 0,
npa: npaValue,
});

banner.on('load', () => {
Expand Down

0 comments on commit 828e6e0

Please sign in to comment.