Skip to content

Commit

Permalink
Added Jetpack Compose LazyList Banner Sample.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 654936228
  • Loading branch information
nventimigli authored and copybara-github committed Sep 20, 2024
1 parent 05920e2 commit 8af96c9
Show file tree
Hide file tree
Showing 14 changed files with 236 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
android:value="true" />

<activity
android:name="com.google.android.gms.example.jetpackcomposedemo.MainActivity"
android:name="com.google.android.gms.example.jetpackcomposedemo.main.MainActivity"
android:exported="true"
android:theme="@style/Theme.JetpackComposeDemo">
<intent-filter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ class GoogleMobileAdsApplication : Application() {
companion object {
const val TAG = "GoogleMobileAdsSample"

const val BANNER_ADUNIT_ID = "ca-app-pub-3940256099942544/9214589741"
const val BANNER_AD_UNIT_ID = "ca-app-pub-3940256099942544/9214589741"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package com.google.android.gms.example.jetpackcomposedemo

import android.app.Activity
import android.content.Context
import com.google.android.gms.example.jetpackcomposedemo.MainViewModel.Companion.TEST_DEVICE_HASHED_ID
import com.google.android.gms.example.jetpackcomposedemo.main.MainViewModel.Companion.TEST_DEVICE_HASHED_ID
import com.google.android.ump.ConsentDebugSettings
import com.google.android.ump.ConsentForm.OnConsentFormDismissedListener
import com.google.android.ump.ConsentInformation
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.google.android.gms.example.jetpackcomposedemo
package com.google.android.gms.example.jetpackcomposedemo.formats

import android.content.Context
import android.util.Log
Expand Down Expand Up @@ -44,7 +44,7 @@ import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.compose_util.BannerAd
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.BANNER_ADUNIT_ID
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.BANNER_AD_UNIT_ID
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.TAG
import com.google.android.gms.example.jetpackcomposedemo.ui.theme.JetpackComposeDemoTheme

Expand Down Expand Up @@ -81,7 +81,7 @@ private fun loadAdaptiveBannerAd(context: Context, width: Int, isPreviewMode: Bo
return adView
}

adView.adUnitId = BANNER_ADUNIT_ID
adView.adUnitId = BANNER_AD_UNIT_ID
val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width)
adView.setAdSize(adSize)

Expand All @@ -92,11 +92,11 @@ private fun loadAdaptiveBannerAd(context: Context, width: Int, isPreviewMode: Bo
}

override fun onAdFailedToLoad(error: LoadAdError) {
Log.e(TAG, "Banner ad failed to load.")
Log.e(TAG, "Banner ad failed to load: ${error.message}")
}

override fun onAdImpression() {
Log.d(TAG, "Banner ad had an impression.")
Log.d(TAG, "Banner ad recorded an impression.")
}

override fun onAdClicked() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2024 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
*
* http://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.formats

import android.content.Context
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.jetpackcomposedemo.R
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.compose_util.BannerAd
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.BANNER_AD_UNIT_ID
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.TAG
import com.google.android.gms.example.jetpackcomposedemo.ui.theme.JetpackComposeDemoTheme
import kotlin.coroutines.resume
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.suspendCancellableCoroutine

@Composable
fun LazyBannerScreen(modifier: Modifier = Modifier) {
val context = LocalContext.current
val isPreviewMode = LocalInspectionMode.current
val deviceCurrentWidth = LocalConfiguration.current.screenWidthDp
val adsToLoad = 5

// Indicate whether the banner ads are currently being loaded.
var isLoadingAds by remember { mutableStateOf(true) }
var loadedAds by remember { mutableStateOf<List<AdView>>(emptyList()) }
val fillerText = loadFillerText(context)

// Load ads when on launch of the composition.
LaunchedEffect(Unit) {
if (!isPreviewMode) {
loadedAds = loadBannerAds(context, adsToLoad, deviceCurrentWidth)
}
isLoadingAds = false
}

// Display a loading indicator if ads are still being fetched.
if (isLoadingAds) {
CircularProgressIndicator(modifier = modifier.width(100.dp).height(100.dp))
} else {
// Display a lazy list with loaded ads and filler content.
LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
items(loadedAds) { adView ->
Column {
BannerAd(adView, modifier.fillMaxWidth())
// Display the filler content.
fillerText.forEach { content ->
Box(
modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.primaryContainer)
.padding(8.dp)
) {
Text(
text = content,
modifier.padding(8.dp),
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
}
}
}

// Clean up the AdViews after use.
DisposableEffect(Unit) { onDispose { loadedAds.forEach { adView -> adView.destroy() } } }
}

@Preview
@Composable
private fun LazyBannerScreenPreview() {
JetpackComposeDemoTheme {
Surface(color = MaterialTheme.colorScheme.background) { LazyBannerScreen() }
}
}

private suspend fun loadBannerAd(context: Context, width: Int): Result<AdView> {
return suspendCancellableCoroutine { continuation ->
val adView = AdView(context)
adView.adUnitId = BANNER_AD_UNIT_ID
val adSize = AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(context, width)
adView.setAdSize(adSize)
adView.adListener =
object : AdListener() {
override fun onAdLoaded() {
Log.d(TAG, "Banner ad was loaded.")
continuation.resume(Result.success(adView))
}

override fun onAdFailedToLoad(error: LoadAdError) {
Log.e(TAG, "Banner ad failed to load: ${error.message}")
adView.destroy()
continuation.resume(Result.failure(Error(error.message)))
}

override fun onAdImpression() {
Log.d(TAG, "Banner ad recorded an impression.")
}

override fun onAdClicked() {
Log.d(TAG, "Banner ad was clicked.")
}
}

continuation.invokeOnCancellation {
// When the coroutine is cancelled, clean up resources.
adView.destroy()
}

val adRequest = AdRequest.Builder().build()
adView.loadAd(adRequest)
}
}

private suspend fun loadBannerAds(context: Context, count: Int, width: Int): List<AdView> =
supervisorScope {
List(count) {
async {
try {
loadBannerAd(context, width).getOrNull()
} catch (e: Error) {
null
}
}
}
.awaitAll()
.filterNotNull()
}

fun loadFillerText(context: Context): List<String> {
val fillerContent = context.resources.getStringArray(R.array.lazy_banner_filler_content)
return fillerContent.toList()
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.google.android.gms.example.jetpackcomposedemo
package com.google.android.gms.example.jetpackcomposedemo.main

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -25,10 +25,17 @@ fun HomeScreen(
Button(
onClick = { navController.navigate(NavDestinations.Banner.name) },
enabled = uiState.canRequestAds,
modifier = modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
) {
Text(LocalContext.current.getString(R.string.nav_banner))
}
Button(
onClick = { navController.navigate(NavDestinations.LazyBanner.name) },
enabled = uiState.canRequestAds,
modifier = Modifier.fillMaxWidth(),
) {
Text(LocalContext.current.getString(R.string.nav_lazy_banner))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.google.android.gms.example.jetpackcomposedemo
package com.google.android.gms.example.jetpackcomposedemo.main

import android.os.Bundle
import android.util.Log
Expand All @@ -23,6 +23,7 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.google.android.gms.example.jetpackcomposedemo
package com.google.android.gms.example.jetpackcomposedemo.main

import android.content.Context
import android.content.ContextWrapper
import androidx.activity.ComponentActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
Expand Down Expand Up @@ -40,6 +39,8 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.jetpackcomposedemo.R
import com.google.android.gms.example.jetpackcomposedemo.formats.BannerScreen
import com.google.android.gms.example.jetpackcomposedemo.formats.LazyBannerScreen
import com.google.android.gms.example.jetpackcomposedemo.ui.theme.JetpackComposeDemoTheme

@Composable
Expand Down Expand Up @@ -79,6 +80,7 @@ fun MainScreen(googleMobileAdsViewModel: MainViewModel, modifier: Modifier = Mod
NavHost(navController = navController, startDestination = NavDestinations.Home.name) {
composable(NavDestinations.Home.name) { HomeScreen(uiState, navController) }
composable(NavDestinations.Banner.name) { BannerScreen() }
composable(NavDestinations.LazyBanner.name) { LazyBannerScreen() }
}
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}
Expand Down Expand Up @@ -120,12 +122,18 @@ private fun MainTopBar(
DropdownMenuItem(
text = { Text(context.getString(R.string.adinspector_open_button)) },
enabled = isMobileAdsInitialized,
onClick = onOpenAdInspector,
onClick = {
menuExpanded = false
onOpenAdInspector()
},
)
if (isPrivacyOptionsRequired) {
DropdownMenuItem(
text = { Text(context.getString(R.string.privacy_options_open_button)) },
onClick = onShowPrivacyOptionsForm,
onClick = {
menuExpanded = false
onShowPrivacyOptionsForm()
},
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.google.android.gms.example.jetpackcomposedemo
package com.google.android.gms.example.jetpackcomposedemo.main

/** UiState for the MainViewModel. */
data class MainUiState(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.google.android.gms.example.jetpackcomposedemo
package com.google.android.gms.example.jetpackcomposedemo.main

import android.app.Activity
import android.content.Context
Expand All @@ -7,6 +7,8 @@ import android.widget.Toast
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.OnAdInspectorClosedListener
import com.google.android.gms.ads.RequestConfiguration
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsConsentManager
import com.google.android.ump.ConsentForm
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.Dispatchers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.google.android.gms.example.jetpackcomposedemo.main

enum class NavDestinations {
Home,
Banner,
LazyBanner,
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,4 @@ val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

val ColorStateLoaded = Color(0xFF009900)
val ColorStateUnloaded = Color(0xFFcc6600)
val ColorStateError = Color(0xFFcc0000)
val LightBlue = Color(0xFFADD8E6)
Loading

0 comments on commit 8af96c9

Please sign in to comment.