Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/tests_e2e_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ concurrency:

jobs:
android:
name: Android
name: Android (${{ matrix.google-mobile-ads-sdk }})
runs-on: ubuntu-latest
timeout-minutes: 70
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
EMULATOR_COMMAND: "-avd TestingAVD -noaudio -gpu swiftshader_indirect -camera-back none -no-snapshot -no-window -no-boot-anim -nojni -memory 2048 -timezone 'Europe/London' -cores 2"
EMULATOR_EXECUTABLE: qemu-system-x86_64-headless
# Picked up automatically by Gradle as the `googleMobileAdsSdk` project property - see
# android/build.gradle. Selects which Google Mobile Ads SDK react-native-google-mobile-ads
# builds against for this job.
ORG_GRADLE_PROJECT_googleMobileAdsSdk: ${{ matrix.google-mobile-ads-sdk }}
strategy:
fail-fast: false
matrix:
Expand All @@ -43,6 +47,8 @@ jobs:
first-boot-delay: [600]
# This is useful for benchmarking, do 0, 1, 2, etc (up to 256 max job-per-matrix limit) for averages
iteration: [0]
# Build/run the E2E suite against both supported Google Mobile Ads SDKs for Android.
google-mobile-ads-sdk: [legacy, next-gen]
steps:
- name: Liberate disk space
uses: jlumbroso/free-disk-space@main
Expand Down Expand Up @@ -215,5 +221,5 @@ jobs:
uses: actions/upload-artifact@v4
if: always()
with:
name: adb_logs
name: adb_logs-${{ matrix.google-mobile-ads-sdk }}
path: adb-log.txt
52 changes: 49 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ buildscript {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : version
}

// GMA Next-Gen SDK (ads-mobile-sdk) documents Kotlin 1.9+ as a minimum requirement, so the
// default here is unconditionally at least that, regardless of which SDK is selected - a newer
// Kotlin compiler is backwards compatible for the legacy SDK too, and this avoids needing to
// read the googleMobileAdsSdk flag this early (before app-json.gradle runs, further down).
def defaultKotlinVersion = '2.1.20'

// The Android Gradle plugin is only required when opening the android folder stand-alone.
// This avoids unnecessary downloads and potential conflicts when the library is included as a
// module dependency in an application project.
Expand All @@ -16,15 +22,15 @@ buildscript {

dependencies {
classpath("com.android.tools.build:gradle:7.0.4")
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion', '1.8.22')}"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion', defaultKotlinVersion)}"
}
} else {
repositories {
mavenCentral()
}

dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion', '1.8.22')}"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion', defaultKotlinVersion)}"
}
}
}
Expand All @@ -36,6 +42,7 @@ apply plugin: 'kotlin-android'

def packageJson = PackageJson.getForProject(project)
def googleMobileAdsVersion = packageJson['sdkVersions']['android']['googleMobileAds']
def googleMobileAdsNextGenVersion = packageJson['sdkVersions']['android']['googleMobileAdsNextGen']
def googleUmpVersion = packageJson['sdkVersions']['android']['googleUmp']
def jsonMinSdk = packageJson['sdkVersions']['android']['minSdk']
def jsonTargetSdk = packageJson['sdkVersions']['android']['targetSdk']
Expand Down Expand Up @@ -79,11 +86,40 @@ def appJSONGoogleMobileAdsDelayAppMeasurementInitBool = false
def appJSONGoogleMobileAdsOptimizeInitializationBool = true
def appJSONGoogleMobileAdsOptimizeAdLoadingBool = true

def appJSONGoogleMobileAdsSdkString = "legacy"

if (rootProject.ext.has("googleMobileAdsJson") && rootProject.ext.googleMobileAdsJson) {
appJSONGoogleMobileAdsAppIDString = rootProject.ext.googleMobileAdsJson.getStringValue("android_app_id", "")
appJSONGoogleMobileAdsDelayAppMeasurementInitBool = rootProject.ext.googleMobileAdsJson.isFlagEnabled("delay_app_measurement_init", false)
appJSONGoogleMobileAdsOptimizeInitializationBool = rootProject.ext.googleMobileAdsJson.isFlagEnabled("optimize_initialization", true)
appJSONGoogleMobileAdsOptimizeAdLoadingBool = rootProject.ext.googleMobileAdsJson.isFlagEnabled("optimize_ad_loading", true)
appJSONGoogleMobileAdsSdkString = rootProject.ext.googleMobileAdsJson.getStringValue("google_mobile_ads_sdk", "legacy")
}

