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 @@ + + + + + +