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..6aa2ca7fe 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,53 +14,23 @@ package com.google.android.gms.snippets -import android.content.Context +import android.app.Activity +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageView import com.google.android.gms.ads.AdListener import com.google.android.gms.ads.AdLoader import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.LoadAdError import com.google.android.gms.ads.admanager.AdManagerAdRequest +import com.google.android.gms.ads.nativead.MediaView 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 +import com.google.android.gms.example.apidemo.databinding.NativeAdBinding /** Kotlin code snippets for the developer guide. */ internal class NativeAdSnippets { - private fun createAdLoader(context: Context) { - // [START create_ad_loader] - // It is recommended to call AdLoader.Builder on a background thread. - CoroutineScope(Dispatchers.IO).launch { - val adLoader = - AdLoader.Builder(context, AD_UNIT_ID) - .forNativeAd { nativeAd -> - // The native ad loaded successfully. You can show the ad. - } - .withAdListener( - object : AdListener() { - override fun onAdFailedToLoad(adError: LoadAdError) { - // The native ad load failed. Check the adError message for failure reasons. - } - } - ) - // Use the NativeAdOptions.Builder class to specify individual options settings. - .withNativeAdOptions(NativeAdOptions.Builder().build()) - .build() - } - // [END create_ad_loader] - } - - private fun setAdLoaderListener(adLoaderBuilder: AdLoader.Builder) { - // [START set_ad_listener] - adLoaderBuilder.withAdListener( - // Override AdListener callbacks here. - object : AdListener() {} - ) - // [END set_ad_listener] - } - private fun loadAd(adLoader: AdLoader) { // [START load_ad] adLoader.loadAd(AdRequest.Builder().build()) @@ -97,20 +67,144 @@ internal class NativeAdSnippets { // [END handle_ad_loaded] } + private fun addNativeAdView( + activity: Activity, + nativeAd: NativeAd, + layoutInflater: LayoutInflater, + frameLayout: FrameLayout, + ) { + // [START add_ad_view] + activity.runOnUiThread { + // Inflate the native ad view and add it to the view hierarchy. + val nativeAdBinding = NativeAdBinding.inflate(layoutInflater) + val adView = nativeAdBinding.root + + // Display and register the native ad asset views here. + displayAndRegisterNativeAd(nativeAd, nativeAdBinding) + + // Remove all old ad views and add the new native. + frameLayout.removeAllViews() + // Add the new native ad view to the view hierarchy. + frameLayout.addView(adView) + } + // [END add_ad_view] + } + + // [START display_native_ad] + private fun displayAndRegisterNativeAd(nativeAd: NativeAd, nativeAdBinding: NativeAdBinding) { + // [START populate_native_ad_view] + // Populate all native ad view assets with the native ad. + nativeAdBinding.adMedia.mediaContent = nativeAd.mediaContent + nativeAdBinding.adHeadline.text = nativeAd.headline + + // Hide all native ad view assets that are not returned within the native ad. + nativeAd.body?.let { body -> + nativeAdBinding.adBody.text = body + nativeAdBinding.adBody.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adBody.visibility = View.INVISIBLE } + + nativeAd.callToAction?.let { callToAction -> + nativeAdBinding.adCallToAction.text = callToAction + nativeAdBinding.adCallToAction.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adCallToAction.visibility = View.INVISIBLE } + + nativeAd.icon?.let { icon -> + nativeAdBinding.adAppIcon.setImageDrawable(icon.drawable) + nativeAdBinding.adAppIcon.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adAppIcon.visibility = View.GONE } + + nativeAd.price?.let { price -> + nativeAdBinding.adPrice.text = price + nativeAdBinding.adPrice.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adPrice.visibility = View.INVISIBLE } + + nativeAd.store?.let { store -> + nativeAdBinding.adStore.text = store + nativeAdBinding.adStore.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adStore.visibility = View.INVISIBLE } + + nativeAd.starRating?.let { rating -> + nativeAdBinding.adStars.rating = rating.toFloat() + nativeAdBinding.adStars.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adStars.visibility = View.INVISIBLE } + + nativeAd.advertiser?.let { advertiser -> + nativeAdBinding.adAdvertiser.text = advertiser + nativeAdBinding.adAdvertiser.visibility = View.VISIBLE + } ?: run { nativeAdBinding.adAdvertiser.visibility = View.INVISIBLE } + // [END populate_native_ad_view] + + // [START register_native_ad_assets] + // Register all native ad assets with the native ad view. + val nativeAdView = nativeAdBinding.root + nativeAdView.advertiserView = nativeAdBinding.adAdvertiser + nativeAdView.bodyView = nativeAdBinding.adBody + nativeAdView.callToActionView = nativeAdBinding.adCallToAction + nativeAdView.headlineView = nativeAdBinding.adHeadline + nativeAdView.iconView = nativeAdBinding.adAppIcon + nativeAdView.priceView = nativeAdBinding.adPrice + nativeAdView.starRatingView = nativeAdBinding.adStars + nativeAdView.storeView = nativeAdBinding.adStore + nativeAd.mediaContent?.let { nativeAdBinding.adMedia.setMediaContent(it) } + nativeAdView.mediaView = nativeAdBinding.adMedia + // [END register_native_ad_assets] + + // [START set_native_ad] + // This method tells the Google Mobile Ads SDK that you have finished populating your + // native ad view with this native ad. + nativeAdView.setNativeAd(nativeAd) + // [END set_native_ad] + } + + // [END display_native_ad] + private fun destroyAd(nativeAd: NativeAd) { // [START destroy_ad] nativeAd.destroy() // [END destroy_ad] } - 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" + private fun setEventCallback(adLoader: AdLoader.Builder) { + // [START set_event_callback] + adLoader + .withAdListener( + object : AdListener() { + override fun onAdClosed() { + // Called when the ad is closed. + } + + override fun onAdFailedToLoad(adError: LoadAdError) { + // Called when an ad fails to load. + } + + override fun onAdOpened() { + // Called when an ad opens full screen. + } + + override fun onAdLoaded() { + // Called when an ad has loaded. + } + + override fun onAdClicked() { + // Called when a click is recorded for an ad. + } + + override fun onAdImpression() { + // Called when an impression is recorded for an ad. + } + + override fun onAdSwipeGestureClicked() { + // Called when a swipe gesture is recorded for an ad. + } + } + ) + .build() + // [END set_event_callback] + } + + private fun setImageScaleType(mediaView: MediaView) { + // [START set_image_scale_type] + mediaView.setImageScaleType(ImageView.ScaleType.CENTER_CROP) + // [END set_image_scale_type] } } 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..f9168daab --- /dev/null +++ b/kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/snippets/NativeAdSnippets.kt @@ -0,0 +1,151 @@ +// 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 android.content.Context +import android.widget.ImageView +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.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.AdListener +import com.google.android.gms.ads.AdLoader +import com.google.android.gms.ads.AdRequest +import com.google.android.gms.ads.LoadAdError +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 display_native_ad_screen] + @Composable + fun DisplayNativeAdScreen(adunitId: String) { + 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, + adunitId, + 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() + } + }, + ) + // [START destroy_native_ad] + // Destroy the native ad to prevent memory leaks when we dispose of this screen. + onDispose { + isDisposed = true + nativeAd?.destroy() + nativeAd = null + } + // [END destroy_native_ad] + } + + // Display the native ad view with a user defined template here. + nativeAd?.let { adValue -> DisplayNativeAdView(adValue) } + } + + // [END display_native_ad_screen] + + // [START load_native_ad_compose] + fun loadNativeAd(context: Context, adUnitId: String, onAdLoaded: (NativeAd) -> Unit) { + val adLoader = + AdLoader.Builder(context, adUnitId) + .forNativeAd { nativeAd -> onAdLoaded(nativeAd) } + .withAdListener( + object : AdListener() { + override fun onAdFailedToLoad(error: LoadAdError) { + // Native ad failed to load. + } + + override fun onAdLoaded() { + // Native ad was loaded. + } + + override fun onAdImpression() { + // Native ad recorded an impression. + } + + override fun onAdClicked() { + // Native ad was clicked. + } + } + ) + .build() + adLoader.loadAd(AdRequest.Builder().build()) + } + + // [END load_native_ad_compose] + + @Composable + fun SetImageScaleType() { + // [START set_image_scale_type_compose] + NativeAdMediaView(Modifier.fillMaxWidth(), scaleType = ImageView.ScaleType.CENTER_CROP) + // [END set_image_scale_type_compose] + } +}