// A gradle.properties override (googleMobileAdsSdk=next-gen) takes precedence over app.json,
// mirroring how newArchEnabled is read, for convenience when testing locally.
if (project.hasProperty("googleMobileAdsSdk")) {
appJSONGoogleMobileAdsSdkString = project.googleMobileAdsSdk
}

def isNextGenSdkEnabled = appJSONGoogleMobileAdsSdkString == "next-gen"

if (!["legacy", "next-gen"].contains(appJSONGoogleMobileAdsSdkString)) {
throw new GradleException(
"react-native-google-mobile-ads: invalid 'google_mobile_ads_sdk' value " +
"'${appJSONGoogleMobileAdsSdkString}' in app.json. Expected 'legacy' or 'next-gen'."
)
}

// GMA Next-Gen SDK (ads-mobile-sdk) ships classes in the same com.google.android.gms.ads.*
// package as Google Mobile Ads SDK (Legacy) (play-services-ads) and cannot be present in the
// same classpath at the same time - see
// https://developers.google.com/ad-manager/mobile-ads-sdk/android/early-access/nextgen/migration
if (isNextGenSdkEnabled) {
configurations.configureEach {
exclude group: "com.google.android.gms", module: "play-services-ads"
exclude group: "com.google.android.gms", module: "play-services-ads-lite"
}
}

if (!appJSONGoogleMobileAdsAppIDString) {
Expand All @@ -108,6 +144,7 @@ android {
appJSONGoogleMobileAdsOptimizeAdLoading : appJSONGoogleMobileAdsOptimizeAdLoadingBool
]
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
buildConfigField "boolean", "IS_NEXT_GEN_ADS_SDK_ENABLED", isNextGenSdkEnabled.toString()

sourceSets {
main {
Expand All @@ -116,6 +153,11 @@ android {
} else {
java.srcDirs += ['src/oldarch']
}
if (isNextGenSdkEnabled) {
java.srcDirs += ['src/nextgen']
} else {
java.srcDirs += ['src/legacy']
}
}
}
}
Expand All @@ -135,7 +177,11 @@ repositories {
}

dependencies {
implementation("com.google.android.gms:play-services-ads:${googleMobileAdsVersion}")
if (isNextGenSdkEnabled) {
implementation("com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:${googleMobileAdsNextGenVersion}")
} else {
implementation("com.google.android.gms:play-services-ads:${googleMobileAdsVersion}")
}
api "com.google.android.ump:user-messaging-platform:${googleUmpVersion}"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ class ReactNativeGoogleMobileAdsModule(
MobileAds.setAppMuted(muted)
}

override fun getConstants(): MutableMap<String, Any> {
return hashMapOf("googleMobileAdsSdk" to "legacy")
}

companion object {
const val NAME = "RNGoogleMobileAdsModule"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.invertase.googlemobileads

/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library 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.
*
*/

import android.app.Activity
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
import com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAd
import com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAdEventCallback
import com.google.android.libraries.ads.mobile.sdk.common.AdEventCallback
import com.google.android.libraries.ads.mobile.sdk.common.AdLoadCallback
import com.google.android.libraries.ads.mobile.sdk.common.AdRequest
import com.google.android.libraries.ads.mobile.sdk.rewarded.RewardItem

class ReactNativeGoogleMobileAdsAppOpenModule(reactContext: ReactApplicationContext?) :
ReactNativeGoogleMobileAdsFullScreenAdModule<AppOpenAd>(reactContext, NAME) {

override fun getAdEventName() = ReactNativeGoogleMobileAdsEvent.GOOGLE_MOBILE_ADS_EVENT_APP_OPEN

@ReactMethod
fun appOpenLoad(requestId: Int, adUnitId: String, adRequestOptions: ReadableMap) {
load(requestId, adUnitId, adRequestOptions)
}

@ReactMethod
fun appOpenShow(requestId: Int, adUnitId: String, showOptions: ReadableMap, promise: Promise) {
show(requestId, adUnitId, showOptions, promise)
}

override fun loadAd(adRequest: AdRequest, adLoadCallback: AdLoadCallback<AppOpenAd>) {
AppOpenAd.load(adRequest, adLoadCallback)
}

override fun attachAdEventCallback(ad: AppOpenAd, callback: SharedAdEventCallback) {
ad.adEventCallback = object : AppOpenAdEventCallback, AdEventCallback by callback {}
}

override fun showAd(ad: AppOpenAd, activity: Activity, onUserEarnedReward: (RewardItem) -> Unit) {
ad.show(activity)
}

override fun setImmersiveModeOnAd(ad: AppOpenAd, enabled: Boolean) {
ad.setImmersiveMode(enabled)
}

companion object {
const val NAME = "RNGoogleMobileAdsAppOpenModule"
}
}
Loading