From 839fe6fe7ac49e25157c7bbc62b765d995147e96 Mon Sep 17 00:00:00 2001 From: Justin Malandruccolo Date: Tue, 29 Jul 2025 13:09:41 -0700 Subject: [PATCH] Added context for how to use the AppOpenAdManager PiperOrigin-RevId: 788575189 --- .../GoogleMobileAdsConsentManager.java | 2 +- .../example/appopendemo/MyApplication.java | 56 ++++---- .../example/appopendemo/SplashActivity.java | 8 +- .../GoogleMobileAdsConsentManager.java | 2 +- .../example/appopenexample/MyApplication.java | 56 ++++---- .../appopenexample/SplashActivity.java | 8 +- java/advanced/APIDemo/app/build.gradle | 1 + .../gms/snippets/AppOpenAdSnippets.java | 131 ++++++++++++++++++ .../example/appopenexample/MyApplication.kt | 47 ++++--- .../example/appopenexample/MyApplication.kt | 47 ++++--- kotlin/advanced/APIDemo/app/build.gradle | 1 + .../android/gms/snippets/AppOpenAdSnippets.kt | 126 +++++++++++++++++ 12 files changed, 383 insertions(+), 102 deletions(-) create mode 100644 java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.java create mode 100644 kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.kt diff --git a/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/GoogleMobileAdsConsentManager.java b/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/GoogleMobileAdsConsentManager.java index 945ea4238..a1a38b2dd 100644 --- a/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/GoogleMobileAdsConsentManager.java +++ b/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/GoogleMobileAdsConsentManager.java @@ -59,7 +59,7 @@ public void gatherConsent( ConsentDebugSettings debugSettings = new ConsentDebugSettings.Builder(activity) // .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA) - .addTestDeviceHashedId(MyApplication.TEST_DEVICE_HASHED_ID) + .addTestDeviceHashedId(SplashActivity.TEST_DEVICE_HASHED_ID) .build(); ConsentRequestParameters params = diff --git a/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/MyApplication.java b/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/MyApplication.java index 06bd376a8..b876a8022 100644 --- a/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/MyApplication.java +++ b/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/MyApplication.java @@ -36,19 +36,12 @@ import java.util.Date; /** Application class that initializes, loads and show ads when activities change states. */ +// [START application_class] public class MyApplication extends Application implements ActivityLifecycleCallbacks, DefaultLifecycleObserver { - // Check your logcat output for the test device hashed ID e.g. - // "Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("ABCDEF012345")) - // to get test ads on this device" or - // "Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("ABCDEF012345") to set this as - // a debug device". - public static final String TEST_DEVICE_HASHED_ID = "ABCDEF012345"; - private AppOpenAdManager appOpenAdManager; private Activity currentActivity; - private static final String TAG = "MyApplication"; @Override public void onCreate() { @@ -59,14 +52,20 @@ public void onCreate() { appOpenAdManager = new AppOpenAdManager(); } + // [END application_class] + /** LifecycleObserver method that shows the app open ad when the app moves to foreground. */ + // [START lifecycle_observer_events] @Override public void onStart(@NonNull LifecycleOwner owner) { DefaultLifecycleObserver.super.onStart(owner); appOpenAdManager.showAdIfAvailable(currentActivity); } + // [END lifecycle_observer_events] + /** ActivityLifecycleCallback methods. */ + // [START activity_lifecycle_callbacks] @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} @@ -96,6 +95,8 @@ public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bun @Override public void onActivityDestroyed(@NonNull Activity activity) {} + // [END activity_lifecycle_callbacks] + /** * Load an app open ad. * @@ -129,6 +130,7 @@ public interface OnShowAdCompleteListener { } /** Inner class that loads and shows app open ads. */ + // [START manager_class] private class AppOpenAdManager { private static final String LOG_TAG = "AppOpenAdManager"; @@ -146,6 +148,8 @@ private class AppOpenAdManager { /** Constructor. */ public AppOpenAdManager() {} + // [END manager_class] + /** * Load an ad. * @@ -156,43 +160,37 @@ private void loadAd(Context context) { if (isLoadingAd || isAdAvailable()) { return; } - isLoadingAd = true; - AdManagerAdRequest request = new AdManagerAdRequest.Builder().build(); + // [START load_ad] AppOpenAd.load( context, AD_UNIT_ID, - request, + new AdManagerAdRequest.Builder().build(), new AppOpenAdLoadCallback() { - /** - * Called when an app open ad has loaded. - * - * @param ad the loaded app open ad. - */ @Override public void onAdLoaded(AppOpenAd ad) { + // Called when an app open ad has loaded. + Log.d(LOG_TAG, "App open ad loaded."); + Toast.makeText(context, "Ad was loaded.", Toast.LENGTH_SHORT).show(); + appOpenAd = ad; isLoadingAd = false; loadTime = (new Date()).getTime(); - - Log.d(LOG_TAG, "onAdLoaded."); - Toast.makeText(context, "onAdLoaded", Toast.LENGTH_SHORT).show(); } - /** - * Called when an app open ad has failed to load. - * - * @param loadAdError the error. - */ @Override public void onAdFailedToLoad(LoadAdError loadAdError) { + // Called when an app open ad has failed to load. + Log.d(LOG_TAG, "App open ad failed to load with error: " + loadAdError.getMessage()); + Toast.makeText(context, "Ad failed to load.", Toast.LENGTH_SHORT).show(); + isLoadingAd = false; - Log.d(LOG_TAG, "onAdFailedToLoad: " + loadAdError.getMessage()); - Toast.makeText(context, "onAdFailedToLoad", Toast.LENGTH_SHORT).show(); } }); + // [END load_ad] } + // [START ad_expiration] /** Check if ad was loaded more than n hours ago. */ private boolean wasLoadTimeLessThanNHoursAgo(long numHours) { long dateDifference = (new Date()).getTime() - loadTime; @@ -202,12 +200,12 @@ private boolean wasLoadTimeLessThanNHoursAgo(long numHours) { /** Check if ad exists and can be shown. */ private boolean isAdAvailable() { - // Ad references in the app open beta will time out after four hours, but this time limit - // may change in future beta versions. For details, see: - // https://support.google.com/admob/answer/9341964?hl=en + // For time interval details, see: https://support.google.com/admob/answer/9341964 return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4); } + // [END ad_expiration] + /** * Show the ad if one isn't already showing. * diff --git a/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/SplashActivity.java b/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/SplashActivity.java index d9ad37193..c7181987c 100644 --- a/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/SplashActivity.java +++ b/java/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopendemo/SplashActivity.java @@ -33,6 +33,12 @@ /** Splash Activity that inflates splash activity xml. */ public class SplashActivity extends AppCompatActivity { + // Check your logcat output for the test device hashed ID e.g. + // "Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("ABCDEF012345")) + // to get test ads on this device" or + // "Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("ABCDEF012345") to set this as + // a debug device". + public static final String TEST_DEVICE_HASHED_ID = "ABCDEF012345"; private static final String LOG_TAG = "SplashActivity"; private final AtomicBoolean isMobileAdsInitializeCalled = new AtomicBoolean(false); private final AtomicBoolean gatherConsentFinished = new AtomicBoolean(false); @@ -129,7 +135,7 @@ private void initializeMobileAdsSdk() { // Set your test devices. MobileAds.setRequestConfiguration( new RequestConfiguration.Builder() - .setTestDeviceIds(Arrays.asList(MyApplication.TEST_DEVICE_HASHED_ID)) + .setTestDeviceIds(Arrays.asList(TEST_DEVICE_HASHED_ID)) .build()); new Thread( diff --git a/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/GoogleMobileAdsConsentManager.java b/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/GoogleMobileAdsConsentManager.java index 7188750e3..0e941fffc 100644 --- a/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/GoogleMobileAdsConsentManager.java +++ b/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/GoogleMobileAdsConsentManager.java @@ -59,7 +59,7 @@ public void gatherConsent( ConsentDebugSettings debugSettings = new ConsentDebugSettings.Builder(activity) // .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA) - .addTestDeviceHashedId(MyApplication.TEST_DEVICE_HASHED_ID) + .addTestDeviceHashedId(SplashActivity.TEST_DEVICE_HASHED_ID) .build(); ConsentRequestParameters params = diff --git a/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.java b/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.java index 2dbfc929f..88404b1a9 100644 --- a/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.java +++ b/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.java @@ -36,19 +36,12 @@ import java.util.Date; /** Application class that initializes, loads and show ads when activities change states. */ +// [START application_class] public class MyApplication extends Application implements ActivityLifecycleCallbacks, DefaultLifecycleObserver { - // Check your logcat output for the test device hashed ID e.g. - // "Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("ABCDEF012345")) - // to get test ads on this device" or - // "Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("ABCDEF012345") to set this as - // a debug device". - public static final String TEST_DEVICE_HASHED_ID = "ABCDEF012345"; - private AppOpenAdManager appOpenAdManager; private Activity currentActivity; - private static final String TAG = "MyApplication"; @Override public void onCreate() { @@ -59,9 +52,12 @@ public void onCreate() { appOpenAdManager = new AppOpenAdManager(); } + // [END application_class] + /** * DefaultLifecycleObserver method that shows the app open ad when the app moves to foreground. */ + // [START lifecycle_observer_events] @Override public void onStart(@NonNull LifecycleOwner owner) { DefaultLifecycleObserver.super.onStart(owner); @@ -69,7 +65,10 @@ public void onStart(@NonNull LifecycleOwner owner) { appOpenAdManager.showAdIfAvailable(currentActivity); } + // [END lifecycle_observer_events] + /** ActivityLifecycleCallback methods. */ + // [START activity_lifecycle_callbacks] @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} @@ -99,6 +98,8 @@ public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bun @Override public void onActivityDestroyed(@NonNull Activity activity) {} + // [END activity_lifecycle_callbacks] + /** * Load an app open ad. * @@ -132,6 +133,7 @@ public interface OnShowAdCompleteListener { } /** Inner class that loads and shows app open ads. */ + // [START manager_class] private class AppOpenAdManager { private static final String LOG_TAG = "AppOpenAdManager"; @@ -149,6 +151,8 @@ private class AppOpenAdManager { /** Constructor. */ public AppOpenAdManager() {} + // [END manager_class] + /** * Load an ad. * @@ -159,43 +163,37 @@ private void loadAd(Context context) { if (isLoadingAd || isAdAvailable()) { return; } - isLoadingAd = true; - AdRequest request = new AdRequest.Builder().build(); + // [START load_ad] AppOpenAd.load( context, AD_UNIT_ID, - request, + new AdRequest.Builder().build(), new AppOpenAdLoadCallback() { - /** - * Called when an app open ad has loaded. - * - * @param ad the loaded app open ad. - */ @Override public void onAdLoaded(AppOpenAd ad) { + // Called when an app open ad has loaded. + Log.d(LOG_TAG, "App open ad loaded."); + Toast.makeText(context, "Ad was loaded.", Toast.LENGTH_SHORT).show(); + appOpenAd = ad; isLoadingAd = false; loadTime = (new Date()).getTime(); - - Log.d(LOG_TAG, "onAdLoaded."); - Toast.makeText(context, "onAdLoaded", Toast.LENGTH_SHORT).show(); } - /** - * Called when an app open ad has failed to load. - * - * @param loadAdError the error. - */ @Override public void onAdFailedToLoad(LoadAdError loadAdError) { + // Called when an app open ad has failed to load. + Log.d(LOG_TAG, "App open ad failed to load with error: " + loadAdError.getMessage()); + Toast.makeText(context, "Ad failed to load.", Toast.LENGTH_SHORT).show(); + isLoadingAd = false; - Log.d(LOG_TAG, "onAdFailedToLoad: " + loadAdError.getMessage()); - Toast.makeText(context, "onAdFailedToLoad", Toast.LENGTH_SHORT).show(); } }); + // [END load_ad] } + // [START ad_expiration] /** Check if ad was loaded more than n hours ago. */ private boolean wasLoadTimeLessThanNHoursAgo(long numHours) { long dateDifference = (new Date()).getTime() - loadTime; @@ -205,12 +203,12 @@ private boolean wasLoadTimeLessThanNHoursAgo(long numHours) { /** Check if ad exists and can be shown. */ private boolean isAdAvailable() { - // Ad references in the app open beta will time out after four hours, but this time limit - // may change in future beta versions. For details, see: - // https://support.google.com/admob/answer/9341964?hl=en + // For time interval details, see: https://support.google.com/admob/answer/9341964 return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4); } + // [END ad_expiration] + /** * Show the ad if one isn't already showing. * diff --git a/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/SplashActivity.java b/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/SplashActivity.java index 0c4ed0696..f37a5e8c4 100644 --- a/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/SplashActivity.java +++ b/java/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/SplashActivity.java @@ -33,6 +33,12 @@ /** Splash Activity that inflates splash activity xml. */ public class SplashActivity extends AppCompatActivity { + // Check your logcat output for the test device hashed ID e.g. + // "Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("ABCDEF012345")) + // to get test ads on this device" or + // "Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("ABCDEF012345") to set this as + // a debug device". + public static final String TEST_DEVICE_HASHED_ID = "ABCDEF012345"; private static final String LOG_TAG = "SplashActivity"; private final AtomicBoolean isMobileAdsInitializeCalled = new AtomicBoolean(false); private final AtomicBoolean gatherConsentFinished = new AtomicBoolean(false); @@ -129,7 +135,7 @@ private void initializeMobileAdsSdk() { // Set your test devices. MobileAds.setRequestConfiguration( new RequestConfiguration.Builder() - .setTestDeviceIds(Arrays.asList(MyApplication.TEST_DEVICE_HASHED_ID)) + .setTestDeviceIds(Arrays.asList(TEST_DEVICE_HASHED_ID)) .build()); new Thread( diff --git a/java/advanced/APIDemo/app/build.gradle b/java/advanced/APIDemo/app/build.gradle index cf37ec2ef..dc6dfae3e 100644 --- a/java/advanced/APIDemo/app/build.gradle +++ b/java/advanced/APIDemo/app/build.gradle @@ -28,4 +28,5 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.gms:play-services-ads:24.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-process:2.9.2' } diff --git a/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.java b/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.java new file mode 100644 index 000000000..da552ac02 --- /dev/null +++ b/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.java @@ -0,0 +1,131 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.android.gms.snippets; + +import android.app.Activity; +import android.util.Log; +import androidx.annotation.NonNull; +import com.google.android.gms.ads.AdError; +import com.google.android.gms.ads.FullScreenContentCallback; +import com.google.android.gms.ads.appopen.AppOpenAd; + +/** Java code snippets for the developer guide. */ +public class AppOpenAdSnippets { + private static final String TAG = "AppOpenAdSnippets"; + private AppOpenAd appOpenAd = null; + private boolean isShowingAd = false; + + /** Interface definition for a callback to be invoked when an app open ad is complete. */ + public interface OnShowAdCompleteListener { + void onShowAdComplete(); + } + + /** + * Shows the ad if available. + * + * @param activity the activity that shows the ad + */ + public void showAdIfAvailable(@NonNull final Activity activity) { + showAdIfAvailable( + activity, + () -> { + // Empty because the user will go back to the activity that shows the ad. + }); + } + + /** + * Shows the ad if available. + * + * @param activity The activity that shows the ad + * @param onShowAdCompleteListener The listener to be notified when an app open ad is complete + */ + // [START show_ad] + public void showAdIfAvailable( + @NonNull final Activity activity, + @NonNull OnShowAdCompleteListener onShowAdCompleteListener) { + // If the app open ad is already showing, do not show the ad again. + if (isShowingAd) { + Log.d(TAG, "The app open ad is already showing."); + return; + } + + // If the app open ad is not available yet, invoke the callback then load the ad. + if (appOpenAd == null) { + Log.d(TAG, "The app open ad is not ready yet."); + onShowAdCompleteListener.onShowAdComplete(); + // Load an ad. + return; + } + // [START_EXCLUDE silent] + setFullScreenContentCallback(activity, onShowAdCompleteListener); + // [END_EXCLUDE] + + isShowingAd = true; + appOpenAd.show(activity); + } + + // [END show_ad] + + private void setFullScreenContentCallback( + Activity activity, @NonNull OnShowAdCompleteListener onShowAdCompleteListener) { + // [START ad_events] + appOpenAd.setFullScreenContentCallback( + new FullScreenContentCallback() { + @Override + public void onAdDismissedFullScreenContent() { + // Called when full screen content is dismissed. + Log.d(TAG, "Ad dismissed fullscreen content."); + // Don't forget to set the ad reference to null so you + // don't show the ad a second time. + appOpenAd = null; + isShowingAd = false; + + onShowAdCompleteListener.onShowAdComplete(); + // Load an ad. + } + + @Override + public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { + // Called when full screen content failed to show. + Log.d(TAG, adError.getMessage()); + appOpenAd = null; + // Don't forget to set the ad reference to null so you + // don't show the ad a second time. + isShowingAd = false; + + onShowAdCompleteListener.onShowAdComplete(); + // Load an ad. + } + + @Override + public void onAdShowedFullScreenContent() { + Log.d(TAG, "Ad showed fullscreen content."); + } + + @Override + public void onAdImpression() { + // Called when an impression is recorded for an ad. + Log.d(TAG, "The ad recorded an impression."); + } + + @Override + public void onAdClicked() { + // Called when ad is clicked. + Log.d(TAG, "The ad was clicked."); + } + }); + // [END ad_events] + } +} diff --git a/kotlin/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt b/kotlin/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt index 1a2bf6ac3..5c56f0b62 100644 --- a/kotlin/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt +++ b/kotlin/admanager/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt @@ -19,6 +19,7 @@ import com.google.android.gms.ads.appopen.AppOpenAd.AppOpenAdLoadCallback import java.util.Date /** Application class that initializes, loads and show ads when activities change states. */ +// [START application_class] class MyApplication : MultiDexApplication(), Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver { @@ -33,9 +34,12 @@ class MyApplication : appOpenAdManager = AppOpenAdManager() } + // [END application_class] + /** * DefaultLifecycleObserver method that shows the app open ad when the app moves to foreground. */ + // [START lifecycle_observer_events] override fun onStart(owner: LifecycleOwner) { super.onStart(owner) currentActivity?.let { @@ -44,7 +48,10 @@ class MyApplication : } } + // [END lifecycle_observer_events] + /** ActivityLifecycleCallback methods. */ + // [START activity_lifecycle_callbacks] override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityStarted(activity: Activity) { @@ -67,6 +74,8 @@ class MyApplication : override fun onActivityDestroyed(activity: Activity) {} + // [END activity_lifecycle_callbacks] + /** * Shows an app open ad. * @@ -99,6 +108,7 @@ class MyApplication : } /** Inner class that loads and shows app open ads. */ + // [START manager_class] private inner class AppOpenAdManager { private val googleMobileAdsConsentManager = @@ -110,6 +120,8 @@ class MyApplication : /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */ private var loadTime: Long = 0 + // [END manager_class] + /** * Load an ad. * @@ -120,41 +132,36 @@ class MyApplication : if (isLoadingAd || isAdAvailable()) { return } - isLoadingAd = true - val request = AdManagerAdRequest.Builder().build() + // [START load_ad] AppOpenAd.load( context, AD_UNIT_ID, - request, + AdManagerAdRequest.Builder().build(), object : AppOpenAdLoadCallback() { - /** - * Called when an app open ad has loaded. - * - * @param ad the loaded app open ad. - */ override fun onAdLoaded(ad: AppOpenAd) { + // Called when an app open ad has loaded. + Log.d(LOG_TAG, "App open ad loaded.") + Toast.makeText(context, "Ad was loaded.", Toast.LENGTH_SHORT).show() + appOpenAd = ad isLoadingAd = false loadTime = Date().time - Log.d(LOG_TAG, "onAdLoaded.") - Toast.makeText(context, "onAdLoaded", Toast.LENGTH_SHORT).show() } - /** - * Called when an app open ad has failed to load. - * - * @param loadAdError the error. - */ override fun onAdFailedToLoad(loadAdError: LoadAdError) { + // Called when an app open ad has failed to load. + Log.d(LOG_TAG, "App open ad failed to load with error: " + loadAdError.message) + Toast.makeText(context, "Ad failed to load.", Toast.LENGTH_SHORT).show() + isLoadingAd = false - Log.d(LOG_TAG, "onAdFailedToLoad: " + loadAdError.message) - Toast.makeText(context, "onAdFailedToLoad", Toast.LENGTH_SHORT).show() } }, ) + // [END load_ad] } + // [START ad_expiration] /** Check if ad was loaded more than n hours ago. */ private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean { val dateDifference: Long = Date().time - loadTime @@ -164,12 +171,12 @@ class MyApplication : /** Check if ad exists and can be shown. */ private fun isAdAvailable(): Boolean { - // Ad references in the app open beta will time out after four hours, but this time limit - // may change in future beta versions. For details, see: - // https://support.google.com/admob/answer/9341964?hl=en + // For time interval details, see: https://support.google.com/admob/answer/9341964 return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4) } + // [END ad_expiration] + /** * Show the ad if one isn't already showing. * diff --git a/kotlin/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt b/kotlin/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt index 0e54d7dbc..c94d151a3 100644 --- a/kotlin/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt +++ b/kotlin/admob/AppOpenExample/app/src/main/java/com/google/android/gms/example/appopenexample/MyApplication.kt @@ -19,6 +19,7 @@ import com.google.android.gms.ads.appopen.AppOpenAd.AppOpenAdLoadCallback import java.util.Date /** Application class that initializes, loads and show ads when activities change states. */ +// [START application_class] class MyApplication : MultiDexApplication(), Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver { @@ -33,9 +34,12 @@ class MyApplication : appOpenAdManager = AppOpenAdManager() } + // [END application_class] + /** * DefaultLifecycleObserver method that shows the app open ad when the app moves to foreground. */ + // [START lifecycle_observer_events] override fun onStart(owner: LifecycleOwner) { super.onStart(owner) currentActivity?.let { @@ -44,7 +48,10 @@ class MyApplication : } } + // [END lifecycle_observer_events] + /** ActivityLifecycleCallback methods. */ + // [START activity_lifecycle_callbacks] override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityStarted(activity: Activity) { @@ -67,6 +74,8 @@ class MyApplication : override fun onActivityDestroyed(activity: Activity) {} + // [END activity_lifecycle_callbacks] + /** * Shows an app open ad. * @@ -99,6 +108,7 @@ class MyApplication : } /** Inner class that loads and shows app open ads. */ + // [START manager_class] private inner class AppOpenAdManager { private var googleMobileAdsConsentManager: GoogleMobileAdsConsentManager = @@ -110,6 +120,8 @@ class MyApplication : /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */ private var loadTime: Long = 0 + // [END manager_class] + /** * Load an ad. * @@ -120,41 +132,36 @@ class MyApplication : if (isLoadingAd || isAdAvailable()) { return } - isLoadingAd = true - val request = AdRequest.Builder().build() + // [START load_ad] AppOpenAd.load( context, AD_UNIT_ID, - request, + AdRequest.Builder().build(), object : AppOpenAdLoadCallback() { - /** - * Called when an app open ad has loaded. - * - * @param ad the loaded app open ad. - */ override fun onAdLoaded(ad: AppOpenAd) { + // Called when an app open ad has loaded. + Log.d(LOG_TAG, "App open ad loaded.") + Toast.makeText(context, "Ad was loaded.", Toast.LENGTH_SHORT).show() + appOpenAd = ad isLoadingAd = false loadTime = Date().time - Log.d(LOG_TAG, "onAdLoaded.") - Toast.makeText(context, "onAdLoaded", Toast.LENGTH_SHORT).show() } - /** - * Called when an app open ad has failed to load. - * - * @param loadAdError the error. - */ override fun onAdFailedToLoad(loadAdError: LoadAdError) { + // Called when an app open ad has failed to load. + Log.d(LOG_TAG, "App open ad failed to load with error: " + loadAdError.message) + Toast.makeText(context, "Ad failed to load.", Toast.LENGTH_SHORT).show() + isLoadingAd = false - Log.d(LOG_TAG, "onAdFailedToLoad: " + loadAdError.message) - Toast.makeText(context, "onAdFailedToLoad", Toast.LENGTH_SHORT).show() } }, ) + // [END load_ad] } + // [START ad_expiration] /** Check if ad was loaded more than n hours ago. */ private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean { val dateDifference: Long = Date().time - loadTime @@ -164,12 +171,12 @@ class MyApplication : /** Check if ad exists and can be shown. */ private fun isAdAvailable(): Boolean { - // Ad references in the app open beta will time out after four hours, but this time limit - // may change in future beta versions. For details, see: - // https://support.google.com/admob/answer/9341964?hl=en + // For time interval details, see: https://support.google.com/admob/answer/9341964 return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4) } + // [END ad_expiration] + /** * Show the ad if one isn't already showing. * diff --git a/kotlin/advanced/APIDemo/app/build.gradle b/kotlin/advanced/APIDemo/app/build.gradle index a2101606a..47c9d1c55 100644 --- a/kotlin/advanced/APIDemo/app/build.gradle +++ b/kotlin/advanced/APIDemo/app/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.9.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.1' + implementation 'androidx.lifecycle:lifecycle-process:2.9.2' // Google Play Services implementation 'com.google.android.gms:play-services-ads:24.5.0' diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.kt new file mode 100644 index 000000000..3cc68634f --- /dev/null +++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/AppOpenAdSnippets.kt @@ -0,0 +1,126 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.android.gms.snippets + +import android.app.Activity +import android.util.Log +import com.google.android.gms.ads.AdError +import com.google.android.gms.ads.FullScreenContentCallback +import com.google.android.gms.ads.appopen.AppOpenAd + +/** Kotlin code snippets for the developer guide. */ +class AppOpenAdSnippets { + private var appOpenAd: AppOpenAd? = null + private var isShowingAd = false + + /** Interface definition for a callback to be invoked when an app open ad is complete. */ + fun interface OnShowAdCompleteListener { + fun onShowAdComplete() + } + + /** + * Shows the ad if available. + * + * @param activity the activity that shows the ad + */ + fun showAdIfAvailable(activity: Activity) { + showAdIfAvailable(activity) { + // Empty because the user will go back to the activity that shows the ad. + } + } + + /** + * Shows the ad if available. + * + * @param activity The activity that shows the ad + * @param onShowAdCompleteListener The listener to be notified when an app open ad is complete + */ + // [START show_ad] + fun showAdIfAvailable(activity: Activity, onShowAdCompleteListener: OnShowAdCompleteListener) { + // If the app open ad is already showing, do not show the ad again. + if (isShowingAd) { + Log.d(TAG, "The app open ad is already showing.") + return + } + + // If the app open ad is not available yet, invoke the callback then load the ad. + if (appOpenAd == null) { + Log.d(TAG, "The app open ad is not ready yet.") + onShowAdCompleteListener.onShowAdComplete() + // Load an ad. + return + } + // [START_EXCLUDE silent] + setFullScreenContentCallback(activity, onShowAdCompleteListener) + // [END_EXCLUDE] + + isShowingAd = true + appOpenAd?.show(activity) + } + + // [END show_ad] + + private fun setFullScreenContentCallback( + activity: Activity, + onShowAdCompleteListener: OnShowAdCompleteListener, + ) { + // [START ad_events] + appOpenAd?.fullScreenContentCallback = + object : FullScreenContentCallback() { + override fun onAdDismissedFullScreenContent() { + // Called when full screen content is dismissed. + Log.d(TAG, "Ad dismissed fullscreen content.") + // Don't forget to set the ad reference to null so you + // don't show the ad a second time. + appOpenAd = null + isShowingAd = false + + onShowAdCompleteListener.onShowAdComplete() + // Load an ad. + } + + override fun onAdFailedToShowFullScreenContent(adError: AdError) { + // Called when full screen content failed to show. + Log.d(TAG, adError.message) + // Don't forget to set the ad reference to null so you + // don't show the ad a second time. + appOpenAd = null + isShowingAd = false + + onShowAdCompleteListener.onShowAdComplete() + // Load an ad. + } + + override fun onAdShowedFullScreenContent() { + Log.d(TAG, "Ad showed fullscreen content.") + } + + override fun onAdImpression() { + // Called when an impression is recorded for an ad. + Log.d(TAG, "The ad recorded an impression.") + } + + override fun onAdClicked() { + // Called when ad is clicked. + Log.d(TAG, "The ad was clicked.") + } + } + // [END ad_events] + } + + private companion object { + const val TAG = "AppOpenAdSnippets" + } +}