From d11bf5631c9bba8c1b20dd59e8502685feef4cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ritzl?= Date: Tue, 2 Jul 2024 14:46:02 +0200 Subject: [PATCH] Added support for app open ads (#44) * First version * Added iOS support * Updated documentation * Added new script apis * Final fixes * Update admob_ios.mm * Update admob_android.cpp * Update ext.manifest * Show app open on first load on iOS --- docs/index.md | 41 ++- extension-admob/api/admob.script_api | 51 ++++ extension-admob/ext.manifest | 3 - .../manifests/android/build.gradle | 3 + extension-admob/src/admob.cpp | 36 ++- extension-admob/src/admob_android.cpp | 49 +++- extension-admob/src/admob_callback_private.h | 3 +- extension-admob/src/admob_ios.mm | 146 ++++++++- extension-admob/src/admob_private.h | 9 +- .../src/java/com/defold/admob/AdmobJNI.java | 178 ++++++++++- game.project | 2 + main/ads.gui | 277 ++++++++++++++---- main/ads.gui_script | 129 ++++---- 13 files changed, 759 insertions(+), 168 deletions(-) diff --git a/docs/index.md b/docs/index.md index 72b229d..6055db3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,6 +28,8 @@ The extension can be configured by adding the following fields to `game.project` [admob] app_id_ios = ca-app-pub-3940256099942544~1458002511 app_id_android = ca-app-pub-3940256099942544~3347511713 +app_open_android = ca-app-pub-3940256099942544/9257395921 +app_open_ios = ca-app-pub-3940256099942544/5575463023 ios_tracking_usage_description = Your data will be used to provide you a better and personalized ad experience. ``` @@ -37,6 +39,12 @@ This is your iOS AdMob app ID. An app ID is a unique ID number assigned to your ### app_id_android This is your Android AdMob app ID. An app ID is a unique ID number assigned to your apps when they're added to AdMob. The app ID is used to identify your apps. +### app_open_android +Ad unit to use for App Open ads on Android. If this value is set App Open Ads will be shown when the app is brought to the foreground. + +### app_open_ios +Ad unit to use for App Open ads on iOS. If this value is set App Open Ads will be shown when the app is brought to the foreground. + ### ios_tracking_usage_description Before requesting the unique IDFA string for a device on iOS 14 the application must request user authorization to access app-related data for tracking the user or the device. This is done automatically when `admob.request_idfa()` is called. The string set in `admob.ios_tracking_usage_description` will be shown to the user. @@ -120,16 +128,26 @@ local function admob_callback(self, message_id, message) end ``` +### Ad formats + +The extension supports the following ad formats: + +* App Open Ads - App open ads are a special ad format intended for publishers wishing to monetize their app load screens. App open ads can be closed by your users at any time. App open ads can be shown when users bring your app to the foreground. +* Banner Ads - Banner ads are rectangular ads that occupy a portion of an app's layout. They stay on screen while users are interacting with the app. +* Interstitial Ads - Interstitial ads are full-screen ads that cover the interface of an app until closed by the user. +* Rewarded Ads - Rewarded ads are ads that users have the option of interacting with in exchange for in-app rewards. +* Rewarded Interstitial Ads - Rewarded interstitial is a type of incentivized ad format that allows you offer rewards for ads that appear automatically during natural app transitions. Unlike rewarded ads, users aren't required to opt-in to view a rewarded interstitial. + ### Loading ads Before an ad unit can be displayed it has to be loaded: ```lua +admob.load_banner(ad_unit, size) admob.load_interstitial(ad_unit) admob.load_rewarded(ad_unit) admob.load_rewarded_interstitial(ad_unit) -admob.load_banner(ad_unit, size) ``` The callback function will be invoked when the ad unit is ready: @@ -150,6 +168,8 @@ local function admob_callback(self, message_id, message) -- same as above elseif message_id == admob.MSG_BANNER then -- same as above + elseif message_id == admob.MSG_APPOPEN then + -- same as above end ``` @@ -162,15 +182,14 @@ admob.is_rewarded_loaded() admob.is_rewarded_interstitial_loaded() ``` - ### Showing ads Once an ad unit has been loaded it is ready to be shown: ```lua +admob.show_banner(position) admob.show_interstitial() admob.show_rewarded() admob.show_rewarded_interstitial() -admob.show_banner(position) ``` The callback function will be invoked when the ad unit is shown: @@ -197,10 +216,24 @@ local function admob_callback(self, message_id, message) -- same as above elseif message_id == admob.MSG_BANNER then -- same as above + elseif message_id == admob.MSG_APPOPEN then + -- same as above end ``` -Banner ads can be hidden and destroyed: +### App Open Ads + +App Open Ads will automatically be loaded and shown if an ad unit id is provided in the `game.project` configuration (see above). If you wish to manually load and show App Open Ads you can leave the `game.project` configuration blank and instead use the following functions: + +```lua +admob.load_appopen(ad_unit) +admob.show_appopen() +admob.is_appopen_loaded() +``` + +### Banner ads + +In addition to loading and showing Banner Ads they can also be hidden and destroyed: ```lua admob.hide_banner() diff --git a/extension-admob/api/admob.script_api b/extension-admob/api/admob.script_api index f8c3ad6..c734f5e 100644 --- a/extension-admob/api/admob.script_api +++ b/extension-admob/api/admob.script_api @@ -193,6 +193,54 @@ [Android](https://developers.google.com/admob/android/ad-inspector), [iOS](https://developers.google.com/admob/ios/ad-inspector) +#***************************************************************************************************** + + - name: load_appopen + type: function + desc: Starts loading an AppOpen Ad, can only be called after `admob.MSG_INITIALIZATION` event + Original docs + [Android](https://developers.google.com/admob/android/app-open), + [iOS](https://developers.google.com/admob/ios/app-open) + + parameters: + - name: ad_unit_id + type: string + desc: Ad unit ID, for test ads use on Android `"ca-app-pub-3940256099942544/9257395921"`, or + on iOS `"ca-app-pub-3940256099942544/5575463023"` + Original docs + [Android](https://developers.google.com/admob/android/app-open), + [iOS](https://developers.google.com/admob/ios/app-open) + +#***************************************************************************************************** + + - name: show_appopen + type: function + desc: Shows loaded AppOpen Ad, can only be called after `admob.EVENT_LOADED` + Original docs + [Android](https://developers.google.com/admob/android/app-open), + [iOS](https://developers.google.com/admob/ios/app-open) + + examples: + - desc: |- + ```lua + if admob and admob.is_appopen_loaded() then + admob.show_appopen() + end + ``` + +#***************************************************************************************************** + + - name: is_appopen_loaded + type: function + desc: Checks if AppOpen Ad is loaded and ready to show + Original docs + [Android](https://developers.google.com/admob/android/app-open), + [iOS](https://developers.google.com/admob/ios/app-open) + + returns: + - name: is_ready + type: boolean + #***************************************************************************************************** - name: load_interstitial @@ -479,6 +527,9 @@ - name: MSG_REWARDED_INTERSTITIAL type: number + - name: MSG_APPOPEN + type: number + #***************************************************************************************************** - name: EVENT_CLOSED diff --git a/extension-admob/ext.manifest b/extension-admob/ext.manifest index 5aced13..edeb6e5 100644 --- a/extension-admob/ext.manifest +++ b/extension-admob/ext.manifest @@ -1,9 +1,6 @@ name: "AdMobExt" platforms: - android: - context: - aaptExtraPackages: ["androidx.work", "com.google.android.gms.common", "androidx.startup"] ios: context: diff --git a/extension-admob/manifests/android/build.gradle b/extension-admob/manifests/android/build.gradle index ffbdf7d..2f874ee 100644 --- a/extension-admob/manifests/android/build.gradle +++ b/extension-admob/manifests/android/build.gradle @@ -5,4 +5,7 @@ repositories { dependencies { // https://developers.google.com/admob/android/quick-start#example_app-level_buildgradle_excerpt implementation 'com.google.android.gms:play-services-ads:22.6.0' + implementation "androidx.lifecycle:lifecycle-common:2.3.1" + implementation "androidx.lifecycle:lifecycle-process:2.3.1" + annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.3.1" } diff --git a/extension-admob/src/admob.cpp b/extension-admob/src/admob.cpp index 2d100d7..6b3f5f4 100644 --- a/extension-admob/src/admob.cpp +++ b/extension-admob/src/admob.cpp @@ -18,7 +18,7 @@ static const char DEFOLD_USERAGENT[] = "defold-3.4.1"; static int Lua_Initialize(lua_State* L) { DM_LUA_STACK_CHECK(L, 0); - Initialize(DEFOLD_USERAGENT); + Initialize(); return 0; } @@ -29,6 +29,24 @@ static int Lua_SetCallback(lua_State* L) return 0; } +static int Lua_LoadAppOpen(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + if (lua_type(L, 1) != LUA_TSTRING) { + return DM_LUA_ERROR("Expected string, got %s. Wrong type for App Open Ad UnitId variable '%s'.", luaL_typename(L, 1), lua_tostring(L, 1)); + } + const char* unitId_lua = luaL_checkstring(L, 1); + LoadAppOpen(unitId_lua, false); + return 0; +} + +static int Lua_ShowAppOpen(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + ShowAppOpen(); + return 0; +} + static int Lua_LoadInterstitial(lua_State* L) { DM_LUA_STACK_CHECK(L, 0); @@ -155,6 +173,14 @@ static int Lua_IsBannerLoaded(lua_State* L) return 1; } +static int Lua_IsAppOpenLoaded(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 1); + bool is_loaded = IsAppOpenLoaded(); + lua_pushboolean(L, is_loaded); + return 1; +} + static int Lua_SetPrivacySettings(lua_State* L) { DM_LUA_STACK_CHECK(L, 0); @@ -189,6 +215,8 @@ static const luaL_reg Module_methods[] = { {"initialize", Lua_Initialize}, {"set_callback", Lua_SetCallback}, + {"load_appopen", Lua_LoadAppOpen}, + {"show_appopen", Lua_ShowAppOpen}, {"load_interstitial", Lua_LoadInterstitial}, {"show_interstitial", Lua_ShowInterstitial}, {"load_rewarded", Lua_LoadRewarded}, @@ -203,6 +231,7 @@ static const luaL_reg Module_methods[] = {"is_interstitial_loaded", Lua_IsInterstitialLoaded}, {"is_rewarded_interstitial_loaded", Lua_IsRewardedInterstitialLoaded}, {"is_banner_loaded", Lua_IsBannerLoaded}, + {"is_appopen_loaded", Lua_IsAppOpenLoaded}, {"set_privacy_settings", Lua_SetPrivacySettings}, {"request_idfa", Lua_RequestIDFA}, {"show_ad_inspector", Lua_ShowAdInspector}, @@ -225,6 +254,7 @@ static void LuaInit(lua_State* L) SETCONSTANT(MSG_INITIALIZATION) SETCONSTANT(MSG_IDFA) SETCONSTANT(MSG_REWARDED_INTERSTITIAL) + SETCONSTANT(MSG_APPOPEN) SETCONSTANT(EVENT_CLOSED) SETCONSTANT(EVENT_FAILED_TO_SHOW) @@ -280,13 +310,15 @@ static void LuaInit(lua_State* L) static dmExtension::Result AppInitializeAdmob(dmExtension::AppParams* params) { + dmLogInfo("AppInitializeAdmob"); return dmExtension::RESULT_OK; } static dmExtension::Result InitializeAdmob(dmExtension::Params* params) { + dmLogInfo("InitializeAdmob"); LuaInit(params->m_L); - Initialize_Ext(); + Initialize_Ext(params, DEFOLD_USERAGENT); InitializeCallback(); return dmExtension::RESULT_OK; } diff --git a/extension-admob/src/admob_android.cpp b/extension-admob/src/admob_android.cpp index 63eaddb..ffaa390 100644 --- a/extension-admob/src/admob_android.cpp +++ b/extension-admob/src/admob_android.cpp @@ -19,6 +19,8 @@ struct Admob jobject m_AdmobJNI; jmethodID m_Initialize; + jmethodID m_LoadAppOpen; + jmethodID m_ShowAppOpen; jmethodID m_LoadInterstitial; jmethodID m_ShowInterstitial; jmethodID m_LoadRewarded; @@ -29,6 +31,7 @@ struct Admob jmethodID m_DestroyBanner; jmethodID m_ShowBanner; jmethodID m_HideBanner; + jmethodID m_IsAppOpenLoaded; jmethodID m_IsRewardedLoaded; jmethodID m_IsInterstitialLoaded; jmethodID m_IsRewardedInterstitialLoaded; @@ -80,6 +83,16 @@ static void CallVoidMethodCharInt(jobject instance, jmethodID method, const char env->DeleteLocalRef(jstr); } +static void CallVoidMethodCharBoolean(jobject instance, jmethodID method, const char* cstr, bool cbool) +{ + dmAndroid::ThreadAttacher threadAttacher; + JNIEnv* env = threadAttacher.GetEnv(); + + jstring jstr = env->NewStringUTF(cstr); + env->CallVoidMethod(instance, method, jstr, cbool); + env->DeleteLocalRef(jstr); +} + static void CallVoidMethodInt(jobject instance, jmethodID method, int cint) { dmAndroid::ThreadAttacher threadAttacher; @@ -98,7 +111,9 @@ static void CallVoidMethodBool(jobject instance, jmethodID method, bool cbool) static void InitJNIMethods(JNIEnv* env, jclass cls) { - g_admob.m_Initialize = env->GetMethodID(cls, "initialize", "(Ljava/lang/String;)V"); + g_admob.m_Initialize = env->GetMethodID(cls, "initialize", "()V"); + g_admob.m_LoadAppOpen = env->GetMethodID(cls, "loadAppOpen", "(Ljava/lang/String;)V"); + g_admob.m_ShowAppOpen = env->GetMethodID(cls, "showAppOpen", "()V"); g_admob.m_LoadInterstitial = env->GetMethodID(cls, "loadInterstitial", "(Ljava/lang/String;)V"); g_admob.m_ShowInterstitial = env->GetMethodID(cls, "showInterstitial", "()V"); g_admob.m_LoadRewarded = env->GetMethodID(cls, "loadRewarded", "(Ljava/lang/String;)V"); @@ -114,6 +129,7 @@ static void InitJNIMethods(JNIEnv* env, jclass cls) g_admob.m_ShowAdInspector = env->GetMethodID(cls, "showAdInspector", "()V"); g_admob.m_UpdateBannerLayout= env->GetMethodID(cls, "updateBannerLayout", "()V"); + g_admob.m_IsAppOpenLoaded = env->GetMethodID(cls, "isAppOpenLoaded", "()Z"); g_admob.m_IsRewardedLoaded = env->GetMethodID(cls, "isRewardedLoaded", "()Z"); g_admob.m_IsInterstitialLoaded = env->GetMethodID(cls, "isInterstitialLoaded", "()Z"); g_admob.m_IsRewardedInterstitialLoaded = env->GetMethodID(cls, "isRewardedInterstitialLoaded", "()Z"); @@ -121,7 +137,7 @@ static void InitJNIMethods(JNIEnv* env, jclass cls) g_admob.m_SetMaxAdContentRating = env->GetMethodID(cls, "setMaxAdContentRating", "(I)V"); } -void Initialize_Ext() +void Initialize_Ext(dmExtension::Params* params, const char* defoldUserAgent) { dmAndroid::ThreadAttacher threadAttacher; JNIEnv* env = threadAttacher.GetEnv(); @@ -129,18 +145,37 @@ void Initialize_Ext() InitJNIMethods(env, cls); - jmethodID jni_constructor = env->GetMethodID(cls, "", "(Landroid/app/Activity;)V"); - - g_admob.m_AdmobJNI = env->NewGlobalRef(env->NewObject(cls, jni_constructor, threadAttacher.GetActivity()->clazz)); + const char* appOpenAdUnitId = dmConfigFile::GetString(params->m_ConfigFile, "admob.app_open_android", 0); + jstring jappOpenAdUnitId = env->NewStringUTF(appOpenAdUnitId); + jstring jdefoldUserAgent = env->NewStringUTF(defoldUserAgent); + jmethodID jni_constructor = env->GetMethodID(cls, "", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V"); + g_admob.m_AdmobJNI = env->NewGlobalRef(env->NewObject(cls, jni_constructor, threadAttacher.GetActivity()->clazz, jappOpenAdUnitId, jdefoldUserAgent)); + env->DeleteLocalRef(jappOpenAdUnitId); + env->DeleteLocalRef(jdefoldUserAgent); } void Finalize_Ext() { } -void Initialize(const char* defoldUserAgent) +void Initialize() +{ + CallVoidMethod(g_admob.m_AdmobJNI, g_admob.m_Initialize); +} + +void LoadAppOpen(const char* unitId, bool showImmediately) +{ + CallVoidMethodCharBoolean(g_admob.m_AdmobJNI, g_admob.m_LoadAppOpen, unitId, showImmediately); +} + +void ShowAppOpen() +{ + CallVoidMethod(g_admob.m_AdmobJNI, g_admob.m_ShowAppOpen); +} + +bool IsAppOpenLoaded() { - CallVoidMethodChar(g_admob.m_AdmobJNI, g_admob.m_Initialize, defoldUserAgent); + return CallBoolMethod(g_admob.m_AdmobJNI, g_admob.m_IsAppOpenLoaded); } void LoadInterstitial(const char* unitId) diff --git a/extension-admob/src/admob_callback_private.h b/extension-admob/src/admob_callback_private.h index d4c4fdd..2a767b6 100755 --- a/extension-admob/src/admob_callback_private.h +++ b/extension-admob/src/admob_callback_private.h @@ -16,7 +16,8 @@ enum MessageId MSG_BANNER = 3, MSG_INITIALIZATION = 4, MSG_IDFA = 5, - MSG_REWARDED_INTERSTITIAL = 6 + MSG_REWARDED_INTERSTITIAL = 6, + MSG_APPOPEN = 7 }; enum MessageEvent diff --git a/extension-admob/src/admob_ios.mm b/extension-admob/src/admob_ios.mm index 1bf10c8..7954586 100755 --- a/extension-admob/src/admob_ios.mm +++ b/extension-admob/src/admob_ios.mm @@ -10,6 +10,8 @@ #import #endif +@interface AdmobExtAppOpenAdDelegate : NSObject +@end @interface AdmobExtInterstitialAdDelegate : NSObject @end @@ -91,8 +93,7 @@ void SendSimpleMessage(MessageId msg, MessageEvent event, NSString *key_2, int v SendSimpleMessage(msg, dict); } - void Initialize(const char* defoldUserAgent) { - m_DefoldUserAgent = defoldUserAgent; + void Initialize() { [[GADMobileAds sharedInstance] startWithCompletionHandler:^(GADInitializationStatus *_Nonnull status) { SendSimpleMessage(MSG_INITIALIZATION, EVENT_COMPLETE); @@ -105,6 +106,98 @@ void Initialize(const char* defoldUserAgent) { return request; } + +//-------------------------------------------------- +// App Open ADS + + static AdmobExtAppOpenAdDelegate *admobExtAppOpenAdDelegate; + static GADAppOpenAd *appOpenAd; + static char *appOpenAdId; + static bool isLoadingAppOpenAd; + static bool isShowingAppOpenAd; + + void SetAppOpenAd(GADAppOpenAd *newAd) { + if (appOpenAd == newAd) { + return; + } + if (newAd != nil) { + [newAd retain]; + } + if (appOpenAd != nil) { + [appOpenAd release]; + } + appOpenAd = newAd; + } + + void LoadAppOpen(const char* unitId, bool showImmediately) { + // Do not load ad if one is already loading. + if (isLoadingAppOpenAd) { + NSLog(@"Already loading app open ad."); + return; + } + + // Do not load ad if there is an unused ad. + if (IsAppOpenLoaded()) { + SendSimpleMessage(MSG_APPOPEN, EVENT_LOADED); + if (showImmediately) { + ShowAppOpen(); + } + return; + } + + appOpenAdId = (char*)unitId; + isLoadingAppOpenAd = true; + [GADAppOpenAd + loadWithAdUnitID:[NSString stringWithUTF8String:unitId] + request:createGADRequest() + completionHandler:^(GADAppOpenAd *_Nullable ad, NSError *_Nullable error) { + isLoadingAppOpenAd = false; + if (error) { + SetAppOpenAd(nil); + NSLog([NSString stringWithFormat:@"Error domain: \"%@\". %@", [error domain], [error localizedDescription]]); + SendSimpleMessage(MSG_APPOPEN, EVENT_FAILED_TO_LOAD, @"code", [error code], + @"error", [NSString stringWithFormat:@"Error domain: \"%@\". %@", [error domain], [error localizedDescription]]); + return; + } + SetAppOpenAd(ad); + SendSimpleMessage(MSG_APPOPEN, EVENT_LOADED); + if (showImmediately) { + ShowAppOpen(); + } + }]; + } + + bool IsAppOpenLoaded() { + return appOpenAd != nil; + } + + void ShowAppOpen() { + // If the app open ad is already showing, do not show the ad again. + if (isShowingAppOpenAd) { + NSLog(@"The app open ad is already showing."); + return; + } + + if (!IsAppOpenLoaded()) { + LoadAppOpen(appOpenAdId, false); + return; + } + isShowingAppOpenAd = true; + NSError* error; + appOpenAd.fullScreenContentDelegate = admobExtAppOpenAdDelegate; + if ([appOpenAd canPresentFromRootViewController:uiViewController error:&error]) { + [appOpenAd presentFromRootViewController:uiViewController]; + } else { + if (error) { + SendSimpleMessage(MSG_APPOPEN, EVENT_NOT_LOADED, @"code", [error code], + @"error", [NSString stringWithFormat:@"Error domain: \"%@\". %@", [error domain], [error localizedDescription]]); + } else { + SendSimpleMessage(MSG_APPOPEN, EVENT_NOT_LOADED, @"error", @"Can't present App Open Ad"); + } + } + } + + //-------------------------------------------------- // Interstitial ADS @@ -441,7 +534,9 @@ void DestroyBanner() { //-------------------------------------------------- -void Initialize_Ext() { +void Initialize_Ext(dmExtension::Params* params, const char* defoldUserAgent) { + m_DefoldUserAgent = defoldUserAgent; + UIWindow* window = dmGraphics::GetNativeiOSUIWindow(); uiViewController = window.rootViewController; @@ -449,9 +544,16 @@ void Initialize_Ext() { admobExtRewardedAdDelegate = [[AdmobExtRewardedAdDelegate alloc] init]; admobExtRewardedInterstitialAdDelegate = [[AdmobExtRewardedInterstitialAdDelegate alloc] init]; admobExtBannerAdDelegate = [[AdmobExtBannerAdDelegate alloc] init]; + admobExtAppOpenAdDelegate = [[AdmobExtAppOpenAdDelegate alloc] init]; admobAppDelegate = [[AdMobAppDelegate alloc] init]; + appOpenAdId = (char*)dmConfigFile::GetString(params->m_ConfigFile, "admob.app_open_ios", 0); + if (appOpenAdId) { + LoadAppOpen(appOpenAdId, true); + } + dmExtension::RegisteriOSUIApplicationDelegate(admobAppDelegate); + dmExtension::RegisteriOSUIApplicationDelegate(admobExtAppOpenAdDelegate); } void Finalize_Ext() { @@ -503,6 +605,7 @@ void ShowAdInspector() { } void ActivateApp() { + ShowAppOpen(); } void SetMaxAdContentRating(MaxAdRating max_ad_rating) { @@ -524,6 +627,37 @@ void SetMaxAdContentRating(MaxAdRating max_ad_rating) { } //namespace +@implementation AdmobExtAppOpenAdDelegate + +- (void)adWillPresentFullScreenContent:(nonnull id)ad { + dmAdmob::SendSimpleMessage(dmAdmob::MSG_APPOPEN, dmAdmob::EVENT_OPENING); +} + +- (void)adDidDismissFullScreenContent:(nonnull id)ad { + dmAdmob::isShowingAppOpenAd = false; + dmAdmob::SetAppOpenAd(nil); + dmAdmob::LoadAppOpen(dmAdmob::appOpenAdId, false); + dmAdmob::SendSimpleMessage(dmAdmob::MSG_APPOPEN, dmAdmob::EVENT_CLOSED); +} + +- (void)ad:(nonnull id)ad didFailToPresentFullScreenContentWithError:(nonnull NSError *)error { + dmAdmob::isShowingAppOpenAd = false; + dmAdmob::SetAppOpenAd(nil); + dmAdmob::LoadAppOpen(dmAdmob::appOpenAdId, false); + dmAdmob::SendSimpleMessage(dmAdmob::MSG_APPOPEN, dmAdmob::EVENT_FAILED_TO_SHOW, @"code", [error code], + @"error", [NSString stringWithFormat:@"Error domain: \"%@\". %@", [error domain], [error localizedDescription]]); +} + +- (void)adDidRecordImpression:(id)ad { + dmAdmob::SendSimpleMessage(dmAdmob::MSG_APPOPEN, dmAdmob::EVENT_IMPRESSION_RECORDED); +} + +- (void)adDidRecordClick:(id)ad { + dmAdmob::SendSimpleMessage(dmAdmob::MSG_APPOPEN, dmAdmob::EVENT_CLICKED); +} + +@end + @implementation AdmobExtInterstitialAdDelegate - (void)adDidDismissFullScreenContent:(id)ad { @@ -662,6 +796,12 @@ - (void)orientationDidChange:(NSNotification *)notification { [self performSelector:@selector(UpdatePosition) withObject:nil afterDelay:0.1]; } +- (void) applicationDidBecomeActive:(UIApplication *)application { + NSLog(@"applicationDidBecomeActive."); + //dmAdmob::ShowAppOpen(); + dmAdmob::LoadAppOpen(dmAdmob::appOpenAdId, true); +} + - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; [super dealloc]; diff --git a/extension-admob/src/admob_private.h b/extension-admob/src/admob_private.h index f6fe464..68d15e3 100644 --- a/extension-admob/src/admob_private.h +++ b/extension-admob/src/admob_private.h @@ -2,6 +2,8 @@ #pragma once +#include + namespace dmAdmob { @@ -42,10 +44,12 @@ enum MaxAdRating MAX_AD_CONTENT_RATING_MA = 3 }; -void Initialize_Ext(); +void Initialize_Ext(dmExtension::Params* params, const char* defoldUserAgent); void Finalize_Ext(); -void Initialize(const char* defoldUserAgent); +void Initialize(); +void LoadAppOpen(const char* unitId, bool showImmediately); +void ShowAppOpen(); void LoadInterstitial(const char* unitId); void ShowInterstitial(); void LoadRewarded(const char* unitId); @@ -62,6 +66,7 @@ void ShowAdInspector(); void ActivateApp(); void SetMaxAdContentRating(MaxAdRating max_ad_rating); +bool IsAppOpenLoaded(); bool IsInterstitialLoaded(); bool IsRewardedLoaded(); bool IsRewardedInterstitialLoaded(); diff --git a/extension-admob/src/java/com/defold/admob/AdmobJNI.java b/extension-admob/src/java/com/defold/admob/AdmobJNI.java index 82f7536..f579273 100644 --- a/extension-admob/src/java/com/defold/admob/AdmobJNI.java +++ b/extension-admob/src/java/com/defold/admob/AdmobJNI.java @@ -14,8 +14,16 @@ import android.content.Context; import android.content.SharedPreferences; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.ProcessLifecycleOwner; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.lifecycle.Lifecycle.Event; + import com.google.android.gms.ads.AdSize; import com.google.android.gms.ads.AdView; +import com.google.android.gms.ads.appopen.AppOpenAd; +import com.google.android.gms.ads.appopen.AppOpenAd.AppOpenAdLoadCallback; +import com.google.android.gms.ads.FullScreenContentCallback; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.AdError; @@ -43,7 +51,7 @@ import org.json.JSONObject; import org.json.JSONException; -public class AdmobJNI { +public class AdmobJNI implements LifecycleObserver { private static final String TAG = "AdmobJNI"; @@ -57,6 +65,7 @@ public class AdmobJNI { private static final int MSG_INITIALIZATION = 4; private static final int MSG_IDFA = 5; private static final int MSG_REWARDED_INTERSTITIAL =6; + private static final int MSG_APPOPEN = 7; private static final int EVENT_CLOSED = 1; private static final int EVENT_FAILED_TO_SHOW = 2; @@ -102,15 +111,27 @@ public class AdmobJNI { private String defoldUserAgent = "defold-x.y.z"; - private Activity activity; - public AdmobJNI(Activity activity) { + + public AdmobJNI(Activity activity, String appOpenAdUnitId, String defoldUserAgent) { this.activity = activity; + this.mAppOpenAdUnitId = appOpenAdUnitId; + this.defoldUserAgent = defoldUserAgent; + + if (isAutomaticAppOpenEnabled()) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + initialize(); + ProcessLifecycleOwner.get().getLifecycle().addObserver(AdmobJNI.this); + loadAppOpen(appOpenAdUnitId, true); + } + }); + } } - public void initialize(String defoldUserAgent) { - this.defoldUserAgent = defoldUserAgent; + public void initialize() { MobileAds.initialize(activity, new OnInitializationCompleteListener() { @Override public void onInitializationComplete(InitializationStatus initializationStatus) { @@ -243,6 +264,153 @@ private AdRequest createAdRequest() { return new AdRequest.Builder().setRequestAgent(defoldUserAgent).build(); } +//-------------------------------------------------- +// App Open Ads + + private String mAppOpenAdUnitId = null; + private AppOpenAd mAppOpenAd = null; + private boolean mIsLoadingAppOpenAd = false; + private boolean mIsShowingAppOpenAd = false; + + @OnLifecycleEvent(Event.ON_START) + protected void onMoveToForeground() { + Log.d(TAG, "onMoveToForeground"); + showAppOpen(); + } + + private boolean isAutomaticAppOpenEnabled() { + return mAppOpenAdUnitId != null; + } + + public boolean isAppOpenLoaded() { + return mAppOpenAd != null; + } + + public void showAppOpen() { + // If the app open ad is already showing, do not show the ad again + if (mIsShowingAppOpenAd) { + Log.d(TAG, "The app open ad is already showing."); + return; + } + + // If the app open ad is not available yet then load it + if (!isAppOpenLoaded() && isAutomaticAppOpenEnabled()) { + Log.d(TAG, "The app open ad is not ready yet."); + loadAppOpen(mAppOpenAdUnitId, true); + return; + } + + Log.d(TAG, "Showing app open ad."); + mIsShowingAppOpenAd = true; + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + mAppOpenAd.setFullScreenContentCallback( + new FullScreenContentCallback() { + + @Override + public void onAdDismissedFullScreenContent() { + // Called when fullscreen content is dismissed. + // Clean up state and load a new ad + Log.d(TAG, "Ad dismissed fullscreen content."); + sendSimpleMessage(MSG_APPOPEN, EVENT_CLOSED); + mAppOpenAd = null; + mIsShowingAppOpenAd = false; + if (isAutomaticAppOpenEnabled()) { + loadAppOpen(mAppOpenAdUnitId, false); + } + } + + @Override + public void onAdFailedToShowFullScreenContent(AdError adError) { + // Called when fullscreen content failed to show. + // Clean up state and load a new ad + Log.d(TAG, adError.getMessage()); + sendSimpleMessage(MSG_APPOPEN, EVENT_FAILED_TO_SHOW, "code", adError.getCode(), + "error", String.format("Error domain: \"%s\". %s", adError.getDomain(), adError.getMessage())); + mAppOpenAd = null; + mIsShowingAppOpenAd = false; + if (isAutomaticAppOpenEnabled()) { + loadAppOpen(mAppOpenAdUnitId, false); + } + } + + @Override + public void onAdShowedFullScreenContent() { + // Called when fullscreen content is shown. + Log.d(TAG, "Ad showed fullscreen content."); + sendSimpleMessage(MSG_APPOPEN, EVENT_OPENING); + } + + @Override + public void onAdImpression() { + sendSimpleMessage(MSG_APPOPEN, EVENT_IMPRESSION_RECORDED); + } + + @Override + public void onAdClicked() { + sendSimpleMessage(MSG_APPOPEN, EVENT_CLICKED); + } + }); + mAppOpenAd.show(activity); + } + }); + } + + // Load an app open ad with the provided ad unit id, optionally also showing it + // immediately when it loaded + public void loadAppOpen(String adUnitId, boolean showImmediately) { + // Do not load ad if one is already loading. + if (mIsLoadingAppOpenAd) { + Log.d(TAG, "Already loading app open ad."); + return; + } + + // Do not load ad if there is an unused ad. + if (isAppOpenLoaded()) { + Log.d(TAG, "App open ad was already loaded."); + sendSimpleMessage(MSG_APPOPEN, EVENT_LOADED); + if (showImmediately) { + showAppOpen(); + } + return; + } + + Log.d(TAG, "Loading app open ad."); + mIsLoadingAppOpenAd = true; + AdRequest request = new AdRequest.Builder().build(); + AppOpenAd.load( + activity, adUnitId, request, + AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, + new AppOpenAdLoadCallback() { + @Override + public void onAdLoaded(AppOpenAd ad) { + // Called when an app open ad has loaded. + Log.d(TAG, "Ad was loaded."); + sendSimpleMessage(MSG_APPOPEN, EVENT_LOADED); + mAppOpenAd = ad; + mIsLoadingAppOpenAd = false; + if (showImmediately) { + showAppOpen(); + } + } + + @Override + public void onAdFailedToLoad(LoadAdError loadAdError) { + // Called when an app open ad has failed to load. + Log.d(TAG, loadAdError.getMessage()); + sendSimpleMessage(MSG_APPOPEN, EVENT_FAILED_TO_SHOW, "code", loadAdError.getCode(), + "error", String.format("Error domain: \"%s\". %s", loadAdError.getDomain(), loadAdError.getMessage())); + mIsLoadingAppOpenAd = false; + } + }); + } + + // Load an app open ad with the provided ad unit id but don't show it + public void loadAppOpen(String adUnitId) { + loadAppOpen(adUnitId, false); + } + //-------------------------------------------------- // Interstitial ADS diff --git a/game.project b/game.project index ddb2b04..a31ea2b 100644 --- a/game.project +++ b/game.project @@ -35,5 +35,7 @@ include_dirs = extension-admob [admob] app_id_ios = ca-app-pub-3940256099942544~1458002511 app_id_android = ca-app-pub-3940256099942544~3347511713 +app_open_android = ca-app-pub-3940256099942544/9257395921 +app_open_ios = ca-app-pub-3940256099942544/5575463023 ios_tracking_usage_description = Your data will be used to provide you a better and personalized ad experience. diff --git a/main/ads.gui b/main/ads.gui index 36e9ca9..76cca8f 100644 --- a/main/ads.gui +++ b/main/ads.gui @@ -70,7 +70,7 @@ nodes { nodes { position { x: 10.0 - y: 790.0 + y: 694.0 z: 0.0 w: 1.0 } @@ -88,7 +88,7 @@ nodes { } size { x: 700.0 - y: 780.0 + y: 700.0 z: 0.0 w: 1.0 } @@ -193,8 +193,8 @@ nodes { } nodes { position { - x: 385.0 - y: 853.092 + x: 545.0 + y: 1093.092 z: 0.0 w: 1.0 } @@ -235,8 +235,8 @@ nodes { } nodes { position { - x: 160.0 - y: 233.85786 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -285,7 +285,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -364,8 +363,8 @@ nodes { } nodes { position { - x: 725.0 - y: 1145.076 + x: 545.0 + y: 1221.076 z: 0.0 w: 1.0 } @@ -406,8 +405,8 @@ nodes { } nodes { position { - x: -180.0 - y: 66.3276 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -456,7 +455,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -535,8 +533,8 @@ nodes { } nodes { position { - x: 385.0 - y: 732.092 + x: 545.0 + y: 976.092 z: 0.0 w: 1.0 } @@ -577,8 +575,8 @@ nodes { } nodes { position { - x: 160.0 - y: 233.85786 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -627,7 +625,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -706,8 +703,8 @@ nodes { } nodes { position { - x: 385.0 - y: 617.092 + x: 545.0 + y: 855.092 z: 0.0 w: 1.0 } @@ -748,8 +745,8 @@ nodes { } nodes { position { - x: 160.0 - y: 233.85786 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -798,7 +795,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -878,7 +874,7 @@ nodes { nodes { position { x: 57.883 - y: 951.0 + y: 1216.0 z: 0.0 w: 1.0 } @@ -920,7 +916,7 @@ nodes { nodes { position { x: 0.0 - y: 268.4665 + y: 0.0 z: 0.0 w: 1.0 } @@ -969,7 +965,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -980,7 +975,7 @@ nodes { nodes { position { x: 46.0 - y: 269.51428 + y: 3.0 z: 0.0 w: 1.0 } @@ -1036,7 +1031,6 @@ nodes { alpha: 1.0 outline_alpha: 1.0 shadow_alpha: 1.0 - overridden_fields: 1 overridden_fields: 8 template_node_child: true text_leading: 1.0 @@ -1049,7 +1043,7 @@ nodes { nodes { position { x: 57.883 - y: 861.0 + y: 1126.0 z: 0.0 w: 1.0 } @@ -1091,7 +1085,7 @@ nodes { nodes { position { x: 0.0 - y: 268.4665 + y: 0.0 z: 0.0 w: 1.0 } @@ -1140,7 +1134,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -1151,7 +1144,7 @@ nodes { nodes { position { x: 46.0 - y: 269.51428 + y: 3.0 z: 0.0 w: 1.0 } @@ -1207,7 +1200,6 @@ nodes { alpha: 1.0 outline_alpha: 1.0 shadow_alpha: 1.0 - overridden_fields: 1 overridden_fields: 8 template_node_child: true text_leading: 1.0 @@ -1220,7 +1212,7 @@ nodes { nodes { position { x: 57.883 - y: 771.0 + y: 1040.0 z: 0.0 w: 1.0 } @@ -1262,7 +1254,7 @@ nodes { nodes { position { x: 0.0 - y: 268.4665 + y: 0.0 z: 0.0 w: 1.0 } @@ -1311,7 +1303,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -1322,7 +1313,7 @@ nodes { nodes { position { x: 46.0 - y: 269.51428 + y: 3.0 z: 0.0 w: 1.0 } @@ -1378,7 +1369,6 @@ nodes { alpha: 1.0 outline_alpha: 1.0 shadow_alpha: 1.0 - overridden_fields: 1 overridden_fields: 8 template_node_child: true text_leading: 1.0 @@ -1391,7 +1381,7 @@ nodes { nodes { position { x: 57.883 - y: 681.0 + y: 955.0 z: 0.0 w: 1.0 } @@ -1433,7 +1423,7 @@ nodes { nodes { position { x: 0.0 - y: 268.4665 + y: 0.0 z: 0.0 w: 1.0 } @@ -1482,7 +1472,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -1493,7 +1482,7 @@ nodes { nodes { position { x: 46.0 - y: 269.51428 + y: 3.0 z: 0.0 w: 1.0 } @@ -1550,7 +1539,6 @@ nodes { alpha: 1.0 outline_alpha: 1.0 shadow_alpha: 1.0 - overridden_fields: 1 overridden_fields: 8 template_node_child: true text_leading: 1.0 @@ -1562,8 +1550,8 @@ nodes { } nodes { position { - x: 46.0 - y: 617.0 + x: 197.0 + y: 771.0 z: 0.0 w: 1.0 } @@ -1604,8 +1592,8 @@ nodes { } nodes { position { - x: 160.0 - y: 233.85786 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -1654,7 +1642,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL @@ -1732,6 +1719,175 @@ nodes { visible: true material: "" } +nodes { + position { + x: 57.883 + y: 866.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "ad_type_appopen" + parent: "inited" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/dirtylarry/radio_label.gui" + template_node_child: false + custom_type: 0 + enabled: true +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 64.0 + y: 68.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "radio/radio_normal" + id: "ad_type_appopen/larryradio" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "ad_type_appopen" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" +} +nodes { + position { + x: 46.0 + y: 3.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "AppOpen" + font: "larryfont" + id: "ad_type_appopen/larrylabel" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "ad_type_appopen" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" +} nodes { position { x: 0.0 @@ -1792,8 +1948,8 @@ nodes { } nodes { position { - x: 548.0 - y: 803.076 + x: 376.0 + y: 886.076 z: 0.0 w: 1.0 } @@ -1834,8 +1990,8 @@ nodes { } nodes { position { - x: -180.0 - y: 66.3276 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -1884,7 +2040,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -1963,8 +2118,8 @@ nodes { } nodes { position { - x: 548.0 - y: 931.076 + x: 371.0 + y: 1000.076 z: 0.0 w: 1.0 } @@ -2005,8 +2160,8 @@ nodes { } nodes { position { - x: -180.0 - y: 66.3276 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -2055,7 +2210,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 @@ -2134,8 +2288,8 @@ nodes { } nodes { position { - x: 548.0 - y: 1046.076 + x: 373.0 + y: 1108.076 z: 0.0 w: 1.0 } @@ -2176,8 +2330,8 @@ nodes { } nodes { position { - x: -180.0 - y: 66.3276 + x: 0.0 + y: 0.0 z: 0.0 w: 1.0 } @@ -2226,7 +2380,6 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 - overridden_fields: 1 template_node_child: true size_mode: SIZE_MODE_MANUAL custom_type: 0 diff --git a/main/ads.gui_script b/main/ads.gui_script index e85f649..a16aa1f 100644 --- a/main/ads.gui_script +++ b/main/ads.gui_script @@ -42,6 +42,8 @@ local function update_ui(self) gui.set_enabled(gui.get_node("show_ad/larrybutton"), admob.is_rewarded_loaded()) elseif self.ad_type == admob.MSG_REWARDED_INTERSTITIAL then gui.set_enabled(gui.get_node("show_ad/larrybutton"), admob.is_rewarded_interstitial_loaded()) + elseif self.ad_type == admob.MSG_APPOPEN then + gui.set_enabled(gui.get_node("show_ad/larrybutton"), admob.is_appopen_loaded()) elseif self.ad_type == admob.MSG_BANNER then local is_banner_loaded = admob.is_banner_loaded() gui.set_enabled(gui.get_node("show_ad/larrybutton"), is_banner_loaded) @@ -67,6 +69,35 @@ local function set_block_height(height) gui.set_size(height_b_node, size) end +local EVENT_TEXTS = { + [admob.EVENT_CLOSED] = "EVENT_CLOSED: %s closed", + [admob.EVENT_FAILED_TO_SHOW] = "EVENT_FAILED_TO_SHOW: %s failed to show", + [admob.EVENT_OPENING] = "EVENT_OPENING: %s is opening", + [admob.EVENT_FAILED_TO_LOAD] = "EVENT_FAILED_TO_LOAD: %s failed to load", + [admob.EVENT_LOADED] = "EVENT_LOADED: %s loaded", + [admob.EVENT_NOT_LOADED] = "EVENT_NOT_LOADED: %s can't call show before EVENT_LOADED", + [admob.EVENT_EARNED_REWARD] = "EVENT_EARNED_REWARD: %s Reward", + [admob.EVENT_IMPRESSION_RECORDED] = "EVENT_IMPRESSION_RECORDED: %s did record impression", + [admob.EVENT_CLICKED] = "EVENT_CLICKED: %s clicked", + [admob.EVENT_JSON_ERROR] = "EVENT_JSON_ERROR: %s Internal NE json error", +} +local function print_event(ad_type, message) + local text = EVENT_TEXTS[message.event] + if text then + text = text:format(ad_type) + if message.code then + text = text .. "\nCode: " .. message.code + end + if message.error then + text = text .. "\nError: " .. message.error + end + if message.amount then + text = text .. "\nAmount: " .. message.amount .. " " .. message.type + end + print(text) + end +end + local function admob_callback(self, message_id, message) if message_id == admob.MSG_INITIALIZATION then self.initialized = true @@ -88,90 +119,19 @@ local function admob_callback(self, message_id, message) print("EVENT_NOT_SUPPORTED: IDFA request not supported on this platform or OS version") end elseif message_id == admob.MSG_INTERSTITIAL then - if message.event == admob.EVENT_CLOSED then - print("EVENT_CLOSED: Interstitial AD closed") - elseif message.event == admob.EVENT_FAILED_TO_SHOW then - print("EVENT_FAILED_TO_SHOW: Interstitial AD failed to show\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_OPENING then - -- on android this event fire only when ADS activity closed =( - print("EVENT_OPENING: Interstitial AD is opening") - elseif message.event == admob.EVENT_FAILED_TO_LOAD then - print("EVENT_FAILED_TO_LOAD: Interstitial AD failed to load\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_LOADED then - print("EVENT_LOADED: Interstitial AD loaded") - elseif message.event == admob.EVENT_NOT_LOADED then - print("EVENT_NOT_LOADED: can't call show_interstitial() before EVENT_LOADED\nError: "..message.error) - elseif message.event == admob.EVENT_IMPRESSION_RECORDED then - print("EVENT_IMPRESSION_RECORDED: Interstitial did record impression") - elseif message.event == admob.EVENT_CLICKED then - print("EVENT_CLICKED: Interstitial clicked") - elseif message.event == admob.EVENT_JSON_ERROR then - print("EVENT_JSON_ERROR: Internal NE json error: "..message.error) - end + print_event("Interstitial", message) elseif message_id == admob.MSG_REWARDED then - if message.event == admob.EVENT_CLOSED then - print("EVENT_CLOSED: Rewarded AD closed") - elseif message.event == admob.EVENT_FAILED_TO_SHOW then - print("EVENT_FAILED_TO_SHOW: Rewarded AD failed to show\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_OPENING then - -- on android this event fire only when ADS activity closed =( - print("EVENT_OPENING: Rewarded AD is opening") - elseif message.event == admob.EVENT_FAILED_TO_LOAD then - print("EVENT_FAILED_TO_LOAD: Rewarded AD failed to load\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_LOADED then - print("EVENT_LOADED: Rewarded AD loaded") - elseif message.event == admob.EVENT_NOT_LOADED then - print("EVENT_NOT_LOADED: can't call show_rewarded() before EVENT_LOADED\nError: "..message.error) - elseif message.event == admob.EVENT_EARNED_REWARD then - print("EVENT_EARNED_REWARD: Reward: " .. tostring(message.amount) .. " " .. tostring(message.type)) - elseif message.event == admob.EVENT_IMPRESSION_RECORDED then - print("EVENT_IMPRESSION_RECORDED: Rewarded did record impression") - elseif message.event == admob.EVENT_CLICKED then - print("EVENT_CLICKED: Rewarded clicked") - elseif message.event == admob.EVENT_JSON_ERROR then - print("EVENT_JSON_ERROR: Internal NE json error: "..message.error) - end + print_event("Rewarded", message) + elseif message_id == admob.MSG_APPOPEN then + print_event("App Open", message) elseif message_id == admob.MSG_REWARDED_INTERSTITIAL then - if message.event == admob.EVENT_CLOSED then - print("EVENT_CLOSED: Rewarded Interstitial AD closed") - elseif message.event == admob.EVENT_FAILED_TO_SHOW then - print("EVENT_FAILED_TO_SHOW: Rewarded Interstitial AD failed to show\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_OPENING then - -- on android this event fire only when ADS activity closed =( - print("EVENT_OPENING: Rewarded Interstitial AD is opening") - elseif message.event == admob.EVENT_FAILED_TO_LOAD then - print("EVENT_FAILED_TO_LOAD: Rewarded Interstitial AD failed to load\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_LOADED then - print("EVENT_LOADED: Rewarded Interstitial AD loaded") - elseif message.event == admob.EVENT_NOT_LOADED then - print("EVENT_NOT_LOADED: can't call show_rewarded_interstitial() before EVENT_LOADED\nError: "..message.error) - elseif message.event == admob.EVENT_EARNED_REWARD then - print("EVENT_EARNED_REWARD: Reward: " .. tostring(message.amount) .. " " .. tostring(message.type)) - elseif message.event == admob.EVENT_IMPRESSION_RECORDED then - print("EVENT_IMPRESSION_RECORDED: Rewarded Interstitial did record impression") - elseif message.event == admob.EVENT_CLICKED then - print("EVENT_CLICKED: Rewarded Interstitial clicked") - elseif message.event == admob.EVENT_JSON_ERROR then - print("EVENT_JSON_ERROR: Internal NE json error: "..message.error) - end + print_event("Rewarded Interstitial", message) elseif message_id == admob.MSG_BANNER then if message.event == admob.EVENT_LOADED then print("EVENT_LOADED: Banner AD loaded. Height: "..message.height.."px Width: "..message.width.."px") set_block_height(message.height) - elseif message.event == admob.EVENT_OPENING then - print("EVENT_OPENING: Banner AD is opening") - elseif message.event == admob.EVENT_FAILED_TO_LOAD then - print("EVENT_FAILED_TO_LOAD: Banner AD failed to load\nCode: "..message.code.."\nError: "..message.error) - elseif message.event == admob.EVENT_CLICKED then - print("EVENT_CLICKED: Banner AD clicked") - elseif message.event == admob.EVENT_CLOSED then - print("EVENT_CLOSED: Banner AD closed") - elseif message.event == admob.EVENT_DESTROYED then - print("EVENT_DESTROYED: Banner AD destroyed") - elseif message.event == admob.EVENT_IMPRESSION_RECORDED then - print("EVENT_IMPRESSION_RECORDED: Banner did record impression") - elseif message.event == admob.EVENT_JSON_ERROR then - print("EVENT_JSON_ERROR: Internal NE json error: "..message.error) + else + print_event("Banner", message) end end update_ui(self) @@ -187,12 +147,14 @@ function init(self) -- Always test ONLY with test ads units when you test integration! if sys.get_sys_info().system_name == 'iPhone OS' then -- https://developers.google.com/admob/ios/test-ads + self.appopen_ad_unit = sys.get_config_string("admob.app_open_ios", "ca-app-pub-3940256099942544/5575463023") self.banner_ad_unit = "ca-app-pub-3940256099942544/2934735716" -- test unit for banners self.interstitial_ad_unit = "ca-app-pub-3940256099942544/4411468910" -- test unit for interstitial self.rewardedvideo_ad_unit = "ca-app-pub-3940256099942544/1712485313" -- test unit for rewarded self.rewarded_interstitial_video_ad_unit = "ca-app-pub-3940256099942544/6978759866" -- test unit for rewarded interstitial else --Android -- From https://developers.google.com/admob/android/test-ads + self.appopen_ad_unit = sys.get_config_string("admob.app_open_android", "ca-app-pub-3940256099942544/9257395921") self.banner_ad_unit = "ca-app-pub-3940256099942544/6300978111" -- test unit for banners self.interstitial_ad_unit = "ca-app-pub-3940256099942544/1033173712" -- test unit for interstitial self.rewardedvideo_ad_unit = "ca-app-pub-3940256099942544/5224354917" -- test unit for rewarded @@ -203,10 +165,12 @@ function init(self) -- !!! Replace this keys with your own keys : -- https://developers.google.com/admob/android/quick-start#set_up_your_app_in_your_admob_account if sys.get_sys_info().system_name == 'iPhone OS' then + self.appopen_ad_unit = sys.get_config_string("admob.app_open_ios", "ca-app-pub-3940256099942544/5575463023") self.banner_ad_unit = "ca-app-pub-3940256099942544/2934735716" -- !!! replace it with iOS banner unit self.interstitial_ad_unit = "ca-app-pub-3940256099942544/4411468910" -- !!! replace it with iOS interstitial unit self.rewardedvideo_ad_unit = "ca-app-pub-3940256099942544/1712485313" -- !!! replace it with iOS rewarded unit else --Android + self.appopen_ad_unit = sys.get_config_string("admob.app_open_android", "ca-app-pub-3940256099942544/9257395921") self.banner_ad_unit = "ca-app-pub-3940256099942544/6300978111" -- !!! replace it with Android banner unit self.interstitial_ad_unit = "ca-app-pub-3940256099942544/1033173712" -- !!! replace it with Android interstitial unit self.rewardedvideo_ad_unit = "ca-app-pub-3940256099942544/5224354917" -- !!! replace it with Android rewarded unit @@ -272,7 +236,8 @@ function on_input(self, action_id, action) self.ad_type = dirtylarry:radio("ad_type_rewarded", action_id, action, admob.MSG_REWARDED, self.ad_type) self.ad_type = dirtylarry:radio("ad_type_banner", action_id, action, admob.MSG_BANNER, self.ad_type) self.ad_type = dirtylarry:radio("ad_type_rewarded_interstitial", action_id, action, admob.MSG_REWARDED_INTERSTITIAL, self.ad_type) - + self.ad_type = dirtylarry:radio("ad_type_appopen", action_id, action, admob.MSG_APPOPEN, self.ad_type) + if prev_type ~= self.ad_type then update_ui(self) end @@ -294,6 +259,9 @@ function on_input(self, action_id, action) elseif self.ad_type == admob.MSG_REWARDED_INTERSTITIAL then print("admob.load_rewarded_interstitial()") admob.load_rewarded_interstitial(self.rewarded_interstitial_video_ad_unit) + elseif self.ad_type == admob.MSG_APPOPEN then + print("admob.load_appopen()") + admob.load_appopen(self.appopen_ad_unit) end end) end @@ -323,6 +291,9 @@ function on_input(self, action_id, action) elseif self.ad_type == admob.MSG_REWARDED_INTERSTITIAL then print("admob.show_rewarded_interstitial()") admob.show_rewarded_interstitial() + elseif self.ad_type == admob.MSG_APPOPEN then + print("admob.show_appopen()") + admob.show_appopen() end end) end