diff --git a/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdLayoutSnippet.xml b/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdLayoutSnippet.xml new file mode 100644 index 000000000..b3125cb75 --- /dev/null +++ b/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdLayoutSnippet.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdLayoutSnippets.xml b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdLayoutSnippets.xml new file mode 100644 index 000000000..b3125cb75 --- /dev/null +++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdLayoutSnippets.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdSnippets.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdSnippets.kt index 0f3046d42..cd263eb86 100644 --- a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdSnippets.kt +++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/snippets/NativeAdSnippets.kt @@ -14,7 +14,11 @@ package com.google.android.gms.snippets +import android.app.Activity import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout import com.google.android.gms.ads.AdListener import com.google.android.gms.ads.AdLoader import com.google.android.gms.ads.AdRequest @@ -22,9 +26,6 @@ import com.google.android.gms.ads.LoadAdError import com.google.android.gms.ads.admanager.AdManagerAdRequest import com.google.android.gms.ads.nativead.NativeAd import com.google.android.gms.ads.nativead.NativeAdOptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch /** Kotlin code snippets for the developer guide. */ internal class NativeAdSnippets { @@ -97,6 +98,106 @@ internal class NativeAdSnippets { // [END handle_ad_loaded] } + private fun handleAdLoadedWithDisplay( + adLoaderBuilder: AdLoader.Builder, + activity: Activity, + layoutInflater: LayoutInflater, + layoutPlaceholder: FrameLayout, + ) { + // [START handle_ad_loaded_with_display] + adLoaderBuilder + .forNativeAd { nativeAd -> + // This callback is invoked when a native ad is successfully loaded. + activity.runOnUiThread { + val nativeAdBinding = NativeAdBinding.inflate(layoutInflater) + val adView = nativeAdBinding.root + + // Populate and register the native ad asset views. + displayNativeAdView(nativeAd, nativeAdBinding) + + // Remove all old ad views and add the new native ad + // view to the view hierarchy. + layoutPlaceholder.removeAllViews() + layoutPlaceholder.addView(adView) + } + } + .build() + // [END handle_ad_loaded_with_display] + } + + private fun displayNativeAdView(nativeAd: NativeAd, nativeAdBinding: NativeAdBinding) { + // [START display_native_ad] + val nativeAdView = nativeAdBinding.root + + // Set the media view. + nativeAdView.mediaView = nativeAdBinding.adMedia + + // Set other ad assets. + nativeAdView.headlineView = nativeAdBinding.adHeadline + nativeAdView.bodyView = nativeAdBinding.adBody + nativeAdView.callToActionView = nativeAdBinding.adCallToAction + nativeAdView.iconView = nativeAdBinding.adAppIcon + nativeAdView.priceView = nativeAdBinding.adPrice + nativeAdView.starRatingView = nativeAdBinding.adStars + nativeAdView.storeView = nativeAdBinding.adStore + nativeAdView.advertiserView = nativeAdBinding.adAdvertiser + + nativeAdBinding.adHeadline.text = nativeAd.headline + nativeAd.mediaContent?.let { nativeAdBinding.adMedia.setMediaContent(it) } + + if (nativeAd.body == null) { + nativeAdBinding.adBody.visibility = View.INVISIBLE + } else { + nativeAdBinding.adBody.text = nativeAd.body + nativeAdBinding.adBody.visibility = View.VISIBLE + } + + if (nativeAd.callToAction == null) { + nativeAdBinding.adCallToAction.visibility = View.INVISIBLE + } else { + nativeAdBinding.adCallToAction.text = nativeAd.callToAction + nativeAdBinding.adCallToAction.visibility = View.VISIBLE + } + + if (nativeAd.icon == null) { + nativeAdBinding.adAppIcon.visibility = View.GONE + } else { + nativeAdBinding.adAppIcon.setImageDrawable(nativeAd.icon?.drawable) + nativeAdBinding.adAppIcon.visibility = View.VISIBLE + } + + if (nativeAd.price == null) { + nativeAdBinding.adPrice.visibility = View.INVISIBLE + } else { + nativeAdBinding.adPrice.text = nativeAd.price + nativeAdBinding.adPrice.visibility = View.VISIBLE + } + + if (nativeAd.store == null) { + nativeAdBinding.adStore.visibility = View.INVISIBLE + } else { + nativeAdBinding.adStore.text = nativeAd.store + nativeAdBinding.adStore.visibility = View.VISIBLE + } + + if (nativeAd.starRating == null) { + nativeAdBinding.adStars.visibility = View.INVISIBLE + } else { + nativeAdBinding.adStars.rating = nativeAd.starRating!!.toFloat() + nativeAdBinding.adStars.visibility = View.VISIBLE + } + + if (nativeAd.advertiser == null) { + nativeAdBinding.adAdvertiser.visibility = View.INVISIBLE + } else { + nativeAdBinding.adAdvertiser.text = nativeAd.advertiser + nativeAdBinding.adAdvertiser.visibility = View.VISIBLE + } + + nativeAdView.setNativeAd(nativeAd) + // [END display_native_ad] + } + private fun destroyAd(nativeAd: NativeAd) { // [START destroy_ad] nativeAd.destroy() diff --git a/kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/snippets/NativeAdSnippets.kt b/kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/snippets/NativeAdSnippets.kt new file mode 100644 index 000000000..8d672b86e --- /dev/null +++ b/kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/snippets/NativeAdSnippets.kt @@ -0,0 +1,138 @@ +// 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.example.jetpackcomposedemo.snippets + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.example.jetpackcomposedemo.R +import com.google.android.gms.ads.nativead.NativeAd +import com.google.android.gms.compose_util.NativeAdAttribution +import com.google.android.gms.compose_util.NativeAdHeadlineView +import com.google.android.gms.compose_util.NativeAdMediaView +import com.google.android.gms.compose_util.NativeAdView + +/** Kotlin code snippets for the developer guide. */ +internal class NativeAdSnippets { + + // [START define_native_ad_view] + @Composable + /** Display a native ad with a user defined template. */ + fun DisplayNativeAdView(nativeAd: NativeAd) { + val context = LocalContext.current + Box(modifier = Modifier.padding(8.dp).wrapContentHeight(Alignment.Top)) { + // Call the NativeAdView composable to display the native ad. + NativeAdView { + // Inside the NativeAdView composable, display the native ad assets. + Column(Modifier.align(Alignment.TopStart).wrapContentHeight(Alignment.Top)) { + // Display the ad attribution. This is required. + NativeAdAttribution(text = context.getString(R.string.attribution)) + // Display the headline asset. This is required. + nativeAd.headline?.let { + NativeAdHeadlineView { Text(text = it, style = MaterialTheme.typography.headlineLarge) } + } + // Display the media asset. This is required. + NativeAdMediaView(Modifier.fillMaxWidth().height(500.dp).fillMaxHeight()) + } + } + } + } + + // [END define_native_ad_view] + + // [START load_native_ad_for_display] + @Composable + fun loadAndDisplayNativeAd() { + var nativeAd by remember { mutableStateOf(null) } + val context = LocalContext.current + var isDisposed by remember { mutableStateOf(false) } + + DisposableEffect(Unit) { + // Load the native ad when we launch this screen + loadNativeAd( + context = context, + onAdLoaded = { ad -> + // Handle the native ad being loaded. + if (!isDisposed) { + nativeAd = ad + } else { + // Destroy the native ad if loaded after the screen is disposed. + ad.destroy() + } + }, + ) + // Destroy the native ad to prevent memory leaks when we dispose of this screen. + onDispose { + isDisposed = true + nativeAd?.destroy() + nativeAd = null + } + } + + // Display the native ad view with a user defined template. + nativeAd?.let { adValue -> displayNativeAdView(adValue) } + } + + fun loadNativeAd(context: Context, onAdLoaded: (NativeAd) -> Unit) { + val adLoader = + AdLoader.Builder(context, NATIVE_AD_UNIT_ID) + .forNativeAd { nativeAd -> onAdLoaded(nativeAd) } + .withAdListener( + object : AdListener() { + override fun onAdFailedToLoad(error: LoadAdError) { + Log.e(TAG, "Native ad failed to load: ${error.message}") + } + + override fun onAdLoaded() { + Log.d(TAG, "Native ad was loaded.") + } + + override fun onAdImpression() { + Log.d(TAG, "Native ad recorded an impression.") + } + + override fun onAdClicked() { + Log.d(TAG, "Native ad was clicked.") + } + } + ) + .build() + adLoader.loadAd(AdRequest.Builder().build()) + } + + // [END load_native_ad_for_display] + + private companion object { + // Test ad unit IDs. + // For more information, + // see https://developers.google.com/admob/android/test-ads. + // and https://developers.google.com/ad-manager/mobile-ads-sdk/android/test-ads. + const val AD_UNIT_ID = "ca-app-pub-3940256099942544/2247696110" + const val VIDEO_AD_UNIT_ID = "ca-app-pub-3940256099942544/1044960115" + const val ADMANAGER_AD_UNIT_ID = "/21775744923/example/native" + const val ADMANAGER_VIDEO_AD_UNIT_ID = "/21775744923/example/native-video" + } +}