Skip to content

Commit 04defc4

Browse files
Nicholas Ventimigliacopybara-github
authored andcommitted
Added Jetpack Compose Banner Sample.
PiperOrigin-RevId: 668083400
1 parent f518858 commit 04defc4

10 files changed

Lines changed: 254 additions & 11 deletions

File tree

kotlin/advanced/JetpackComposeDemo/app/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,18 @@ android {
3636
dependencies {
3737
implementation("androidx.activity:activity")
3838
implementation("androidx.activity:activity-ktx")
39-
implementation("androidx.activity:activity-compose:1.9.0")
39+
implementation("androidx.activity:activity-compose:1.9.1")
4040
implementation("androidx.core:core-ktx:1.13.1")
41-
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
41+
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4")
4242
implementation(platform("androidx.compose:compose-bom:2024.06.00"))
4343
implementation("androidx.compose.ui:ui")
4444
implementation("androidx.compose.ui:ui-graphics")
4545
implementation("androidx.compose.material3:material3")
4646
implementation("androidx.compose.foundation:foundation")
4747
implementation("androidx.navigation:navigation-compose:2.7.7")
48+
implementation("androidx.navigation:navigation-runtime-ktx:2.7.7")
4849
implementation("com.google.android.gms:play-services-ads:23.3.0")
4950
implementation("com.google.android.ump:user-messaging-platform:3.0.0")
5051
implementation(project(":compose-util"))
51-
implementation("androidx.navigation:navigation-runtime-ktx:2.7.7")
5252
debugImplementation("androidx.compose.ui:ui-tooling")
5353
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.gms.example.jetpackcomposedemo
18+
19+
import android.content.Context
20+
import android.util.Log
21+
import androidx.compose.foundation.layout.Arrangement
22+
import androidx.compose.foundation.layout.Box
23+
import androidx.compose.foundation.layout.Column
24+
import androidx.compose.foundation.layout.fillMaxSize
25+
import androidx.compose.foundation.layout.fillMaxWidth
26+
import androidx.compose.material3.MaterialTheme
27+
import androidx.compose.material3.Surface
28+
import androidx.compose.runtime.Composable
29+
import androidx.compose.runtime.DisposableEffect
30+
import androidx.compose.runtime.LaunchedEffect
31+
import androidx.compose.runtime.getValue
32+
import androidx.compose.runtime.mutableStateOf
33+
import androidx.compose.runtime.remember
34+
import androidx.compose.runtime.setValue
35+
import androidx.compose.ui.Alignment
36+
import androidx.compose.ui.Modifier
37+
import androidx.compose.ui.platform.LocalConfiguration
38+
import androidx.compose.ui.platform.LocalContext
39+
import androidx.compose.ui.platform.LocalInspectionMode
40+
import androidx.compose.ui.tooling.preview.Preview
41+
import com.google.android.gms.ads.AdListener
42+
import com.google.android.gms.ads.AdRequest
43+
import com.google.android.gms.ads.AdSize
44+
import com.google.android.gms.ads.AdView
45+
import com.google.android.gms.ads.LoadAdError
46+
import com.google.android.gms.compose_util.BannerAd
47+
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.BANNER_ADUNIT_ID
48+
import com.google.android.gms.example.jetpackcomposedemo.GoogleMobileAdsApplication.Companion.TAG
49+
import com.google.android.gms.example.jetpackcomposedemo.ui.theme.JetpackComposeDemoTheme
50+
51+
@Composable
52+
fun BannerScreen(modifier: Modifier = Modifier) {
53+
val context = LocalContext.current
54+
val isPreviewMode = LocalInspectionMode.current
55+
val deviceWidth = LocalConfiguration.current.screenWidthDp
56+
var adView by remember { mutableStateOf<AdView?>(null) }
57+
58+
LaunchedEffect(context, deviceWidth) {
59+
// Create a new AdView when size changes.
60+
adView?.destroy()
61+
adView = loadAdaptiveBannerAd(context, deviceWidth, isPreviewMode)
62+
}
63+
64+
Column(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom) {
65+
adView?.let { adView ->
66+
Box(modifier = Modifier.fillMaxWidth()) {
67+
BannerAd(adView, Modifier.align(Alignment.BottomCenter))
68+
}
69+
}
70+
}
71+
72+
// Clean up the AdView after use.
73+
DisposableEffect(Unit) { onDispose { adView?.destroy() } }
74+
}
75+
76+
private fun loadAdaptiveBannerAd(context: Context, width: Int, isPreviewMode: Boolean): AdView {
77+
val adView = AdView(context)
78+
79+
// Do not load the AdView in preview mode.
80+
if (isPreviewMode) {
81+
return adView
82+
}
83+
84+
adView.adUnitId = BANNER_ADUNIT_ID
85+
val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width)
86+
adView.setAdSize(adSize)
87+
88+
adView.adListener =
89+
object : AdListener() {
90+
override fun onAdLoaded() {
91+
Log.d(TAG, "Banner ad was loaded.")
92+
}
93+
94+
override fun onAdFailedToLoad(error: LoadAdError) {
95+
Log.e(TAG, "Banner ad failed to load.")
96+
}
97+
98+
override fun onAdImpression() {
99+
Log.d(TAG, "Banner ad had an impression.")
100+
}
101+
102+
override fun onAdClicked() {
103+
Log.d(TAG, "Banner ad was clicked.")
104+
}
105+
}
106+
107+
val adRequest = AdRequest.Builder().build()
108+
adView.loadAd(adRequest)
109+
return adView
110+
}
111+
112+
@Preview
113+
@Composable
114+
private fun BannerScreenPreview() {
115+
JetpackComposeDemoTheme {
116+
Surface(color = MaterialTheme.colorScheme.background) { BannerScreen() }
117+
}
118+
}

kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/GoogleMobileAdsApplication.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@ import android.app.Application
2121
class GoogleMobileAdsApplication : Application() {
2222
companion object {
2323
const val TAG = "GoogleMobileAdsSample"
24+
25+
const val BANNER_ADUNIT_ID = "ca-app-pub-3940256099942544/9214589741"
2426
}
2527
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.google.android.gms.example.jetpackcomposedemo
2+
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.material3.Button
6+
import androidx.compose.material3.MaterialTheme
7+
import androidx.compose.material3.Surface
8+
import androidx.compose.material3.Text
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.platform.LocalContext
12+
import androidx.compose.ui.tooling.preview.Preview
13+
import androidx.navigation.NavHostController
14+
import androidx.navigation.compose.rememberNavController
15+
import com.example.jetpackcomposedemo.R
16+
import com.google.android.gms.example.jetpackcomposedemo.ui.theme.JetpackComposeDemoTheme
17+
18+
@Composable
19+
fun HomeScreen(
20+
uiState: MainUiState,
21+
navController: NavHostController,
22+
modifier: Modifier = Modifier,
23+
) {
24+
Column {
25+
Button(
26+
onClick = { navController.navigate(NavDestinations.Banner.name) },
27+
enabled = uiState.canRequestAds,
28+
modifier = modifier.fillMaxWidth(),
29+
) {
30+
Text(LocalContext.current.getString(R.string.nav_banner))
31+
}
32+
}
33+
}
34+
35+
@Preview
36+
@Composable
37+
private fun HomeScreenPreview() {
38+
JetpackComposeDemoTheme {
39+
Surface(color = MaterialTheme.colorScheme.background) {
40+
HomeScreen(MainUiState(), rememberNavController())
41+
}
42+
}
43+
}

kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/MainActivity.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import androidx.activity.ComponentActivity
2222
import androidx.activity.compose.setContent
2323
import androidx.activity.enableEdgeToEdge
2424
import androidx.lifecycle.lifecycleScope
25-
import com.example.jetpackcomposedemo.R
2625
import com.google.android.gms.ads.MobileAds
2726
import kotlinx.coroutines.launch
2827

@@ -38,7 +37,7 @@ class MainActivity : ComponentActivity() {
3837
// Log the Mobile Ads SDK version.
3938
Log.d(
4039
GoogleMobileAdsApplication.TAG,
41-
getString(R.string.version_format, MobileAds.getVersion()),
40+
"Google Mobile Ads SDK Version: ${MobileAds.getVersion()}",
4241
)
4342

4443
// Initialize the view model. This will gather consent and initialize Google Mobile Ads.

kotlin/advanced/JetpackComposeDemo/app/src/main/java/com/google/android/gms/example/jetpackcomposedemo/MainScreen.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.google.android.gms.example.jetpackcomposedemo
33
import android.content.Context
44
import android.content.ContextWrapper
55
import androidx.activity.ComponentActivity
6+
import androidx.compose.foundation.background
67
import androidx.compose.foundation.layout.Column
78
import androidx.compose.foundation.layout.Spacer
89
import androidx.compose.foundation.layout.WindowInsets
@@ -72,11 +73,12 @@ fun MainScreen(googleMobileAdsViewModel: MainViewModel, modifier: Modifier = Mod
7273
)
7374
},
7475
contentWindowInsets =
75-
WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
76+
WindowInsets.systemBars.only(WindowInsetsSides.Vertical + WindowInsetsSides.Horizontal),
7677
) { innerPadding ->
7778
Column(Modifier.padding(innerPadding)) {
7879
NavHost(navController = navController, startDestination = NavDestinations.Home.name) {
79-
composable(NavDestinations.Home.name) {}
80+
composable(NavDestinations.Home.name) { HomeScreen(uiState, navController) }
81+
composable(NavDestinations.Banner.name) { BannerScreen() }
8082
}
8183
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
8284
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.google.android.gms.example.jetpackcomposedemo
22

33
enum class NavDestinations {
4-
Home
4+
Home,
5+
Banner,
56
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<resources>
22
<string name="app_name">Google Mobile Ads Jetpack Compose Demo</string>
3-
<string name="adinspector_open_button">Ad inspector</string>
3+
<string name="adinspector_open_button">Ad Inspector</string>
44
<string name="main_title">Jetpack Compose Demo</string>
55
<string name="nav_home">Home</string>
6+
<string name="nav_banner">Banner</string>
67
<string name="privacy_options_open_button">Show Privacy Options Form</string>
7-
<string name="version_format">Google Mobile Ads SDK Version: %s </string>
8+
<string name="text_reload">Reload Ad</string>
89
</resources>

kotlin/advanced/JetpackComposeDemo/compose-util/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ android {
3030

3131
dependencies {
3232
implementation("androidx.core:core-ktx:1.13.1")
33-
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
33+
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4")
3434
implementation(platform("androidx.compose:compose-bom:2024.06.00"))
3535
implementation("androidx.compose.ui:ui")
3636
implementation("androidx.compose.ui:ui-graphics")
3737
implementation("androidx.compose.material3:material3")
3838
implementation("androidx.compose.foundation:foundation")
3939
implementation("com.google.android.gms:play-services-ads:23.2.0")
40+
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.4")
4041
debugImplementation("androidx.compose.ui:ui-tooling")
4142
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.google.android.gms.compose_util
2+
3+
/*
4+
* Copyright 2024 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import android.view.ViewGroup
20+
import android.widget.FrameLayout
21+
import androidx.compose.foundation.layout.Box
22+
import androidx.compose.foundation.layout.wrapContentSize
23+
import androidx.compose.material3.Text
24+
import androidx.compose.runtime.Composable
25+
import androidx.compose.runtime.DisposableEffect
26+
import androidx.compose.runtime.getValue
27+
import androidx.compose.runtime.mutableStateOf
28+
import androidx.compose.runtime.remember
29+
import androidx.compose.runtime.setValue
30+
import androidx.compose.ui.Alignment
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.platform.LocalInspectionMode
33+
import androidx.compose.ui.viewinterop.AndroidView
34+
import androidx.lifecycle.compose.LifecycleResumeEffect
35+
import com.google.android.gms.ads.AdView
36+
37+
/**
38+
* A composable function to display a banner advertisement.
39+
*
40+
* @param adView The banner [AdView].
41+
* @param modifier The modifier to apply to the banner ad.
42+
*/
43+
@Composable
44+
fun BannerAd(adView: AdView, modifier: Modifier = Modifier) {
45+
var parent by remember { mutableStateOf<FrameLayout?>(null) }
46+
// Ad load does not work in preview mode because it requires a network connection.
47+
if (LocalInspectionMode.current) {
48+
Box { Text(text = "Google Mobile Ads preview banner.", modifier.align(Alignment.Center)) }
49+
return
50+
}
51+
52+
AndroidView(
53+
modifier = modifier.wrapContentSize(),
54+
factory = { context -> FrameLayout(context).also { parent = it } },
55+
update = { layout ->
56+
disposeLayout(adView, layout)
57+
layout.addView(adView)
58+
},
59+
)
60+
61+
// Pause and resume the AdView when the lifecycle is paused and resumed.
62+
LifecycleResumeEffect(adView) {
63+
adView.resume()
64+
onPauseOrDispose { adView.pause() }
65+
}
66+
67+
// Clean up the AdView after use.
68+
DisposableEffect(Unit) { onDispose { disposeLayout(adView, parent) } }
69+
}
70+
71+
/** Clean up the AdView after use. */
72+
private fun disposeLayout(adView: AdView, layout: FrameLayout?) {
73+
// Ensure AdViews and Composable references are up to date.
74+
(adView.parent as? ViewGroup)?.removeView(adView)
75+
layout?.removeAllViews()
76+
}

0 commit comments

Comments
 (0)