react-native-pangleads 0.1.0

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/NitroPangleAds.podspec +26 -0
  3. package/README.md +80 -0
  4. package/android/CMakeLists.txt +20 -0
  5. package/android/build.gradle +129 -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/pangleads/HybridPangleAds.kt +123 -0
  12. package/android/src/main/java/com/margelo/nitro/pangleads/HybridPangleInterstitialAd.kt +106 -0
  13. package/android/src/main/java/com/margelo/nitro/pangleads/HybridPangleRewardedAd.kt +139 -0
  14. package/android/src/main/java/com/margelo/nitro/pangleads/NitroPangleAdsPackage.java +27 -0
  15. package/android/src/main/java/com/margelo/nitro/pangleads/PangleAdEventEmitter.kt +41 -0
  16. package/android/src/main/java/com/margelo/nitro/pangleads/PangleAndroidUtils.kt +32 -0
  17. package/ios/HybridPangleAds.swift +120 -0
  18. package/ios/HybridPangleInterstitialAd.swift +110 -0
  19. package/ios/HybridPangleRewardedAd.swift +143 -0
  20. package/ios/PangleSupport.swift +129 -0
  21. package/lib/module/index.js +172 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/module/index.test.js +201 -0
  24. package/lib/module/index.test.js.map +1 -0
  25. package/lib/module/package.json +1 -0
  26. package/lib/module/specs/PangleAds.nitro.js +4 -0
  27. package/lib/module/specs/PangleAds.nitro.js.map +1 -0
  28. package/lib/typescript/package.json +1 -0
  29. package/lib/typescript/src/index.d.ts +68 -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/PangleAds.nitro.d.ts +46 -0
  34. package/lib/typescript/src/specs/PangleAds.nitro.d.ts.map +1 -0
  35. package/nitro.json +18 -0
  36. package/nitrogen/generated/android/NitroPangleAds+autolinking.cmake +85 -0
  37. package/nitrogen/generated/android/NitroPangleAds+autolinking.gradle +27 -0
  38. package/nitrogen/generated/android/NitroPangleAdsOnLoad.cpp +50 -0
  39. package/nitrogen/generated/android/NitroPangleAdsOnLoad.hpp +25 -0
  40. package/nitrogen/generated/android/c++/JFunc_void_std__optional_PangleAdEventPayload_.hpp +78 -0
  41. package/nitrogen/generated/android/c++/JHybridPangleAdsSpec.cpp +88 -0
  42. package/nitrogen/generated/android/c++/JHybridPangleAdsSpec.hpp +69 -0
  43. package/nitrogen/generated/android/c++/JHybridPangleInterstitialAdSpec.cpp +85 -0
  44. package/nitrogen/generated/android/c++/JHybridPangleInterstitialAdSpec.hpp +68 -0
  45. package/nitrogen/generated/android/c++/JHybridPangleRewardedAdSpec.cpp +89 -0
  46. package/nitrogen/generated/android/c++/JHybridPangleRewardedAdSpec.hpp +68 -0
  47. package/nitrogen/generated/android/c++/JPangleAdEventPayload.hpp +58 -0
  48. package/nitrogen/generated/android/c++/JPangleAdEventType.hpp +74 -0
  49. package/nitrogen/generated/android/c++/JPangleAdVerificationOptions.hpp +58 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/Func_void_std__optional_PangleAdEventPayload_.kt +81 -0
  51. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/HybridPangleAdsSpec.kt +72 -0
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/HybridPangleInterstitialAdSpec.kt +73 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/HybridPangleRewardedAdSpec.kt +73 -0
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/NitroPangleAdsOnLoad.kt +35 -0
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/PangleAdEventPayload.kt +32 -0
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/PangleAdEventType.kt +26 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/PangleAdVerificationOptions.kt +32 -0
  58. package/nitrogen/generated/ios/NitroPangleAds+autolinking.rb +60 -0
  59. package/nitrogen/generated/ios/NitroPangleAds-Swift-Cxx-Bridge.cpp +90 -0
  60. package/nitrogen/generated/ios/NitroPangleAds-Swift-Cxx-Bridge.hpp +270 -0
  61. package/nitrogen/generated/ios/NitroPangleAds-Swift-Cxx-Umbrella.hpp +66 -0
  62. package/nitrogen/generated/ios/NitroPangleAdsAutolinking.mm +33 -0
  63. package/nitrogen/generated/ios/NitroPangleAdsAutolinking.swift +25 -0
  64. package/nitrogen/generated/ios/c++/HybridPangleAdsSpecSwift.cpp +11 -0
  65. package/nitrogen/generated/ios/c++/HybridPangleAdsSpecSwift.hpp +114 -0
  66. package/nitrogen/generated/ios/c++/HybridPangleInterstitialAdSpecSwift.cpp +11 -0
  67. package/nitrogen/generated/ios/c++/HybridPangleInterstitialAdSpecSwift.hpp +107 -0
  68. package/nitrogen/generated/ios/c++/HybridPangleRewardedAdSpecSwift.cpp +11 -0
  69. package/nitrogen/generated/ios/c++/HybridPangleRewardedAdSpecSwift.hpp +110 -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_PangleAdEventPayload_.swift +54 -0
  73. package/nitrogen/generated/ios/swift/HybridPangleAdsSpec.swift +54 -0
  74. package/nitrogen/generated/ios/swift/HybridPangleAdsSpec_cxx.swift +193 -0
  75. package/nitrogen/generated/ios/swift/HybridPangleInterstitialAdSpec.swift +53 -0
  76. package/nitrogen/generated/ios/swift/HybridPangleInterstitialAdSpec_cxx.swift +186 -0
  77. package/nitrogen/generated/ios/swift/HybridPangleRewardedAdSpec.swift +53 -0
  78. package/nitrogen/generated/ios/swift/HybridPangleRewardedAdSpec_cxx.swift +193 -0
  79. package/nitrogen/generated/ios/swift/PangleAdEventPayload.swift +77 -0
  80. package/nitrogen/generated/ios/swift/PangleAdEventType.swift +60 -0
  81. package/nitrogen/generated/ios/swift/PangleAdVerificationOptions.swift +84 -0
  82. package/nitrogen/generated/shared/c++/HybridPangleAdsSpec.cpp +26 -0
  83. package/nitrogen/generated/shared/c++/HybridPangleAdsSpec.hpp +74 -0
  84. package/nitrogen/generated/shared/c++/HybridPangleInterstitialAdSpec.cpp +25 -0
  85. package/nitrogen/generated/shared/c++/HybridPangleInterstitialAdSpec.hpp +73 -0
  86. package/nitrogen/generated/shared/c++/HybridPangleRewardedAdSpec.cpp +25 -0
  87. package/nitrogen/generated/shared/c++/HybridPangleRewardedAdSpec.hpp +76 -0
  88. package/nitrogen/generated/shared/c++/PangleAdEventPayload.hpp +72 -0
  89. package/nitrogen/generated/shared/c++/PangleAdEventType.hpp +96 -0
  90. package/nitrogen/generated/shared/c++/PangleAdVerificationOptions.hpp +72 -0
  91. package/package.json +177 -0
  92. package/react-native.config.js +8 -0
  93. package/src/index.test.ts +255 -0
  94. package/src/index.ts +305 -0
  95. package/src/specs/PangleAds.nitro.ts +66 -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 = "NitroPangleAds"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = "https://github.com/exzos28/react-native-pangleads"
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/NitroPangleAds+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 "Ads-Global/PangleSDK", "8.2.0.7"
24
+
25
+ install_modules_dependencies(s)
26
+ end
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # react-native-pangleads
2
+
3
+ [Nitro Modules](https://nitro.margelo.com/) bridge for Pangle interstitial
4
+ and rewarded ads.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install react-native-pangleads react-native-nitro-modules
10
+ ```
11
+
12
+ Pangle's Android SDK lives outside Google's default Maven repos. With Expo
13
+ CNG, enable the bundled config plugin:
14
+
15
+ ```json
16
+ {
17
+ "expo": {
18
+ "plugins": ["react-native-pangleads"]
19
+ }
20
+ }
21
+ ```
22
+
23
+ Without CNG, add the repo to your app's `android/build.gradle` yourself:
24
+
25
+ ```groovy
26
+ allprojects {
27
+ repositories {
28
+ maven { url "https://artifact.bytedance.com/repository/pangle" }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```ts
36
+ import MobileAds, {
37
+ AdEventType,
38
+ InterstitialAd,
39
+ RewardedAd,
40
+ RewardedAdEventType,
41
+ } from 'react-native-pangleads';
42
+
43
+ await MobileAds().initialize('YOUR_PANGLE_APP_ID');
44
+ MobileAds().setGDPRConsent(true);
45
+ MobileAds().setCCPAConsent(true);
46
+ MobileAds().setCOPPA(false);
47
+
48
+ const interstitial = InterstitialAd.createForAdRequest('YOUR_PLACEMENT_ID');
49
+ interstitial.addAdEventListener(AdEventType.LOADED, () => interstitial.show());
50
+ interstitial.addAdEventListener(AdEventType.CLOSED, () => {
51
+ interstitial.load(); // reuse the same instance for the next impression
52
+ });
53
+ interstitial.load();
54
+
55
+ const rewarded = RewardedAd.createForAdRequest('YOUR_PLACEMENT_ID');
56
+ rewarded.addAdEventListener(RewardedAdEventType.LOADED, () => rewarded.show());
57
+ rewarded.addAdEventListener(RewardedAdEventType.EARNED_REWARD, () => {
58
+ // grant the reward
59
+ });
60
+ rewarded.load({userId: 'your-user-id'}); // optional, for SSV
61
+ ```
62
+
63
+ ## Notes
64
+
65
+ - Ad instances are meant to be reused — call `load()` again on the same
66
+ `InterstitialAd`/`RewardedAd` after `CLOSED` instead of creating a new one.
67
+ - No `AdEventType.PAID` event (unlike Liftoff/Google Mobile Ads).
68
+ - Pangle doesn't report reward type/amount, so `LOADED`/`EARNED_REWARD`
69
+ payloads are always `undefined` — treat them as signals only.
70
+ - Use `RewardedAdEventType.LOADED`, not `AdEventType.LOADED`, on a
71
+ `RewardedAd`.
72
+ - No built-in consent UI (no UMP equivalent) — plug your own and forward the
73
+ result via `setGDPRConsent`/`setCCPAConsent`/`setCOPPA`.
74
+
75
+ See [`src/specs/PangleAds.nitro.ts`](src/specs/PangleAds.nitro.ts) for the
76
+ full native interface and [`example`](example) for a runnable demo.
77
+
78
+ ## License
79
+
80
+ MIT
@@ -0,0 +1,20 @@
1
+ project(NitroPangleAds)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME NitroPangleAds)
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/NitroPangleAds+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,129 @@
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/NitroPangleAds+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["NitroPangleAds_" + name]
33
+ }
34
+
35
+ def getExtOrIntegerDefault(name) {
36
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroPangleAds_" + name]).toInteger()
37
+ }
38
+
39
+ android {
40
+ namespace "com.margelo.nitro.pangleads"
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
+ maven { url "https://artifact.bytedance.com/repository/pangle" }
121
+ }
122
+
123
+ dependencies {
124
+ implementation "com.facebook.react:react-android"
125
+ implementation project(":react-native-nitro-modules")
126
+
127
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
128
+ implementation "com.pangle.global:pag-sdk:8.2.0.4"
129
+ }
@@ -0,0 +1,2 @@
1
+ -keep class com.margelo.nitro.pangleads.** { *; }
2
+ -keep class com.bytedance.sdk.openadsdk.** { *; }
@@ -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
+ NitroPangleAds_kotlinVersion=2.1.20
2
+ NitroPangleAds_minSdkVersion=24
3
+ NitroPangleAds_targetSdkVersion=36
4
+ NitroPangleAds_compileSdkVersion=36
5
+ NitroPangleAds_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 "NitroPangleAdsOnLoad.hpp"
4
+
5
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
6
+ return margelo::nitro::pangleads::initialize(vm);
7
+ }
@@ -0,0 +1,123 @@
1
+ package com.margelo.nitro.pangleads
2
+
3
+ import androidx.annotation.Keep
4
+ import com.bytedance.sdk.openadsdk.api.PAGConstant.PAGPAConsentType
5
+ import com.bytedance.sdk.openadsdk.api.init.PAGConfig
6
+ import com.bytedance.sdk.openadsdk.api.init.PAGSdk
7
+ import com.facebook.proguard.annotations.DoNotStrip
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.margelo.nitro.NitroModules
10
+ import com.margelo.nitro.core.Promise
11
+ import kotlinx.coroutines.CompletableDeferred
12
+ import kotlinx.coroutines.Dispatchers
13
+ import kotlinx.coroutines.withContext
14
+
15
+ @DoNotStrip
16
+ @Keep
17
+ class HybridPangleAds : HybridPangleAdsSpec() {
18
+ private val context: ReactApplicationContext by lazy {
19
+ NitroModules.applicationContext as? ReactApplicationContext
20
+ ?: error("Pangle Ads requires a ReactApplicationContext")
21
+ }
22
+
23
+ private val stateLock = Any()
24
+ private var initializedAppId: String? = null
25
+ private var initializingAppId: String? = null
26
+ private var initialization: CompletableDeferred<Unit>? = null
27
+
28
+ override fun initialize(appId: String): Promise<Unit> = Promise.async {
29
+ require(appId.isNotBlank()) { "Pangle app ID is empty" }
30
+
31
+ val alreadyInitialized = synchronized(stateLock) {
32
+ val existing = initializedAppId
33
+ if (existing != null && existing != appId) {
34
+ error("Pangle SDK was already initialized with another app ID")
35
+ }
36
+ existing == appId
37
+ }
38
+ if (alreadyInitialized) return@async
39
+
40
+ val (deferred, shouldStart) = synchronized(stateLock) {
41
+ val current = initialization
42
+ if (current != null) {
43
+ if (initializingAppId != appId) {
44
+ error("Pangle SDK is being initialized with another app ID")
45
+ }
46
+ current to false
47
+ } else {
48
+ CompletableDeferred<Unit>().also {
49
+ initialization = it
50
+ initializingAppId = appId
51
+ } to true
52
+ }
53
+ }
54
+
55
+ if (shouldStart) {
56
+ val config = PAGConfig.Builder().appId(appId).build()
57
+ withContext(Dispatchers.Main) {
58
+ PAGSdk.init(
59
+ context.applicationContext,
60
+ config,
61
+ object : PAGSdk.PAGInitCallback {
62
+ override fun success() {
63
+ synchronized(stateLock) {
64
+ initializedAppId = appId
65
+ initialization = null
66
+ initializingAppId = null
67
+ }
68
+ deferred.complete(Unit)
69
+ }
70
+
71
+ override fun fail(code: Int, msg: String) {
72
+ synchronized(stateLock) {
73
+ if (initialization === deferred) initialization = null
74
+ initializingAppId = null
75
+ }
76
+ deferred.completeExceptionally(pangleException("initialize", code, msg))
77
+ }
78
+ },
79
+ )
80
+ }
81
+ }
82
+
83
+ deferred.await()
84
+ }
85
+
86
+ // pag-sdk 8.x collapsed the separate GDPR/DoNotSell/ChildDirected APIs into a single
87
+ // PAConsent flag, so GDPR/CCPA consent are no longer distinguishable, and there is no
88
+ // longer a public way to flag child-directed (COPPA) traffic to Pangle on Android.
89
+ override fun setGDPRConsent(optIn: Boolean) {
90
+ PAGConfig.setPAConsent(
91
+ if (optIn) PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT
92
+ else PAGPAConsentType.PAG_PA_CONSENT_TYPE_NO_CONSENT,
93
+ )
94
+ }
95
+
96
+ override fun setCCPAConsent(optIn: Boolean) {
97
+ PAGConfig.setPAConsent(
98
+ if (optIn) PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT
99
+ else PAGPAConsentType.PAG_PA_CONSENT_TYPE_NO_CONSENT,
100
+ )
101
+ }
102
+
103
+ override fun setCOPPA(isUserCoppa: Boolean) {}
104
+
105
+ override fun createInterstitialAd(
106
+ placementId: String,
107
+ ): HybridPangleInterstitialAdSpec {
108
+ requireInitialized(placementId)
109
+ return HybridPangleInterstitialAd(context, placementId)
110
+ }
111
+
112
+ override fun createRewardedAd(placementId: String): HybridPangleRewardedAdSpec {
113
+ requireInitialized(placementId)
114
+ return HybridPangleRewardedAd(context, placementId)
115
+ }
116
+
117
+ private fun requireInitialized(placementId: String) {
118
+ require(synchronized(stateLock) { initializedAppId != null }) {
119
+ "Pangle SDK is not initialized"
120
+ }
121
+ require(placementId.isNotBlank()) { "Pangle placement ID is empty" }
122
+ }
123
+ }
@@ -0,0 +1,106 @@
1
+ package com.margelo.nitro.pangleads
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import androidx.annotation.Keep
6
+ import com.bytedance.sdk.openadsdk.api.interstitial.PAGInterstitialAd
7
+ import com.bytedance.sdk.openadsdk.api.interstitial.PAGInterstitialAdInteractionListener
8
+ import com.bytedance.sdk.openadsdk.api.interstitial.PAGInterstitialAdLoadListener
9
+ import com.bytedance.sdk.openadsdk.api.interstitial.PAGInterstitialRequest
10
+ import com.facebook.proguard.annotations.DoNotStrip
11
+ import com.facebook.react.bridge.ReactApplicationContext
12
+ import com.margelo.nitro.core.Promise
13
+ import kotlinx.coroutines.Dispatchers
14
+ import kotlinx.coroutines.withContext
15
+
16
+ @DoNotStrip
17
+ @Keep
18
+ class HybridPangleInterstitialAd(
19
+ private val context: ReactApplicationContext,
20
+ private val placementId: String,
21
+ ) : HybridPangleInterstitialAdSpec(), PAGInterstitialAdInteractionListener {
22
+ private val events = PangleAdEventEmitter()
23
+ private val mainHandler = Handler(Looper.getMainLooper())
24
+ private var ad: PAGInterstitialAd? = null
25
+ private var showTimeout: Runnable? = null
26
+
27
+ override fun load() {
28
+ context.runOnUiQueueThread {
29
+ PAGInterstitialAd.loadAd(
30
+ placementId,
31
+ PAGInterstitialRequest(),
32
+ object : PAGInterstitialAdLoadListener {
33
+ override fun onError(code: Int, message: String) {
34
+ mainHandler.post {
35
+ events.emit(
36
+ PangleAdEventType.ERROR,
37
+ pangleException("load", code, message).toPangleEventPayload(),
38
+ )
39
+ }
40
+ }
41
+
42
+ override fun onAdLoaded(interstitialAd: PAGInterstitialAd) {
43
+ mainHandler.post {
44
+ interstitialAd.setAdInteractionListener(this@HybridPangleInterstitialAd)
45
+ ad = interstitialAd
46
+ events.emit(PangleAdEventType.LOADED)
47
+ }
48
+ }
49
+ },
50
+ )
51
+ }
52
+ }
53
+
54
+ override fun show(): Promise<Unit> = Promise.async {
55
+ withContext(Dispatchers.Main) {
56
+ val activity = context.requireCurrentActivity()
57
+ val current = ad ?: error("Pangle interstitial ad is not ready")
58
+ armShowTimeout()
59
+ current.show(activity)
60
+ }
61
+ }
62
+
63
+ override fun addAdEventListener(
64
+ eventType: PangleAdEventType,
65
+ listener: (payload: PangleAdEventPayload?) -> Unit,
66
+ ) = events.add(eventType, listener)
67
+
68
+ override fun removeAdEventListener(subscriptionId: Double) {
69
+ events.remove(subscriptionId)
70
+ }
71
+
72
+ override fun removeAllListeners() {
73
+ events.removeAll()
74
+ }
75
+
76
+ override fun onAdShowed() {
77
+ disarmShowTimeout()
78
+ events.emit(PangleAdEventType.OPENED)
79
+ }
80
+
81
+ override fun onAdClicked() {
82
+ events.emit(PangleAdEventType.CLICKED)
83
+ }
84
+
85
+ override fun onAdDismissed() {
86
+ disarmShowTimeout()
87
+ ad = null
88
+ events.emit(PangleAdEventType.CLOSED)
89
+ }
90
+
91
+ private fun armShowTimeout() {
92
+ disarmShowTimeout()
93
+ val runnable = Runnable {
94
+ showTimeout = null
95
+ ad = null
96
+ events.emit(PangleAdEventType.ERROR, pangleShowTimeoutPayload())
97
+ }
98
+ showTimeout = runnable
99
+ mainHandler.postDelayed(runnable, PANGLE_SHOW_TIMEOUT_MS)
100
+ }
101
+
102
+ private fun disarmShowTimeout() {
103
+ showTimeout?.let(mainHandler::removeCallbacks)
104
+ showTimeout = null
105
+ }
106
+ }