diff --git a/kotlin/advanced/APIDemo/app/build.gradle b/kotlin/advanced/APIDemo/app/build.gradle
index 98cf7abc2..f35342531 100644
--- a/kotlin/advanced/APIDemo/app/build.gradle
+++ b/kotlin/advanced/APIDemo/app/build.gradle
@@ -7,7 +7,7 @@ android {
compileSdk 34
defaultConfig {
applicationId "com.google.android.gms.example.apidemo"
- minSdkVersion 21
+ minSdkVersion 24
targetSdkVersion 34
versionCode 1
versionName "1.0"
diff --git a/kotlin/advanced/APIDemo/app/src/main/AndroidManifest.xml b/kotlin/advanced/APIDemo/app/src/main/AndroidManifest.xml
index 9ef44e66b..d4431d4b7 100644
--- a/kotlin/advanced/APIDemo/app/src/main/AndroidManifest.xml
+++ b/kotlin/advanced/APIDemo/app/src/main/AndroidManifest.xml
@@ -5,7 +5,7 @@
AdMobBannerSizesFragment()
R.id.nav_collapsible_banner -> CollapsibleBannerFragment()
R.id.nav_admob_custommute -> AdMobCustomMuteThisAdFragment()
+ R.id.nav_admob_preload -> AdMobPreloadingAdsFragment()
R.id.nav_gam_adsizes -> AdManagerMultipleAdSizesFragment()
R.id.nav_gam_appevents -> AdManagerAppEventsFragment()
R.id.nav_gam_customtargeting -> AdManagerCustomTargetingFragment()
diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/AdMobPreloadingAdsFragment.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/AdMobPreloadingAdsFragment.kt
new file mode 100644
index 000000000..a54bdd79a
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/AdMobPreloadingAdsFragment.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.apidemo.preloading
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import com.google.android.gms.ads.AdFormat
+import com.google.android.gms.ads.MobileAds
+import com.google.android.gms.ads.preload.PreloadCallback
+import com.google.android.gms.ads.preload.PreloadConfiguration
+import com.google.android.gms.example.apidemo.MainActivity.Companion.LOG_TAG
+import com.google.android.gms.example.apidemo.R
+import com.google.android.gms.example.apidemo.databinding.FragmentPreloadMainBinding
+
+/** Demonstrates how to preload ads. */
+class AdMobPreloadingAdsFragment : Fragment() {
+ private lateinit var viewBinding: FragmentPreloadMainBinding
+ private val fragmentList = mutableListOf()
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ viewBinding = FragmentPreloadMainBinding.inflate(inflater, container, false)
+
+ // Initialize the fragment UI.
+ addFragments()
+
+ // Start preloading ads.
+ startPreload()
+
+ return viewBinding.root
+ }
+
+ // [START start_preload]
+ private fun startPreload() {
+ // Define the number of ads that can be preloaded for each ad unit.
+ val bufferSize = 2
+ // Define a list of PreloadConfiguration objects, specifying the ad unit ID and ad format for
+ // each ad unit to be preloaded.
+ val preloadConfigs =
+ listOf(
+ PreloadConfiguration.Builder(InterstitialFragment.AD_UNIT_ID, AdFormat.INTERSTITIAL)
+ .setBufferSize(bufferSize)
+ .build(),
+ PreloadConfiguration.Builder(RewardedFragment.AD_UNIT_ID, AdFormat.REWARDED)
+ .setBufferSize(bufferSize)
+ .build(),
+ PreloadConfiguration.Builder(AppOpenFragment.AD_UNIT_ID, AdFormat.APP_OPEN_AD)
+ .setBufferSize(bufferSize)
+ .build(),
+ )
+
+ // Define a callback to receive preload availability events.
+ val callback =
+ object : PreloadCallback {
+ override fun onAdsAvailable(preloadConfig: PreloadConfiguration) {
+ Log.i(LOG_TAG, "Preload ad for ${ preloadConfig.adFormat } is available.")
+ updateUI()
+ }
+
+ override fun onAdsExhausted(preloadConfig: PreloadConfiguration) {
+ Log.i(LOG_TAG, "Preload ad for configuration ${ preloadConfig.adFormat } is exhausted.")
+ updateUI()
+ }
+ }
+
+ // Start the preloading initialization process.
+ MobileAds.startPreload(this.requireContext(), preloadConfigs, callback)
+ }
+
+ // [END start_preload]
+
+ private fun addFragments() {
+ addFragment(AppOpenFragment())
+ addFragment(InterstitialFragment())
+ addFragment(RewardedFragment())
+ }
+
+ private fun addFragment(fragment: T) {
+ val fragmentManager = getParentFragmentManager()
+ val fragmentTransaction = fragmentManager.beginTransaction()
+ fragmentTransaction.add(R.id.list_formats, fragment)
+ fragmentTransaction.commit()
+ fragmentList.add(fragment)
+ }
+
+ private fun updateUI() {
+ fragmentList.forEach { it.updateUI() }
+ }
+}
diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/AppOpenFragment.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/AppOpenFragment.kt
new file mode 100644
index 000000000..b3e2fd3a0
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/AppOpenFragment.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.apidemo.preloading
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.google.android.gms.ads.appopen.AppOpenAd
+import com.google.android.gms.example.apidemo.MainActivity.Companion.LOG_TAG
+import com.google.android.gms.example.apidemo.R
+import com.google.android.gms.example.apidemo.databinding.FragmentPreloadItemBinding
+
+/** A [Fragment] subclass that preloads an app open ad. */
+class AppOpenFragment : PreloadItemFragment() {
+
+ private lateinit var viewBinding: FragmentPreloadItemBinding
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ viewBinding = FragmentPreloadItemBinding.inflate(inflater, container, false)
+
+ // Initialize the UI.
+ initializeUI()
+
+ return viewBinding.root
+ }
+
+ private fun initializeUI() {
+ viewBinding.txtTitle.text = getText(R.string.preload_app_open)
+ viewBinding.btnShow.setOnClickListener {
+ pollAndShowAd()
+ updateUI()
+ }
+ updateUI()
+ }
+
+ private fun pollAndShowAd() {
+ // Verify that an ad is available before polling for an ad.
+ if (!AppOpenAd.isAdAvailable(requireContext(), AD_UNIT_ID)) {
+ Log.w(LOG_TAG, "Preloaded app open ad ${AD_UNIT_ID} is not available.")
+ return
+ }
+ // Polling returns the next available ad and load another ad in the background.
+ val ad = AppOpenAd.pollAd(requireContext(), AD_UNIT_ID)
+ activity?.let { activity -> ad?.show(activity) }
+ }
+
+ @Synchronized
+ override fun updateUI() {
+ if (AppOpenAd.isAdAvailable(requireContext(), AD_UNIT_ID)) {
+ viewBinding.txtStatus.text = getString(R.string.preload_available)
+ viewBinding.btnShow.isEnabled = true
+ } else {
+ viewBinding.txtStatus.text = getString(R.string.preload_exhausted)
+ viewBinding.btnShow.isEnabled = false
+ }
+ }
+
+ companion object {
+ const val AD_UNIT_ID = "ca-app-pub-3940256099942544/9257395921"
+ }
+}
diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/InterstitialFragment.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/InterstitialFragment.kt
new file mode 100644
index 000000000..642e034cf
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/InterstitialFragment.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.apidemo.preloading
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.google.android.gms.ads.interstitial.InterstitialAd
+import com.google.android.gms.example.apidemo.MainActivity.Companion.LOG_TAG
+import com.google.android.gms.example.apidemo.R
+import com.google.android.gms.example.apidemo.databinding.FragmentPreloadItemBinding
+
+/** A [Fragment] subclass that preloads an interstitial ad. */
+class InterstitialFragment : PreloadItemFragment() {
+
+ private lateinit var viewBinding: FragmentPreloadItemBinding
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ viewBinding = FragmentPreloadItemBinding.inflate(inflater, container, false)
+
+ initializeUI()
+
+ return viewBinding.root
+ }
+
+ private fun initializeUI() {
+ viewBinding.txtTitle.text = getText(R.string.preload_interstitial)
+ viewBinding.btnShow.setOnClickListener {
+ pollAndShowAd()
+ updateUI()
+ }
+ updateUI()
+ }
+
+ // [START pollAndShowAd]
+ private fun pollAndShowAd() {
+ // [START isAdAvailable]
+ // Verify that a preloaded ad is available before polling for an ad.
+ if (!InterstitialAd.isAdAvailable(requireContext(), AD_UNIT_ID)) {
+ Log.w(LOG_TAG, "Preloaded interstitial ad ${AD_UNIT_ID} is not available.")
+ return
+ }
+ // [END isAdAvailable]
+
+ // Polling returns the next available ad and load another ad in the background.
+ val ad = InterstitialAd.pollAd(requireContext(), AD_UNIT_ID)
+ activity?.let { activity -> ad?.show(activity) }
+ }
+
+ // [END pollAndShowAd]
+
+ @Synchronized
+ override fun updateUI() {
+ if (InterstitialAd.isAdAvailable(requireContext(), AD_UNIT_ID)) {
+ viewBinding.txtStatus.text = getString(R.string.preload_available)
+ viewBinding.btnShow.isEnabled = true
+ } else {
+ viewBinding.txtStatus.text = getString(R.string.preload_exhausted)
+ viewBinding.btnShow.isEnabled = false
+ }
+ }
+
+ companion object {
+ const val AD_UNIT_ID = "ca-app-pub-3940256099942544/1033173712"
+ }
+}
diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/PreloadItemFragment.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/PreloadItemFragment.kt
new file mode 100644
index 000000000..b8f65e063
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/PreloadItemFragment.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.apidemo.preloading
+
+import androidx.fragment.app.Fragment
+
+/** Abstract fragment for displaying the UI for a single ad preload format. */
+abstract class PreloadItemFragment : Fragment() {
+ /** Updates the UI for the fragment. */
+ abstract fun updateUI()
+}
diff --git a/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/RewardedFragment.kt b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/RewardedFragment.kt
new file mode 100644
index 000000000..d1d15f9cc
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/preloading/RewardedFragment.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.apidemo.preloading
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.google.android.gms.ads.rewarded.RewardedAd
+import com.google.android.gms.example.apidemo.MainActivity.Companion.LOG_TAG
+import com.google.android.gms.example.apidemo.R
+import com.google.android.gms.example.apidemo.databinding.FragmentPreloadItemBinding
+
+/** A [Fragment] subclass that preloads a rewarded ad. */
+class RewardedFragment : PreloadItemFragment() {
+
+ private lateinit var viewBinding: FragmentPreloadItemBinding
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ viewBinding = FragmentPreloadItemBinding.inflate(inflater, container, false)
+
+ initializeUI()
+
+ return viewBinding.root
+ }
+
+ private fun initializeUI() {
+ viewBinding.txtTitle.text = getText(R.string.preload_rewarded)
+ viewBinding.btnShow.setOnClickListener {
+ pollAndShowAd()
+ updateUI()
+ }
+ updateUI()
+ }
+
+ private fun pollAndShowAd() {
+ // Verify that a preloaded ad is available before polling for an ad.
+ if (!RewardedAd.isAdAvailable(requireContext(), AD_UNIT_ID)) {
+ Log.w(LOG_TAG, "Preloaded rewarded ad ${AD_UNIT_ID} is not available.")
+ return
+ }
+ // Polling returns the next available ad and load another ad in the background.
+ val ad = RewardedAd.pollAd(requireContext(), AD_UNIT_ID)
+ activity?.let { activity ->
+ ad?.show(activity) { reward ->
+ Log.d(LOG_TAG, "User was rewarded ${reward.amount} ${reward.type}")
+ }
+ }
+ }
+
+ @Synchronized
+ override fun updateUI() {
+ if (RewardedAd.isAdAvailable(requireContext(), AD_UNIT_ID)) {
+ viewBinding.txtStatus.text = getString(R.string.preload_available)
+ viewBinding.btnShow.isEnabled = true
+ } else {
+ viewBinding.txtStatus.text = getString(R.string.preload_exhausted)
+ viewBinding.btnShow.isEnabled = false
+ }
+ }
+
+ companion object {
+ const val AD_UNIT_ID = "ca-app-pub-3940256099942544/5224354917"
+ }
+}
diff --git a/kotlin/advanced/APIDemo/app/src/main/res/layout/fragment_preload_item.xml b/kotlin/advanced/APIDemo/app/src/main/res/layout/fragment_preload_item.xml
new file mode 100644
index 000000000..8351d7257
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/res/layout/fragment_preload_item.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/kotlin/advanced/APIDemo/app/src/main/res/layout/fragment_preload_main.xml b/kotlin/advanced/APIDemo/app/src/main/res/layout/fragment_preload_main.xml
new file mode 100644
index 000000000..26ad82927
--- /dev/null
+++ b/kotlin/advanced/APIDemo/app/src/main/res/layout/fragment_preload_main.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/kotlin/advanced/APIDemo/app/src/main/res/menu/activity_main_drawer.xml b/kotlin/advanced/APIDemo/app/src/main/res/menu/activity_main_drawer.xml
index 0a13a461f..803e4c2ed 100644
--- a/kotlin/advanced/APIDemo/app/src/main/res/menu/activity_main_drawer.xml
+++ b/kotlin/advanced/APIDemo/app/src/main/res/menu/activity_main_drawer.xml
@@ -20,6 +20,9 @@
+
diff --git a/kotlin/advanced/APIDemo/app/src/main/res/values/strings.xml b/kotlin/advanced/APIDemo/app/src/main/res/values/strings.xml
index 4a0b1b79e..30da69e55 100644
--- a/kotlin/advanced/APIDemo/app/src/main/res/values/strings.xml
+++ b/kotlin/advanced/APIDemo/app/src/main/res/values/strings.xml
@@ -83,6 +83,7 @@
AdMob - Ad TargetingAdMob - Banner SizesAdMob - Custom Mute This Ad
+ AdMob - Ad preloadingAd Manager - Fluid SizeAd Manager - PPIDAd Manager - Custom Targeting
@@ -120,4 +121,12 @@
MutePlayVideo Status:
+
+ Ad Preloading
+ Show Ad
+ Available
+ Exhausted
+ Interstitial Ad
+ Rewarded Ad
+ App Open Ad
diff --git a/kotlin/advanced/APIDemo/app/src/main/res/xml/security.xml b/kotlin/advanced/APIDemo/app/src/main/res/xml/network_security_config.xml
similarity index 100%
rename from kotlin/advanced/APIDemo/app/src/main/res/xml/security.xml
rename to kotlin/advanced/APIDemo/app/src/main/res/xml/network_security_config.xml
diff --git a/kotlin/advanced/APIDemo/build.gradle b/kotlin/advanced/APIDemo/build.gradle
index 391dd40aa..5a3454dc7 100644
--- a/kotlin/advanced/APIDemo/build.gradle
+++ b/kotlin/advanced/APIDemo/build.gradle
@@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.6.0'
+ classpath 'com.android.tools.build:gradle:8.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong