react-native-liftoffads 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +20 -0
  2. package/NitroLiftoffAds.podspec +26 -0
  3. package/README.md +61 -0
  4. package/android/CMakeLists.txt +20 -0
  5. package/android/build.gradle +132 -0
  6. package/android/consumer-rules.pro +2 -0
  7. package/android/fix-prefab.gradle +46 -0
  8. package/android/gradle.properties +5 -0
  9. package/android/src/main/AndroidManifest.xml +1 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +7 -0
  11. package/android/src/main/java/com/margelo/nitro/liftoffads/HybridLiftoffAds.kt +121 -0
  12. package/android/src/main/java/com/margelo/nitro/liftoffads/HybridLiftoffInterstitialAd.kt +96 -0
  13. package/android/src/main/java/com/margelo/nitro/liftoffads/HybridLiftoffRewardedAd.kt +104 -0
  14. package/android/src/main/java/com/margelo/nitro/liftoffads/LiftoffAdEventEmitter.kt +51 -0
  15. package/android/src/main/java/com/margelo/nitro/liftoffads/LiftoffAndroidUtils.kt +54 -0
  16. package/android/src/main/java/com/margelo/nitro/liftoffads/NitroLiftoffAdsPackage.java +27 -0
  17. package/ios/HybridLiftoffAds.swift +113 -0
  18. package/ios/HybridLiftoffInterstitialAd.swift +98 -0
  19. package/ios/HybridLiftoffRewardedAd.swift +100 -0
  20. package/ios/LiftoffSupport.swift +106 -0
  21. package/lib/module/index.js +169 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/module/index.test.js +205 -0
  24. package/lib/module/index.test.js.map +1 -0
  25. package/lib/module/package.json +1 -0
  26. package/lib/module/specs/LiftoffAds.nitro.js +4 -0
  27. package/lib/module/specs/LiftoffAds.nitro.js.map +1 -0
  28. package/lib/typescript/package.json +1 -0
  29. package/lib/typescript/src/index.d.ts +72 -0
  30. package/lib/typescript/src/index.d.ts.map +1 -0
  31. package/lib/typescript/src/index.test.d.ts +2 -0
  32. package/lib/typescript/src/index.test.d.ts.map +1 -0
  33. package/lib/typescript/src/specs/LiftoffAds.nitro.d.ts +46 -0
  34. package/lib/typescript/src/specs/LiftoffAds.nitro.d.ts.map +1 -0
  35. package/nitro.json +18 -0
  36. package/nitrogen/generated/android/NitroLiftoffAds+autolinking.cmake +85 -0
  37. package/nitrogen/generated/android/NitroLiftoffAds+autolinking.gradle +27 -0
  38. package/nitrogen/generated/android/NitroLiftoffAdsOnLoad.cpp +50 -0
  39. package/nitrogen/generated/android/NitroLiftoffAdsOnLoad.hpp +25 -0
  40. package/nitrogen/generated/android/c++/JFunc_void_std__optional_LiftoffAdEventPayload_.hpp +78 -0
  41. package/nitrogen/generated/android/c++/JHybridLiftoffAdsSpec.cpp +88 -0
  42. package/nitrogen/generated/android/c++/JHybridLiftoffAdsSpec.hpp +69 -0
  43. package/nitrogen/generated/android/c++/JHybridLiftoffInterstitialAdSpec.cpp +89 -0
  44. package/nitrogen/generated/android/c++/JHybridLiftoffInterstitialAdSpec.hpp +68 -0
  45. package/nitrogen/generated/android/c++/JHybridLiftoffRewardedAdSpec.cpp +93 -0
  46. package/nitrogen/generated/android/c++/JHybridLiftoffRewardedAdSpec.hpp +69 -0
  47. package/nitrogen/generated/android/c++/JLiftoffAdEventPayload.hpp +58 -0
  48. package/nitrogen/generated/android/c++/JLiftoffAdEventType.hpp +77 -0
  49. package/nitrogen/generated/android/c++/JLiftoffAdShowOptions.hpp +53 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/Func_void_std__optional_LiftoffAdEventPayload_.kt +81 -0
  51. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/HybridLiftoffAdsSpec.kt +72 -0
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/HybridLiftoffInterstitialAdSpec.kt +73 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/HybridLiftoffRewardedAdSpec.kt +77 -0
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/LiftoffAdEventPayload.kt +32 -0
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/LiftoffAdEventType.kt +27 -0
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/LiftoffAdShowOptions.kt +29 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/liftoffads/NitroLiftoffAdsOnLoad.kt +35 -0
  58. package/nitrogen/generated/ios/NitroLiftoffAds+autolinking.rb +60 -0
  59. package/nitrogen/generated/ios/NitroLiftoffAds-Swift-Cxx-Bridge.cpp +90 -0
  60. package/nitrogen/generated/ios/NitroLiftoffAds-Swift-Cxx-Bridge.hpp +285 -0
  61. package/nitrogen/generated/ios/NitroLiftoffAds-Swift-Cxx-Umbrella.hpp +66 -0
  62. package/nitrogen/generated/ios/NitroLiftoffAdsAutolinking.mm +33 -0
  63. package/nitrogen/generated/ios/NitroLiftoffAdsAutolinking.swift +25 -0
  64. package/nitrogen/generated/ios/c++/HybridLiftoffAdsSpecSwift.cpp +11 -0
  65. package/nitrogen/generated/ios/c++/HybridLiftoffAdsSpecSwift.hpp +114 -0
  66. package/nitrogen/generated/ios/c++/HybridLiftoffInterstitialAdSpecSwift.cpp +11 -0
  67. package/nitrogen/generated/ios/c++/HybridLiftoffInterstitialAdSpecSwift.hpp +110 -0
  68. package/nitrogen/generated/ios/c++/HybridLiftoffRewardedAdSpecSwift.cpp +11 -0
  69. package/nitrogen/generated/ios/c++/HybridLiftoffRewardedAdSpecSwift.hpp +116 -0
  70. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  71. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  72. package/nitrogen/generated/ios/swift/Func_void_std__optional_LiftoffAdEventPayload_.swift +54 -0
  73. package/nitrogen/generated/ios/swift/HybridLiftoffAdsSpec.swift +54 -0
  74. package/nitrogen/generated/ios/swift/HybridLiftoffAdsSpec_cxx.swift +193 -0
  75. package/nitrogen/generated/ios/swift/HybridLiftoffInterstitialAdSpec.swift +53 -0
  76. package/nitrogen/generated/ios/swift/HybridLiftoffInterstitialAdSpec_cxx.swift +193 -0
  77. package/nitrogen/generated/ios/swift/HybridLiftoffRewardedAdSpec.swift +54 -0
  78. package/nitrogen/generated/ios/swift/HybridLiftoffRewardedAdSpec_cxx.swift +204 -0
  79. package/nitrogen/generated/ios/swift/LiftoffAdEventPayload.swift +77 -0
  80. package/nitrogen/generated/ios/swift/LiftoffAdEventType.swift +64 -0
  81. package/nitrogen/generated/ios/swift/LiftoffAdShowOptions.swift +47 -0
  82. package/nitrogen/generated/shared/c++/HybridLiftoffAdsSpec.cpp +26 -0
  83. package/nitrogen/generated/shared/c++/HybridLiftoffAdsSpec.hpp +74 -0
  84. package/nitrogen/generated/shared/c++/HybridLiftoffInterstitialAdSpec.cpp +25 -0
  85. package/nitrogen/generated/shared/c++/HybridLiftoffInterstitialAdSpec.hpp +76 -0
  86. package/nitrogen/generated/shared/c++/HybridLiftoffRewardedAdSpec.cpp +26 -0
  87. package/nitrogen/generated/shared/c++/HybridLiftoffRewardedAdSpec.hpp +78 -0
  88. package/nitrogen/generated/shared/c++/LiftoffAdEventPayload.hpp +72 -0
  89. package/nitrogen/generated/shared/c++/LiftoffAdEventType.hpp +100 -0
  90. package/nitrogen/generated/shared/c++/LiftoffAdShowOptions.hpp +67 -0
  91. package/package.json +173 -0
  92. package/react-native.config.js +8 -0
  93. package/src/index.test.ts +257 -0
  94. package/src/index.ts +307 -0
  95. package/src/specs/LiftoffAds.nitro.ts +67 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 exzos
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,26 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "NitroLiftoffAds"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = "https://github.com/exzos28/react-native-liftoffads"
10
+ s.license = { :type => "MIT" }
11
+ s.authors = { "exzos" => "oleksandr.kurinnyi.work@gmail.com" }
12
+ s.platforms = { :ios => "15.1" }
13
+ s.source = { :path => "." }
14
+
15
+ s.source_files = ["ios/**/*.{swift,m,mm}"]
16
+
17
+ load "nitrogen/generated/ios/NitroLiftoffAds+autolinking.rb"
18
+ add_nitrogen_files(s)
19
+
20
+ s.dependency "React-Core"
21
+ s.dependency "React-jsi"
22
+ s.dependency "React-callinvoker"
23
+ s.dependency "VungleAds", "7.7.5"
24
+
25
+ install_modules_dependencies(s)
26
+ end
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # react-native-liftoffads
2
+
3
+ [Nitro Modules](https://nitro.margelo.com/) bridge for Liftoff Monetize
4
+ (Vungle) interstitial and rewarded ads.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install react-native-liftoffads react-native-nitro-modules
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ts
15
+ import MobileAds, {
16
+ AdEventType,
17
+ InterstitialAd,
18
+ RewardedAd,
19
+ RewardedAdEventType,
20
+ } from 'react-native-liftoffads';
21
+
22
+ await MobileAds().initialize('YOUR_LIFTOFF_APP_ID');
23
+ MobileAds().setGDPRConsent(true, '1.0');
24
+ MobileAds().setCCPAConsent(true);
25
+ MobileAds().setCOPPA(false);
26
+
27
+ const interstitial = InterstitialAd.createForAdRequest('YOUR_PLACEMENT_ID');
28
+ interstitial.addAdEventListener(AdEventType.LOADED, () => {
29
+ interstitial.show({immersiveModeEnabled: true});
30
+ });
31
+ interstitial.addAdEventListener(AdEventType.CLOSED, () => {
32
+ interstitial.load(); // reuse the same instance for the next impression
33
+ });
34
+ interstitial.load();
35
+
36
+ const rewarded = RewardedAd.createForAdRequest('YOUR_PLACEMENT_ID');
37
+ rewarded.setUserId('your-user-id'); // optional, for SSV
38
+ rewarded.addAdEventListener(RewardedAdEventType.LOADED, () => rewarded.show());
39
+ rewarded.addAdEventListener(RewardedAdEventType.EARNED_REWARD, () => {
40
+ // grant the reward
41
+ });
42
+ rewarded.load();
43
+ ```
44
+
45
+ ## Notes
46
+
47
+ - Ad instances are meant to be reused — call `load()` again on the same
48
+ `InterstitialAd`/`RewardedAd` after `CLOSED` instead of creating a new one.
49
+ - Liftoff doesn't report reward type/amount, so `LOADED`/`EARNED_REWARD`
50
+ payloads are always `undefined` — treat them as signals only.
51
+ - Use `RewardedAdEventType.LOADED`, not `AdEventType.LOADED`, on a
52
+ `RewardedAd`.
53
+ - No built-in consent UI (no UMP equivalent) — plug your own and forward the
54
+ result via `setGDPRConsent`/`setCCPAConsent`/`setCOPPA`.
55
+
56
+ See [`src/specs/LiftoffAds.nitro.ts`](src/specs/LiftoffAds.nitro.ts) for the
57
+ full native interface and [`example`](example) for a runnable demo.
58
+
59
+ ## License
60
+
61
+ MIT
@@ -0,0 +1,20 @@
1
+ project(NitroLiftoffAds)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME NitroLiftoffAds)
5
+ set(CMAKE_VERBOSE_MAKEFILE ON)
6
+ set(CMAKE_CXX_STANDARD 20)
7
+
8
+ add_library(${PACKAGE_NAME} SHARED
9
+ src/main/cpp/cpp-adapter.cpp
10
+ )
11
+
12
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroLiftoffAds+autolinking.cmake)
13
+
14
+ find_library(LOG_LIB log)
15
+
16
+ target_link_libraries(
17
+ ${PACKAGE_NAME}
18
+ ${LOG_LIB}
19
+ android
20
+ )
@@ -0,0 +1,132 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:8.12.1"
9
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.20"
10
+ }
11
+ }
12
+
13
+ def reactNativeArchitectures() {
14
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
15
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
16
+ }
17
+
18
+ def isNewArchitectureEnabled() {
19
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
20
+ }
21
+
22
+ apply plugin: "com.android.library"
23
+ apply plugin: "org.jetbrains.kotlin.android"
24
+
25
+ def nitroAutolinkingFile = file("../nitrogen/generated/android/NitroLiftoffAds+autolinking.gradle")
26
+ if (nitroAutolinkingFile.exists()) {
27
+ apply from: nitroAutolinkingFile
28
+ }
29
+ apply from: "./fix-prefab.gradle"
30
+
31
+ def getExtOrDefault(name) {
32
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroLiftoffAds_" + name]
33
+ }
34
+
35
+ def getExtOrIntegerDefault(name) {
36
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroLiftoffAds_" + name]).toInteger()
37
+ }
38
+
39
+ android {
40
+ namespace "com.margelo.nitro.liftoffads"
41
+
42
+ ndkVersion getExtOrDefault("ndkVersion")
43
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
44
+
45
+ defaultConfig {
46
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
47
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
48
+ consumerProguardFiles "consumer-rules.pro"
49
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
50
+
51
+ externalNativeBuild {
52
+ cmake {
53
+ cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
54
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
55
+ abiFilters (*reactNativeArchitectures())
56
+
57
+ buildTypes {
58
+ debug {
59
+ cppFlags "-O1 -g"
60
+ }
61
+ release {
62
+ cppFlags "-O2"
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ externalNativeBuild {
70
+ cmake {
71
+ path "CMakeLists.txt"
72
+ }
73
+ }
74
+
75
+ packagingOptions {
76
+ excludes = [
77
+ "META-INF",
78
+ "META-INF/**",
79
+ "**/libc++_shared.so",
80
+ "**/libfbjni.so",
81
+ "**/libjsi.so",
82
+ "**/libfolly_json.so",
83
+ "**/libfolly_runtime.so",
84
+ "**/libglog.so",
85
+ "**/libhermes.so",
86
+ "**/libreactnative.so",
87
+ "**/libreactnativejni.so"
88
+ ]
89
+ }
90
+
91
+ buildFeatures {
92
+ buildConfig true
93
+ prefab true
94
+ }
95
+
96
+ buildTypes {
97
+ release {
98
+ minifyEnabled false
99
+ }
100
+ }
101
+
102
+ lintOptions {
103
+ disable "GradleCompatible"
104
+ }
105
+
106
+ compileOptions {
107
+ sourceCompatibility JavaVersion.VERSION_17
108
+ targetCompatibility JavaVersion.VERSION_17
109
+ }
110
+
111
+ kotlinOptions {
112
+ jvmTarget = "17"
113
+ }
114
+
115
+ }
116
+
117
+ repositories {
118
+ google()
119
+ mavenCentral()
120
+ }
121
+
122
+ dependencies {
123
+ implementation "com.facebook.react:react-android"
124
+ implementation project(":react-native-nitro-modules")
125
+
126
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
127
+ implementation "com.vungle:vungle-ads:7.7.7"
128
+ implementation "com.google.android.gms:play-services-tasks:18.2.1"
129
+ implementation "com.google.android.gms:play-services-appset:16.1.0"
130
+ implementation "com.google.android.gms:play-services-basement:18.6.0"
131
+ implementation "com.google.android.gms:play-services-ads-identifier:18.2.0"
132
+ }
@@ -0,0 +1,2 @@
1
+ -keep class com.margelo.nitro.liftoffads.** { *; }
2
+ -keep class com.vungle.ads.** { *; }
@@ -0,0 +1,46 @@
1
+ tasks.configureEach { task ->
2
+ def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
3
+ def matcher = task.name =~ prefabConfigurePattern
4
+ if (matcher.matches()) {
5
+ def variantName = matcher[0][1]
6
+ task.outputs.upToDateWhen { false }
7
+ task.dependsOn("externalNativeBuild${variantName}")
8
+ }
9
+ }
10
+
11
+ afterEvaluate {
12
+ def abis = reactNativeArchitectures()
13
+ rootProject.allprojects.each { proj ->
14
+ if (proj === rootProject) return
15
+
16
+ def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config ->
17
+ config.dependencies.any { dep ->
18
+ dep.group == project.group && dep.name == project.name
19
+ }
20
+ }
21
+ if (!dependsOnThisLib && proj != project) return
22
+
23
+ if (!proj.plugins.hasPlugin("com.android.application") &&
24
+ !proj.plugins.hasPlugin("com.android.library")) {
25
+ return
26
+ }
27
+
28
+ def variants = proj.android.hasProperty("applicationVariants") ?
29
+ proj.android.applicationVariants : proj.android.libraryVariants
30
+ variants.all { variant ->
31
+ def variantName = variant.name
32
+ abis.each { abi ->
33
+ def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
34
+ if (!searchDir.exists()) return
35
+ def matches = []
36
+ searchDir.eachDir { randomDir ->
37
+ def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
38
+ if (prefabFile.exists()) matches << prefabFile
39
+ }
40
+ matches.each { prefabConfig ->
41
+ prefabConfig.setLastModified(System.currentTimeMillis())
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,5 @@
1
+ NitroLiftoffAds_kotlinVersion=2.1.20
2
+ NitroLiftoffAds_minSdkVersion=24
3
+ NitroLiftoffAds_targetSdkVersion=36
4
+ NitroLiftoffAds_compileSdkVersion=36
5
+ NitroLiftoffAds_ndkVersion=27.1.12297006
@@ -0,0 +1 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -0,0 +1,7 @@
1
+ #include <jni.h>
2
+
3
+ #include "NitroLiftoffAdsOnLoad.hpp"
4
+
5
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
6
+ return margelo::nitro::liftoffads::initialize(vm);
7
+ }
@@ -0,0 +1,121 @@
1
+ package com.margelo.nitro.liftoffads
2
+
3
+ import androidx.annotation.Keep
4
+ import com.facebook.proguard.annotations.DoNotStrip
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.margelo.nitro.NitroModules
7
+ import com.margelo.nitro.core.Promise
8
+ import com.vungle.ads.InitializationListener
9
+ import com.vungle.ads.VungleAds
10
+ import com.vungle.ads.VungleError
11
+ import com.vungle.ads.VunglePrivacySettings
12
+ import kotlinx.coroutines.CompletableDeferred
13
+ import kotlinx.coroutines.Dispatchers
14
+ import kotlinx.coroutines.withContext
15
+
16
+ @DoNotStrip
17
+ @Keep
18
+ class HybridLiftoffAds : HybridLiftoffAdsSpec() {
19
+ private val context: ReactApplicationContext by lazy {
20
+ NitroModules.applicationContext as? ReactApplicationContext
21
+ ?: error("Liftoff Ads requires a ReactApplicationContext")
22
+ }
23
+
24
+ private val stateLock = Any()
25
+ private var initializedAppId: String? = null
26
+ private var initializingAppId: String? = null
27
+ private var initialization: CompletableDeferred<Unit>? = null
28
+
29
+ override fun initialize(appId: String): Promise<Unit> = Promise.async {
30
+ require(appId.isNotBlank()) { "Liftoff app ID is empty" }
31
+
32
+ val alreadyInitialized = synchronized(stateLock) {
33
+ if (VungleAds.isInitialized()) {
34
+ val existing = initializedAppId
35
+ if (existing != null && existing != appId) {
36
+ error("Liftoff SDK was already initialized with another app ID")
37
+ }
38
+ initializedAppId = appId
39
+ true
40
+ } else {
41
+ false
42
+ }
43
+ }
44
+ if (alreadyInitialized) return@async
45
+
46
+ val (deferred, shouldStart) = synchronized(stateLock) {
47
+ val current = initialization
48
+ if (current != null) {
49
+ if (initializingAppId != appId) {
50
+ error("Liftoff SDK is being initialized with another app ID")
51
+ }
52
+ current to false
53
+ } else {
54
+ CompletableDeferred<Unit>().also {
55
+ initialization = it
56
+ initializingAppId = appId
57
+ } to true
58
+ }
59
+ }
60
+
61
+ if (shouldStart) {
62
+ withContext(Dispatchers.Main) {
63
+ VungleAds.init(
64
+ context.applicationContext,
65
+ appId,
66
+ object : InitializationListener {
67
+ override fun onSuccess() {
68
+ synchronized(stateLock) {
69
+ initializedAppId = appId
70
+ initialization = null
71
+ initializingAppId = null
72
+ }
73
+ deferred.complete(Unit)
74
+ }
75
+
76
+ override fun onError(vungleError: VungleError) {
77
+ synchronized(stateLock) {
78
+ if (initialization === deferred) initialization = null
79
+ initializingAppId = null
80
+ }
81
+ deferred.completeExceptionally(
82
+ liftoffException("initialize", vungleError),
83
+ )
84
+ }
85
+ },
86
+ )
87
+ }
88
+ }
89
+
90
+ deferred.await()
91
+ }
92
+
93
+ override fun setGDPRConsent(optIn: Boolean, consentMessageVersion: String) {
94
+ VunglePrivacySettings.setGDPRStatus(optIn, consentMessageVersion)
95
+ }
96
+
97
+ override fun setCCPAConsent(optIn: Boolean) {
98
+ VunglePrivacySettings.setCCPAStatus(optIn)
99
+ }
100
+
101
+ override fun setCOPPA(isUserCoppa: Boolean) {
102
+ VunglePrivacySettings.setCOPPAStatus(isUserCoppa)
103
+ }
104
+
105
+ override fun createInterstitialAd(
106
+ placementId: String,
107
+ ): HybridLiftoffInterstitialAdSpec {
108
+ requireInitialized(placementId)
109
+ return HybridLiftoffInterstitialAd(context, placementId)
110
+ }
111
+
112
+ override fun createRewardedAd(placementId: String): HybridLiftoffRewardedAdSpec {
113
+ requireInitialized(placementId)
114
+ return HybridLiftoffRewardedAd(context, placementId)
115
+ }
116
+
117
+ private fun requireInitialized(placementId: String) {
118
+ require(VungleAds.isInitialized()) { "Liftoff SDK is not initialized" }
119
+ require(placementId.isNotBlank()) { "Liftoff placement ID is empty" }
120
+ }
121
+ }
@@ -0,0 +1,96 @@
1
+ package com.margelo.nitro.liftoffads
2
+
3
+ import androidx.annotation.Keep
4
+ import com.facebook.proguard.annotations.DoNotStrip
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.margelo.nitro.core.Promise
7
+ import com.vungle.ads.AdConfig
8
+ import com.vungle.ads.BaseAd
9
+ import com.vungle.ads.InterstitialAd
10
+ import com.vungle.ads.InterstitialAdListener
11
+ import com.vungle.ads.VungleError
12
+ import kotlinx.coroutines.Dispatchers
13
+ import kotlinx.coroutines.withContext
14
+
15
+ @DoNotStrip
16
+ @Keep
17
+ class HybridLiftoffInterstitialAd(
18
+ private val context: ReactApplicationContext,
19
+ placementId: String,
20
+ ) : HybridLiftoffInterstitialAdSpec(), InterstitialAdListener {
21
+ private val events = LiftoffAdEventEmitter()
22
+ private val immersiveMode = ImmersiveModeController()
23
+ private val ad = InterstitialAd(context, placementId, AdConfig())
24
+
25
+ init {
26
+ ad.adListener = this
27
+ }
28
+
29
+ override fun load() {
30
+ context.runOnUiQueueThread { ad.load() }
31
+ }
32
+
33
+ override fun show(options: LiftoffAdShowOptions?): Promise<Unit> = Promise.async {
34
+ try {
35
+ withContext(Dispatchers.Main) {
36
+ val activity = context.requireCurrentActivity()
37
+ if (!ad.canPlayAd()) error("Liftoff interstitial ad is not ready")
38
+ if (options?.immersiveModeEnabled == true) immersiveMode.enable(activity)
39
+ ad.play(activity)
40
+ }
41
+ } catch (error: Throwable) {
42
+ immersiveMode.restore()
43
+ events.emit(LiftoffAdEventType.ERROR, error.toLiftoffEventPayload())
44
+ throw error
45
+ }
46
+ }
47
+
48
+ override fun addAdEventListener(
49
+ eventType: LiftoffAdEventType,
50
+ listener: (payload: LiftoffAdEventPayload?) -> Unit,
51
+ ) = events.add(eventType, listener)
52
+
53
+ override fun removeAdEventListener(subscriptionId: Double) {
54
+ events.remove(subscriptionId)
55
+ }
56
+
57
+ override fun removeAllListeners() {
58
+ events.removeAll()
59
+ }
60
+
61
+ override fun onAdLoaded(baseAd: BaseAd) {
62
+ events.emit(LiftoffAdEventType.LOADED)
63
+ }
64
+
65
+ override fun onAdStart(baseAd: BaseAd) {
66
+ events.emit(LiftoffAdEventType.OPENED)
67
+ }
68
+
69
+ override fun onAdImpression(baseAd: BaseAd) = Unit
70
+
71
+ override fun onAdClicked(baseAd: BaseAd) {
72
+ events.emit(LiftoffAdEventType.CLICKED)
73
+ }
74
+
75
+ override fun onAdLeftApplication(baseAd: BaseAd) = Unit
76
+
77
+ override fun onAdEnd(baseAd: BaseAd) {
78
+ immersiveMode.restore()
79
+ events.emit(LiftoffAdEventType.CLOSED)
80
+ }
81
+
82
+ override fun onAdFailedToLoad(baseAd: BaseAd, adError: VungleError) {
83
+ events.emit(
84
+ LiftoffAdEventType.ERROR,
85
+ liftoffException("load", adError).toLiftoffEventPayload(),
86
+ )
87
+ }
88
+
89
+ override fun onAdFailedToPlay(baseAd: BaseAd, adError: VungleError) {
90
+ immersiveMode.restore()
91
+ events.emit(
92
+ LiftoffAdEventType.ERROR,
93
+ liftoffException("show", adError).toLiftoffEventPayload(),
94
+ )
95
+ }
96
+ }
@@ -0,0 +1,104 @@
1
+ package com.margelo.nitro.liftoffads
2
+
3
+ import androidx.annotation.Keep
4
+ import com.facebook.proguard.annotations.DoNotStrip
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.margelo.nitro.core.Promise
7
+ import com.vungle.ads.AdConfig
8
+ import com.vungle.ads.BaseAd
9
+ import com.vungle.ads.RewardedAd
10
+ import com.vungle.ads.RewardedAdListener
11
+ import com.vungle.ads.VungleError
12
+ import kotlinx.coroutines.Dispatchers
13
+ import kotlinx.coroutines.withContext
14
+
15
+ @DoNotStrip
16
+ @Keep
17
+ class HybridLiftoffRewardedAd(
18
+ private val context: ReactApplicationContext,
19
+ placementId: String,
20
+ ) : HybridLiftoffRewardedAdSpec(), RewardedAdListener {
21
+ private val events = LiftoffAdEventEmitter()
22
+ private val immersiveMode = ImmersiveModeController()
23
+ private val ad = RewardedAd(context, placementId, AdConfig())
24
+
25
+ init {
26
+ ad.adListener = this
27
+ }
28
+
29
+ override fun load() {
30
+ context.runOnUiQueueThread { ad.load() }
31
+ }
32
+
33
+ override fun setUserId(userId: String) {
34
+ context.runOnUiQueueThread { ad.setUserId(userId) }
35
+ }
36
+
37
+ override fun show(options: LiftoffAdShowOptions?): Promise<Unit> = Promise.async {
38
+ try {
39
+ withContext(Dispatchers.Main) {
40
+ val activity = context.requireCurrentActivity()
41
+ if (!ad.canPlayAd()) error("Liftoff rewarded ad is not ready")
42
+ if (options?.immersiveModeEnabled == true) immersiveMode.enable(activity)
43
+ ad.play(activity)
44
+ }
45
+ } catch (error: Throwable) {
46
+ immersiveMode.restore()
47
+ events.emit(LiftoffAdEventType.ERROR, error.toLiftoffEventPayload())
48
+ throw error
49
+ }
50
+ }
51
+
52
+ override fun addAdEventListener(
53
+ eventType: LiftoffAdEventType,
54
+ listener: (payload: LiftoffAdEventPayload?) -> Unit,
55
+ ) = events.add(eventType, listener)
56
+
57
+ override fun removeAdEventListener(subscriptionId: Double) {
58
+ events.remove(subscriptionId)
59
+ }
60
+
61
+ override fun removeAllListeners() {
62
+ events.removeAll()
63
+ }
64
+
65
+ override fun onAdLoaded(baseAd: BaseAd) {
66
+ events.emit(LiftoffAdEventType.REWARDED_LOADED)
67
+ }
68
+
69
+ override fun onAdStart(baseAd: BaseAd) {
70
+ events.emit(LiftoffAdEventType.OPENED)
71
+ }
72
+
73
+ override fun onAdImpression(baseAd: BaseAd) = Unit
74
+
75
+ override fun onAdClicked(baseAd: BaseAd) {
76
+ events.emit(LiftoffAdEventType.CLICKED)
77
+ }
78
+
79
+ override fun onAdLeftApplication(baseAd: BaseAd) = Unit
80
+
81
+ override fun onAdRewarded(baseAd: BaseAd) {
82
+ events.emit(LiftoffAdEventType.REWARDED_EARNED_REWARD)
83
+ }
84
+
85
+ override fun onAdEnd(baseAd: BaseAd) {
86
+ immersiveMode.restore()
87
+ events.emit(LiftoffAdEventType.CLOSED)
88
+ }
89
+
90
+ override fun onAdFailedToLoad(baseAd: BaseAd, adError: VungleError) {
91
+ events.emit(
92
+ LiftoffAdEventType.ERROR,
93
+ liftoffException("load", adError).toLiftoffEventPayload(),
94
+ )
95
+ }
96
+
97
+ override fun onAdFailedToPlay(baseAd: BaseAd, adError: VungleError) {
98
+ immersiveMode.restore()
99
+ events.emit(
100
+ LiftoffAdEventType.ERROR,
101
+ liftoffException("show", adError).toLiftoffEventPayload(),
102
+ )
103
+ }
104
+ }