expo-ads 0.1.2 → 1.0.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.
- package/.editorconfig +5 -0
- package/android/build.gradle +32 -33
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/expo/modules/ads/meta/MetaAdsInitializer.kt +3 -1
- package/android/src/main/java/expo/modules/ads/meta/MetaAdsModule.kt +278 -69
- package/android/src/main/java/expo/modules/ads/meta/MetaBannerAdView.kt +77 -37
- package/android/src/main/java/expo/modules/ads/meta/MetaInterstitialManager.kt +130 -0
- package/android/src/main/java/expo/modules/ads/meta/MetaNativeAdManager.kt +146 -0
- package/android/src/main/java/expo/modules/ads/meta/MetaNativeAdView.kt +142 -0
- package/android/src/main/java/expo/modules/ads/meta/MetaNativeBannerAdType.kt +59 -0
- package/android/src/main/java/expo/modules/ads/meta/MetaNativeHostViews.kt +175 -0
- package/android/src/main/java/expo/modules/ads/meta/MetaRewardedInterstitialManager.kt +168 -0
- package/android/src/main/java/expo/modules/ads/meta/MetaRewardedManager.kt +168 -0
- package/build/meta/MetaAdEventType.d.ts +6 -0
- package/build/meta/MetaAdEventType.d.ts.map +1 -0
- package/build/meta/MetaAdEventType.js +2 -0
- package/build/meta/MetaAdEventType.js.map +1 -0
- package/build/meta/{ExpoAdsMetaModule.d.ts → MetaAdsModule.d.ts} +16 -3
- package/build/meta/MetaAdsModule.d.ts.map +1 -0
- package/build/meta/MetaAdsModule.js +3 -0
- package/build/meta/MetaAdsModule.js.map +1 -0
- package/build/meta/ads/BannerAd.d.ts +2 -2
- package/build/meta/ads/BannerAd.d.ts.map +1 -1
- package/build/meta/ads/BannerAd.js +9 -4
- package/build/meta/ads/BannerAd.js.map +1 -1
- package/build/meta/ads/InterstitialAd.d.ts +37 -0
- package/build/meta/ads/InterstitialAd.d.ts.map +1 -0
- package/build/meta/ads/InterstitialAd.js +99 -0
- package/build/meta/ads/InterstitialAd.js.map +1 -0
- package/build/meta/ads/RewardedAd.d.ts +55 -0
- package/build/meta/ads/RewardedAd.d.ts.map +1 -0
- package/build/meta/ads/RewardedAd.js +171 -0
- package/build/meta/ads/RewardedAd.js.map +1 -0
- package/build/meta/ads/RewardedInterstitialAd.d.ts +54 -0
- package/build/meta/ads/RewardedInterstitialAd.d.ts.map +1 -0
- package/build/meta/ads/RewardedInterstitialAd.js +170 -0
- package/build/meta/ads/RewardedInterstitialAd.js.map +1 -0
- package/build/meta/ads/native-ad/NativeAd.d.ts +59 -0
- package/build/meta/ads/native-ad/NativeAd.d.ts.map +1 -0
- package/build/meta/ads/native-ad/NativeAd.js +106 -0
- package/build/meta/ads/native-ad/NativeAd.js.map +1 -0
- package/build/meta/ads/native-ad/NativeAdView.d.ts +47 -0
- package/build/meta/ads/native-ad/NativeAdView.d.ts.map +1 -0
- package/build/meta/ads/native-ad/NativeAdView.js +58 -0
- package/build/meta/ads/native-ad/NativeAdView.js.map +1 -0
- package/build/meta/ads/native-ad/NativeAssets.d.ts +24 -0
- package/build/meta/ads/native-ad/NativeAssets.d.ts.map +1 -0
- package/build/meta/ads/native-ad/NativeAssets.js +28 -0
- package/build/meta/ads/native-ad/NativeAssets.js.map +1 -0
- package/build/meta/events/MetaInterstitialAdEventType.d.ts +15 -0
- package/build/meta/events/MetaInterstitialAdEventType.d.ts.map +1 -0
- package/build/meta/events/MetaInterstitialAdEventType.js +16 -0
- package/build/meta/events/MetaInterstitialAdEventType.js.map +1 -0
- package/build/meta/events/MetaNativeAdEventType.d.ts +12 -0
- package/build/meta/events/MetaNativeAdEventType.d.ts.map +1 -0
- package/build/meta/events/MetaNativeAdEventType.js +13 -0
- package/build/meta/events/MetaNativeAdEventType.js.map +1 -0
- package/build/meta/events/MetaRewardedAdEventType.d.ts +17 -0
- package/build/meta/events/MetaRewardedAdEventType.d.ts.map +1 -0
- package/build/meta/events/MetaRewardedAdEventType.js +18 -0
- package/build/meta/events/MetaRewardedAdEventType.js.map +1 -0
- package/build/meta/events/MetaRewardedInterstitialAdEventType.d.ts +13 -0
- package/build/meta/events/MetaRewardedInterstitialAdEventType.d.ts.map +1 -0
- package/build/meta/events/MetaRewardedInterstitialAdEventType.js +14 -0
- package/build/meta/events/MetaRewardedInterstitialAdEventType.js.map +1 -0
- package/build/meta/hooks/useInterstitialAd.d.ts +12 -0
- package/build/meta/hooks/useInterstitialAd.d.ts.map +1 -0
- package/build/meta/hooks/useInterstitialAd.js +48 -0
- package/build/meta/hooks/useInterstitialAd.js.map +1 -0
- package/build/meta/hooks/useNativeAd.d.ts +37 -0
- package/build/meta/hooks/useNativeAd.d.ts.map +1 -0
- package/build/meta/hooks/useNativeAd.js +50 -0
- package/build/meta/hooks/useNativeAd.js.map +1 -0
- package/build/meta/hooks/useRewardedAd.d.ts +16 -0
- package/build/meta/hooks/useRewardedAd.d.ts.map +1 -0
- package/build/meta/hooks/useRewardedAd.js +83 -0
- package/build/meta/hooks/useRewardedAd.js.map +1 -0
- package/build/meta/hooks/useRewardedInterstitialAd.d.ts +16 -0
- package/build/meta/hooks/useRewardedInterstitialAd.d.ts.map +1 -0
- package/build/meta/hooks/useRewardedInterstitialAd.js +83 -0
- package/build/meta/hooks/useRewardedInterstitialAd.js.map +1 -0
- package/build/meta/index.d.ts +22 -2
- package/build/meta/index.d.ts.map +1 -1
- package/build/meta/index.js +15 -3
- package/build/meta/index.js.map +1 -1
- package/build/meta/internal/ref.d.ts +10 -0
- package/build/meta/internal/ref.d.ts.map +1 -0
- package/build/meta/internal/ref.js +31 -0
- package/build/meta/internal/ref.js.map +1 -0
- package/build/meta/specs/components/{ExpoAdsMetaBannerViewNativeComponent.d.ts → MetaAdsBannerViewNativeComponent.d.ts} +3 -3
- package/build/meta/specs/components/MetaAdsBannerViewNativeComponent.d.ts.map +1 -0
- package/build/meta/specs/components/MetaAdsBannerViewNativeComponent.js +5 -0
- package/build/meta/specs/components/MetaAdsBannerViewNativeComponent.js.map +1 -0
- package/build/meta/specs/components/MetaAdsNativeViewNativeComponent.d.ts +23 -0
- package/build/meta/specs/components/MetaAdsNativeViewNativeComponent.d.ts.map +1 -0
- package/build/meta/specs/components/MetaAdsNativeViewNativeComponent.js +7 -0
- package/build/meta/specs/components/MetaAdsNativeViewNativeComponent.js.map +1 -0
- package/build/meta/types/MetaAdError.d.ts +2 -1
- package/build/meta/types/MetaAdError.d.ts.map +1 -1
- package/build/meta/types/MetaAdError.js.map +1 -1
- package/build/meta/types/MetaAdEventListener.d.ts +8 -0
- package/build/meta/types/MetaAdEventListener.d.ts.map +1 -0
- package/build/meta/types/MetaAdEventListener.js +2 -0
- package/build/meta/types/MetaAdEventListener.js.map +1 -0
- package/build/meta/types/MetaAdEventsListener.d.ts +7 -0
- package/build/meta/types/MetaAdEventsListener.d.ts.map +1 -0
- package/build/meta/types/MetaAdEventsListener.js +2 -0
- package/build/meta/types/MetaAdEventsListener.js.map +1 -0
- package/build/meta/types/MetaBannerAdProps.d.ts +6 -0
- package/build/meta/types/MetaBannerAdProps.d.ts.map +1 -1
- package/build/meta/types/MetaBannerAdProps.js.map +1 -1
- package/build/meta/types/MetaInterstitialEvent.d.ts +8 -0
- package/build/meta/types/MetaInterstitialEvent.d.ts.map +1 -0
- package/build/meta/types/MetaInterstitialEvent.js +2 -0
- package/build/meta/types/MetaInterstitialEvent.js.map +1 -0
- package/build/meta/types/MetaNativeAdProps.d.ts +55 -0
- package/build/meta/types/MetaNativeAdProps.d.ts.map +1 -0
- package/build/meta/types/MetaNativeAdProps.js +13 -0
- package/build/meta/types/MetaNativeAdProps.js.map +1 -0
- package/build/meta/types/MetaRewardedEvent.d.ts +19 -0
- package/build/meta/types/MetaRewardedEvent.d.ts.map +1 -0
- package/build/meta/types/MetaRewardedEvent.js +2 -0
- package/build/meta/types/MetaRewardedEvent.js.map +1 -0
- package/build/meta/types/MetaRewardedInterstitialEvent.d.ts +19 -0
- package/build/meta/types/MetaRewardedInterstitialEvent.d.ts.map +1 -0
- package/build/meta/types/MetaRewardedInterstitialEvent.js +2 -0
- package/build/meta/types/MetaRewardedInterstitialEvent.js.map +1 -0
- package/expo-module.config.json +6 -18
- package/package.json +79 -83
- package/src/meta/MetaAdEventType.ts +7 -0
- package/src/meta/{ExpoAdsMetaModule.ts → MetaAdsModule.ts} +70 -44
- package/src/meta/ads/BannerAd.tsx +12 -4
- package/src/meta/ads/InterstitialAd.ts +122 -0
- package/src/meta/ads/RewardedAd.ts +198 -0
- package/src/meta/ads/RewardedInterstitialAd.ts +206 -0
- package/src/meta/ads/native-ad/NativeAd.ts +142 -0
- package/src/meta/ads/native-ad/NativeAdView.tsx +88 -0
- package/src/meta/ads/native-ad/NativeAssets.tsx +52 -0
- package/src/meta/events/MetaInterstitialAdEventType.ts +14 -0
- package/src/meta/events/MetaNativeAdEventType.ts +13 -0
- package/src/meta/events/MetaRewardedAdEventType.ts +17 -0
- package/src/meta/events/MetaRewardedInterstitialAdEventType.ts +12 -0
- package/src/meta/hooks/useInterstitialAd.ts +54 -0
- package/src/meta/hooks/useNativeAd.ts +60 -0
- package/src/meta/hooks/useRewardedAd.ts +100 -0
- package/src/meta/hooks/useRewardedInterstitialAd.ts +102 -0
- package/src/meta/index.ts +42 -12
- package/src/meta/internal/ref.ts +37 -0
- package/src/meta/specs/components/{ExpoAdsMetaBannerViewNativeComponent.ts → MetaAdsBannerViewNativeComponent.ts} +6 -3
- package/src/meta/specs/components/MetaAdsNativeViewNativeComponent.ts +43 -0
- package/src/meta/types/MetaAdError.ts +8 -1
- package/src/meta/types/MetaAdEventListener.ts +15 -0
- package/src/meta/types/MetaAdEventsListener.ts +7 -0
- package/src/meta/types/MetaBannerAdProps.ts +7 -0
- package/src/meta/types/MetaInterstitialEvent.ts +8 -0
- package/src/meta/types/MetaNativeAdProps.ts +66 -0
- package/src/meta/types/MetaRewardedEvent.ts +20 -0
- package/src/meta/types/MetaRewardedInterstitialEvent.ts +20 -0
- package/tsconfig.json +1 -1
- package/android/src/main/java/expo/modules/ads/google/GoogleAdsModule.kt +0 -17
- package/build/google/ExpoAdsGoogleModule.d.ts +0 -6
- package/build/google/ExpoAdsGoogleModule.d.ts.map +0 -1
- package/build/google/ExpoAdsGoogleModule.js +0 -3
- package/build/google/ExpoAdsGoogleModule.js.map +0 -1
- package/build/google/index.d.ts +0 -2
- package/build/google/index.d.ts.map +0 -1
- package/build/google/index.js +0 -6
- package/build/google/index.js.map +0 -1
- package/build/meta/ExpoAdsMetaModule.d.ts.map +0 -1
- package/build/meta/ExpoAdsMetaModule.js +0 -3
- package/build/meta/ExpoAdsMetaModule.js.map +0 -1
- package/build/meta/specs/components/ExpoAdsMetaBannerViewNativeComponent.d.ts.map +0 -1
- package/build/meta/specs/components/ExpoAdsMetaBannerViewNativeComponent.js +0 -5
- package/build/meta/specs/components/ExpoAdsMetaBannerViewNativeComponent.js.map +0 -1
- package/ios/Expo-ads.podspec +0 -23
- package/ios/Expo-adsModule.swift +0 -7
- package/src/google/ExpoAdsGoogleModule.ts +0 -6
- package/src/google/index.ts +0 -5
package/.editorconfig
ADDED
package/android/build.gradle
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
|
-
import groovy.json.JsonSlurper
|
|
2
|
-
|
|
3
|
-
plugins {
|
|
4
|
-
id 'com.android.library'
|
|
5
|
-
id 'expo-module-gradle-plugin'
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
group = 'expo.modules.ads'
|
|
9
|
-
version = '0.1.
|
|
10
|
-
|
|
11
|
-
def packageJson = new JsonSlurper().parseText(file("${projectDir}/../package.json").text)
|
|
12
|
-
|
|
13
|
-
def metaAudienceNetworkVersion = packageJson.sdkVersions.android.metaAudienceNetwork
|
|
14
|
-
def playServicesAdsIdentifierVersion = packageJson.sdkVersions.android.playServicesAdsIdentifier
|
|
15
|
-
def playServicesBasementVersion = packageJson.sdkVersions.android.playServicesBasement
|
|
16
|
-
|
|
17
|
-
android {
|
|
18
|
-
namespace "expo.modules.ads"
|
|
19
|
-
defaultConfig {
|
|
20
|
-
versionCode 1
|
|
21
|
-
versionName "0.1.
|
|
22
|
-
}
|
|
23
|
-
lintOptions {
|
|
24
|
-
abortOnError false
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
implementation("com.
|
|
32
|
-
implementation("com.google.android.gms:play-services-
|
|
33
|
-
implementation("com.google.android.gms:play-services-basement:${playServicesBasementVersion}")
|
|
1
|
+
import groovy.json.JsonSlurper
|
|
2
|
+
|
|
3
|
+
plugins {
|
|
4
|
+
id 'com.android.library'
|
|
5
|
+
id 'expo-module-gradle-plugin'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
group = 'expo.modules.ads'
|
|
9
|
+
version = '0.1.3'
|
|
10
|
+
|
|
11
|
+
def packageJson = new JsonSlurper().parseText(file("${projectDir}/../package.json").text)
|
|
12
|
+
|
|
13
|
+
def metaAudienceNetworkVersion = packageJson.sdkVersions.android.metaAudienceNetwork
|
|
14
|
+
def playServicesAdsIdentifierVersion = packageJson.sdkVersions.android.playServicesAdsIdentifier
|
|
15
|
+
def playServicesBasementVersion = packageJson.sdkVersions.android.playServicesBasement
|
|
16
|
+
|
|
17
|
+
android {
|
|
18
|
+
namespace "expo.modules.ads"
|
|
19
|
+
defaultConfig {
|
|
20
|
+
versionCode 1
|
|
21
|
+
versionName "0.1.3"
|
|
22
|
+
}
|
|
23
|
+
lintOptions {
|
|
24
|
+
abortOnError false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
dependencies {
|
|
30
|
+
implementation("com.facebook.android:audience-network-sdk:${metaAudienceNetworkVersion}")
|
|
31
|
+
implementation("com.google.android.gms:play-services-ads-identifier:${playServicesAdsIdentifierVersion}")
|
|
32
|
+
implementation("com.google.android.gms:play-services-basement:${playServicesBasementVersion}")
|
|
34
33
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
<manifest>
|
|
2
|
-
</manifest>
|
|
1
|
+
<manifest>
|
|
2
|
+
</manifest>
|
|
@@ -10,7 +10,7 @@ import com.facebook.ads.AudienceNetworkAds
|
|
|
10
10
|
|
|
11
11
|
object MetaAdsInitializer : AudienceNetworkAds.InitListener {
|
|
12
12
|
|
|
13
|
-
const val TAG = "
|
|
13
|
+
const val TAG = "MetaAds"
|
|
14
14
|
|
|
15
15
|
private enum class State { IDLE, INITIALIZING, READY, FAILED }
|
|
16
16
|
|
|
@@ -21,6 +21,8 @@ object MetaAdsInitializer : AudienceNetworkAds.InitListener {
|
|
|
21
21
|
|
|
22
22
|
fun isReady(): Boolean = synchronized(lock) { state == State.READY }
|
|
23
23
|
|
|
24
|
+
fun clearPending() = synchronized(lock) { pending.clear() }
|
|
25
|
+
|
|
24
26
|
fun whenReady(context: Context, callback: (success: Boolean, message: String) -> Unit) {
|
|
25
27
|
val appContext = context.applicationContext
|
|
26
28
|
var startInit = false
|
|
@@ -7,87 +7,296 @@ import expo.modules.kotlin.modules.Module
|
|
|
7
7
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
8
8
|
|
|
9
9
|
class MetaAdsModule : Module() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
init {
|
|
11
|
+
Log.d("MetaAds", "🔥 MetaAdsModule INSTANCE CREATED")
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
override fun definition() =
|
|
15
|
+
ModuleDefinition {
|
|
16
|
+
Name("MetaAds") // the name JS uses to find this module (import from "expo-ads/meta")
|
|
17
|
+
|
|
18
|
+
// Runs automatically when the module loads (warm-up)
|
|
19
|
+
OnCreate {
|
|
20
|
+
val ctx = appContext.reactContext
|
|
21
|
+
if (ctx == null) {
|
|
22
|
+
Log.w(MetaAdsInitializer.TAG, "[Module] OnCreate: React context is null, skipping warm-up")
|
|
23
|
+
} else {
|
|
24
|
+
Log.d(MetaAdsInitializer.TAG, "[Module] OnCreate: starting SDK warm-up")
|
|
25
|
+
MetaAdsInitializer.whenReady(ctx) { _, _ -> }
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
|
-
}
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Log.e(MetaAdsInitializer.TAG, "[Module] initialize() called from JS but React context is null")
|
|
29
|
-
promise.reject("ERR_NO_CONTEXT", "React context not available", null)
|
|
30
|
-
return@AsyncFunction
|
|
29
|
+
OnDestroy {
|
|
30
|
+
MetaAdsInitializer.clearPending()
|
|
31
|
+
MetaInterstitialManager.destroyAll()
|
|
32
|
+
MetaRewardedInterstitialManager.destroyAll()
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
Events("onInterstitialEvent", "onRewardedEvent", "onRewardedInterstitialEvent")
|
|
36
|
+
|
|
37
|
+
// Lets JS call: await MetaAds.initialize()
|
|
38
|
+
AsyncFunction("initialize") { promise: Promise ->
|
|
39
|
+
val ctx = appContext.reactContext
|
|
40
|
+
if (ctx == null) {
|
|
41
|
+
Log.e(MetaAdsInitializer.TAG, "[Module] initialize() called from JS but React context is null")
|
|
42
|
+
promise.reject("ERR_NO_CONTEXT", "React context not available", null)
|
|
43
|
+
return@AsyncFunction
|
|
44
|
+
}
|
|
45
|
+
Log.d(MetaAdsInitializer.TAG, "[Module] initialize() called from JS")
|
|
46
|
+
MetaAdsInitializer.whenReady(ctx) { ok, msg ->
|
|
47
|
+
if (ok) promise.resolve(msg) else promise.reject("ERR_META_INIT", msg, null)
|
|
48
|
+
}
|
|
35
49
|
}
|
|
36
|
-
}
|
|
37
50
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
// Lets JS call: await MetaAds.setTestMode(true)
|
|
52
|
+
//
|
|
53
|
+
// This is a plain static AdSettings toggle with no I/O, so it's a synchronous Function
|
|
54
|
+
// rather than an AsyncFunction — but it's still safe (and expected) to `await` it from
|
|
55
|
+
// JS, since every native function on this module returns a Promise from the JS side
|
|
56
|
+
// regardless of whether it's sync or async natively.
|
|
57
|
+
//
|
|
58
|
+
// Deliberately NOT gated on MetaAdsInitializer.isReady(): Meta's own docs call this
|
|
59
|
+
// (and addTestDevices) *before* the SDK finishes async initialization — test mode and
|
|
60
|
+
// test devices must be registered before the first loadAd() call, not after init.
|
|
61
|
+
Function("setTestMode") { enabled: Boolean ->
|
|
62
|
+
Log.d(MetaAdsInitializer.TAG, "[Module] setTestMode($enabled)")
|
|
63
|
+
AdSettings.setTestMode(enabled)
|
|
64
|
+
}
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
// Lets JS call: await MetaAds.addTestDevices(["HASHED_ID_1", "HASHED_ID_2"])
|
|
67
|
+
//
|
|
68
|
+
// Accepts a list so a single call can register every test device hash at once; each
|
|
69
|
+
// hash is normally copied from Logcat the first time a real ad request is made on that
|
|
70
|
+
// device (see MetaAdsInitializer's warm-up call in OnCreate above, or any loadAd()).
|
|
71
|
+
Function("addTestDevices") { deviceHashes: List<String> ->
|
|
72
|
+
Log.d(MetaAdsInitializer.TAG, "[Module] addTestDevices(${deviceHashes.size} device(s))")
|
|
73
|
+
AdSettings.addTestDevices(deviceHashes)
|
|
74
|
+
}
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
// Lets JS call: await MetaAds.clearTestDevices()
|
|
77
|
+
//
|
|
78
|
+
// Mirrors Meta's documented "before shipping" cleanup step
|
|
79
|
+
// (AdSettings.setTestMode(false); AdSettings.clearTestDevices();). Exposed separately
|
|
80
|
+
// from setTestMode(false) so callers can clear registered device hashes independently.
|
|
81
|
+
Function("clearTestDevices") {
|
|
82
|
+
Log.d(MetaAdsInitializer.TAG, "[Module] clearTestDevices()")
|
|
83
|
+
AdSettings.clearTestDevices()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
AsyncFunction("interstitialLoad") { requestId: Int, unitId: String ->
|
|
87
|
+
val emit: InterstitialEmitter = { body ->
|
|
88
|
+
try {
|
|
89
|
+
sendEvent("onInterstitialEvent", body)
|
|
90
|
+
} catch (
|
|
91
|
+
e: Exception,
|
|
92
|
+
) {
|
|
93
|
+
Log.w(MetaAdsInitializer.TAG, "[Interstitial] sendEvent failed: ${e.message}")
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
val ctx = appContext.currentActivity ?: appContext.reactContext
|
|
97
|
+
if (ctx == null) {
|
|
98
|
+
emit(
|
|
99
|
+
mapOf(
|
|
100
|
+
"requestId" to requestId,
|
|
101
|
+
"type" to "error",
|
|
102
|
+
"code" to "ERR_NO_CONTEXT",
|
|
103
|
+
"message" to "No Activity/React context available",
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
return@AsyncFunction
|
|
107
|
+
}
|
|
108
|
+
MetaInterstitialManager.runOnMain {
|
|
109
|
+
MetaInterstitialManager.load(ctx, requestId, unitId, emit)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
72
112
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
113
|
+
AsyncFunction("interstitialShow") { requestId: Int, promise: Promise ->
|
|
114
|
+
MetaInterstitialManager.runOnMain {
|
|
115
|
+
val failure = MetaInterstitialManager.show(requestId)
|
|
116
|
+
if (failure == null) {
|
|
117
|
+
promise.resolve(null)
|
|
118
|
+
} else {
|
|
119
|
+
promise.reject(failure.first, failure.second, null)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Function("interstitialDestroy") { requestId: Int ->
|
|
125
|
+
MetaInterstitialManager.runOnMain { MetaInterstitialManager.destroy(requestId) }
|
|
126
|
+
}
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
|
|
128
|
+
AsyncFunction("rewardedLoad") { requestId: Int, unitId: String, userId: String?, currency: String? ->
|
|
129
|
+
val emit: RewardedEmitter = { body ->
|
|
130
|
+
try {
|
|
131
|
+
sendEvent("onRewardedEvent", body)
|
|
132
|
+
} catch (e: Exception) {
|
|
133
|
+
Log.w(MetaAdsInitializer.TAG, "[Rewarded] sendEvent failed: ${e.message}")
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
val ctx = appContext.currentActivity ?: appContext.reactContext
|
|
137
|
+
if (ctx == null) {
|
|
138
|
+
emit(
|
|
139
|
+
mapOf(
|
|
140
|
+
"requestId" to requestId,
|
|
141
|
+
"type" to "error",
|
|
142
|
+
"code" to "ERR_NO_CONTEXT",
|
|
143
|
+
"message" to "No Activity/React context available",
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
return@AsyncFunction
|
|
147
|
+
}
|
|
148
|
+
MetaRewardedManager.runOnMain {
|
|
149
|
+
MetaRewardedManager.load(ctx, requestId, unitId, userId, currency, emit)
|
|
150
|
+
}
|
|
79
151
|
}
|
|
80
152
|
|
|
81
|
-
|
|
82
|
-
|
|
153
|
+
AsyncFunction("rewardedShow") { requestId: Int, promise: Promise ->
|
|
154
|
+
val emit: RewardedEmitter = { body ->
|
|
155
|
+
try {
|
|
156
|
+
sendEvent("onRewardedEvent", body)
|
|
157
|
+
} catch (e: Exception) {
|
|
158
|
+
Log.w(MetaAdsInitializer.TAG, "[Rewarded] sendEvent failed: ${e.message}")
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
MetaRewardedManager.runOnMain {
|
|
162
|
+
val failure = MetaRewardedManager.show(requestId, emit)
|
|
163
|
+
if (failure == null) promise.resolve(null) else promise.reject(failure.first, failure.second, null)
|
|
164
|
+
}
|
|
83
165
|
}
|
|
84
166
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
167
|
+
AsyncFunction("rewardedIsValid") { requestId: Int, promise: Promise ->
|
|
168
|
+
MetaRewardedManager.runOnMain { promise.resolve(MetaRewardedManager.isValid(requestId)) }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Function("rewardedDestroy") { requestId: Int ->
|
|
172
|
+
MetaRewardedManager.runOnMain { MetaRewardedManager.destroy(requestId) }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
AsyncFunction("rewardedInterstitialLoad") { requestId: Int, unitId: String, userId: String?, currency: String? ->
|
|
176
|
+
val emit: RewardedInterstitialEmitter = { body ->
|
|
177
|
+
try {
|
|
178
|
+
sendEvent("onRewardedInterstitialEvent", body)
|
|
179
|
+
} catch (
|
|
180
|
+
e: Exception,
|
|
181
|
+
) {
|
|
182
|
+
Log.w(MetaAdsInitializer.TAG, "[RewardedInterstitial] sendEvent failed: ${e.message}")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
val ctx = appContext.currentActivity ?: appContext.reactContext
|
|
186
|
+
if (ctx == null) {
|
|
187
|
+
emit(
|
|
188
|
+
mapOf(
|
|
189
|
+
"requestId" to requestId,
|
|
190
|
+
"type" to "error",
|
|
191
|
+
"code" to "ERR_NO_CONTEXT",
|
|
192
|
+
"message" to "No Activity/React context available",
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
return@AsyncFunction
|
|
196
|
+
}
|
|
197
|
+
MetaRewardedInterstitialManager.runOnMain {
|
|
198
|
+
MetaRewardedInterstitialManager.load(ctx, requestId, unitId, userId, currency, emit)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
AsyncFunction("rewardedInterstitialShow") { requestId: Int, promise: Promise ->
|
|
203
|
+
val emit: RewardedInterstitialEmitter = { body ->
|
|
204
|
+
try {
|
|
205
|
+
sendEvent("onRewardedInterstitialEvent", body)
|
|
206
|
+
} catch (
|
|
207
|
+
e: Exception,
|
|
208
|
+
) {
|
|
209
|
+
Log.w(MetaAdsInitializer.TAG, "[RewardedInterstitial] sendEvent failed: ${e.message}")
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
MetaRewardedInterstitialManager.runOnMain {
|
|
213
|
+
val failure = MetaRewardedInterstitialManager.show(requestId, emit)
|
|
214
|
+
if (failure == null) promise.resolve(null) else promise.reject(failure.first, failure.second, null)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
AsyncFunction("rewardedInterstitialIsValid") { requestId: Int, promise: Promise ->
|
|
219
|
+
MetaRewardedInterstitialManager.runOnMain { promise.resolve(MetaRewardedInterstitialManager.isValid(requestId)) }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
Function("rewardedInterstitialDestroy") { requestId: Int ->
|
|
223
|
+
MetaRewardedInterstitialManager.runOnMain { MetaRewardedInterstitialManager.destroy(requestId) }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Banner ad native view, exposed to JS as <MetaBannerAdView>.
|
|
227
|
+
View(MetaBannerAdView::class) {
|
|
228
|
+
Events("onAdLoaded", "onAdFailedToLoad", "onAdClicked", "onLoggingImpression")
|
|
229
|
+
|
|
230
|
+
Prop("unitId") { view: MetaBannerAdView, value: String? ->
|
|
231
|
+
view.setUnitId(value)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
Prop("size") { view: MetaBannerAdView, value: MetaBannerAdSize ->
|
|
235
|
+
view.setSize(value)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Fires after unitId/size are applied for this commit, mirroring the
|
|
239
|
+
// onAfterUpdateTransaction "props changed -> (re)request" pattern used by the reference
|
|
240
|
+
// Google Mobile Ads ViewManager, but expressed as an Expo Modules view lifecycle hook.
|
|
241
|
+
OnViewDidUpdateProps { view: MetaBannerAdView -> view.requestAd() }
|
|
242
|
+
|
|
243
|
+
OnViewDestroys { view: MetaBannerAdView -> view.cleanup() }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
AsyncFunction("nativeAdLoad") { unitId: String, promise: Promise ->
|
|
247
|
+
val ctx = appContext.currentActivity ?: appContext.reactContext
|
|
248
|
+
if (ctx == null) {
|
|
249
|
+
promise.reject("ERR_NO_CONTEXT", "No Activity/React context available", null)
|
|
250
|
+
return@AsyncFunction
|
|
251
|
+
}
|
|
252
|
+
MetaNativeAdManager.runOnMain {
|
|
253
|
+
MetaNativeAdManager.load(
|
|
254
|
+
ctx,
|
|
255
|
+
unitId,
|
|
256
|
+
emit = { body ->
|
|
257
|
+
try {
|
|
258
|
+
sendEvent("onNativeAdEvent", body)
|
|
259
|
+
} catch (
|
|
260
|
+
e: Exception,
|
|
261
|
+
) {
|
|
262
|
+
Log.w(MetaAdsInitializer.TAG, "[Native] sendEvent failed: ${e.message}")
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
onLoaded = { promise.resolve(it) },
|
|
266
|
+
onFailed = { code, msg -> promise.reject(code, msg, null) },
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
Function("nativeAdDestroy") { adId: String ->
|
|
272
|
+
MetaNativeAdManager.runOnMain { MetaNativeAdManager.destroy(adId) }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
View(MetaNativeAdView::class) {
|
|
276
|
+
Prop("adId") { view: MetaNativeAdView, value: String? -> view.setAdId(value) }
|
|
277
|
+
|
|
278
|
+
AsyncFunction("registerAsset") { view: MetaNativeAdView, assetType: String, reactTag: Int ->
|
|
279
|
+
view.registerAsset(assetType, reactTag)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
GroupView<MetaNativeAdView> {
|
|
283
|
+
AddChildView { parent: MetaNativeAdView, child: android.view.View, index: Int ->
|
|
284
|
+
parent.addChild(child, index)
|
|
285
|
+
}
|
|
286
|
+
GetChildCount { parent -> parent.childCount() }
|
|
287
|
+
GetChildViewAt { parent, index -> parent.childAt(index) }
|
|
288
|
+
RemoveChildViewAt { parent, index -> parent.removeChildAt(index) }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
OnViewDestroys { view: MetaNativeAdView -> view.cleanup() }
|
|
292
|
+
}
|
|
293
|
+
View(MetaNativeMediaView::class) {}
|
|
294
|
+
View(MetaNativeIconView::class) {}
|
|
295
|
+
View(MetaNativeAdOptionsView::class) {
|
|
296
|
+
Prop("orientation") { view: MetaNativeAdOptionsView, v: String? -> view.setOrientation(v) }
|
|
297
|
+
Prop("iconSize") { view: MetaNativeAdOptionsView, v: Int? -> view.setIconSize(v) }
|
|
298
|
+
Prop("iconColor") { view: MetaNativeAdOptionsView, v: Int? -> view.setIconColor(v) }
|
|
299
|
+
OnViewDidUpdateProps { view: MetaNativeAdOptionsView -> view.rebuild() }
|
|
90
300
|
}
|
|
91
301
|
}
|
|
92
|
-
|
|
93
|
-
}
|
|
302
|
+
}
|
|
@@ -18,64 +18,68 @@ class MetaBannerAdView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
18
18
|
private var adView: AdView? = null
|
|
19
19
|
private var unitId: String? = null
|
|
20
20
|
private var size: MetaBannerAdSize = MetaBannerAdSize.BANNER_HEIGHT_50
|
|
21
|
+
private var loadedUnitId: String? = null
|
|
22
|
+
private var loadedSize: MetaBannerAdSize? = null
|
|
23
|
+
private var destroyed = false
|
|
21
24
|
|
|
22
25
|
private val onAdLoaded by EventDispatcher()
|
|
23
26
|
private val onAdFailedToLoad by EventDispatcher()
|
|
24
27
|
private val onAdClicked by EventDispatcher()
|
|
25
28
|
private val onLoggingImpression by EventDispatcher()
|
|
26
29
|
|
|
27
|
-
fun setUnitId(value: String?) {
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
+
fun setUnitId(value: String?) { unitId = value }
|
|
31
|
+
fun setSize(value: MetaBannerAdSize) { size = value }
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
// Swallows dispatches to a dead React context instead of crashing
|
|
34
|
+
private fun safeDispatch(block: () -> Unit) {
|
|
35
|
+
if (destroyed) return
|
|
36
|
+
try {
|
|
37
|
+
block()
|
|
38
|
+
} catch (e: Exception) {
|
|
39
|
+
Log.w(TAG, "[Banner] event dispatch failed: ${e.message}")
|
|
40
|
+
}
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
/**
|
|
36
|
-
* Called from `OnViewDidUpdateProps` in MetaAdsModule after unitId/size are applied for a
|
|
37
|
-
* commit (mirroring the "props changed -> (re)request" pattern from the reference Google
|
|
38
|
-
* Mobile Ads ViewManager's onAfterUpdateTransaction). Runs the two required guards, in
|
|
39
|
-
* order, before ever touching the SDK:
|
|
40
|
-
* 1. Ad unit must be configured — otherwise don't build a request at all.
|
|
41
|
-
* 2. SDK must already report READY — otherwise tell the caller to initialize first.
|
|
42
|
-
* Neither failure throws; both are surfaced to JS as onAdFailedToLoad so callers can
|
|
43
|
-
* handle them the same way they'd handle any other load failure.
|
|
44
|
-
*/
|
|
45
43
|
fun requestAd() {
|
|
44
|
+
if (destroyed) return
|
|
46
45
|
val currentUnitId = unitId
|
|
47
46
|
|
|
48
47
|
if (currentUnitId.isNullOrBlank()) {
|
|
49
48
|
val message = "Meta banner ad request skipped: no adUnitId was provided. Set the " +
|
|
50
49
|
"`unitId` prop on <MetaBannerAd> to a valid Audience Network placement ID."
|
|
51
50
|
Log.e(TAG, "[Banner] $message")
|
|
52
|
-
onAdFailedToLoad(mapOf("code" to "ERR_NO_AD_UNIT", "message" to message))
|
|
51
|
+
safeDispatch { onAdFailedToLoad(mapOf("code" to "ERR_NO_AD_UNIT", "message" to message)) }
|
|
53
52
|
return
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
if (!MetaAdsInitializer.isReady()) {
|
|
57
56
|
val message = "Meta banner ad request skipped: Audience Network SDK is not initialized " +
|
|
58
|
-
"yet. Call
|
|
57
|
+
"yet. Call MetaAds.initialize() and wait for it to resolve before requesting a " +
|
|
59
58
|
"banner ad."
|
|
60
59
|
Log.e(TAG, "[Banner] $message")
|
|
61
|
-
onAdFailedToLoad(mapOf("code" to "ERR_NOT_INITIALIZED", "message" to message))
|
|
60
|
+
safeDispatch { onAdFailedToLoad(mapOf("code" to "ERR_NOT_INITIALIZED", "message" to message)) }
|
|
62
61
|
return
|
|
63
62
|
}
|
|
64
63
|
|
|
64
|
+
// Skip redundant reloads when nothing changed
|
|
65
|
+
if (adView != null && loadedUnitId == currentUnitId && loadedSize == size) return
|
|
66
|
+
|
|
65
67
|
loadAd(currentUnitId, size)
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
private fun loadAd(currentUnitId: String, currentSize: MetaBannerAdSize) {
|
|
69
|
-
// Tear down any previous ad view before creating a new one (unit or size may have changed).
|
|
70
71
|
destroyAdView()
|
|
72
|
+
loadedUnitId = currentUnitId
|
|
73
|
+
loadedSize = currentSize
|
|
71
74
|
|
|
72
75
|
val newAdView = AdView(context, currentUnitId, currentSize.toFacebookAdSize())
|
|
73
76
|
adView = newAdView
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
val adListener = object : AdListener {
|
|
79
|
+
override fun onError(ad: Ad, adError: AdError) {
|
|
80
|
+
if (adView !== newAdView) return // stale callback from a replaced ad
|
|
81
|
+
Log.e(TAG, "[Banner] onError: ${adError.errorCode} ${adError.errorMessage}")
|
|
82
|
+
safeDispatch {
|
|
79
83
|
onAdFailedToLoad(
|
|
80
84
|
mapOf(
|
|
81
85
|
"code" to adError.errorCode.toString(),
|
|
@@ -83,9 +87,12 @@ class MetaBannerAdView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
83
87
|
)
|
|
84
88
|
)
|
|
85
89
|
}
|
|
90
|
+
}
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
override fun onAdLoaded(ad: Ad) {
|
|
93
|
+
if (adView !== newAdView) return
|
|
94
|
+
Log.d(TAG, "[Banner] onAdLoaded")
|
|
95
|
+
safeDispatch {
|
|
89
96
|
onAdLoaded(
|
|
90
97
|
mapOf(
|
|
91
98
|
"width" to currentSize.toFacebookAdSize().width,
|
|
@@ -93,35 +100,68 @@ class MetaBannerAdView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
93
100
|
)
|
|
94
101
|
)
|
|
95
102
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
// Force a layout pass so RN shows the natively-added child
|
|
104
|
+
post {
|
|
105
|
+
measure(
|
|
106
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
107
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
108
|
+
)
|
|
109
|
+
layout(left, top, right, bottom)
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
|
-
|
|
112
|
+
|
|
113
|
+
override fun onAdClicked(ad: Ad) = safeDispatch { onAdClicked(emptyMap()) }
|
|
114
|
+
|
|
115
|
+
override fun onLoggingImpression(ad: Ad) = safeDispatch { onLoggingImpression(emptyMap()) }
|
|
116
|
+
}
|
|
106
117
|
|
|
107
118
|
addView(
|
|
108
119
|
newAdView,
|
|
109
120
|
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
|
110
121
|
)
|
|
111
|
-
|
|
122
|
+
|
|
123
|
+
newAdView.loadAd(newAdView.buildLoadAdConfig().withAdListener(adListener).build())
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
private fun destroyAdView() {
|
|
115
127
|
adView?.let {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
try {
|
|
129
|
+
removeView(it)
|
|
130
|
+
it.destroy()
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
Log.w(TAG, "[Banner] destroy failed: ${e.message}")
|
|
133
|
+
}
|
|
119
134
|
}
|
|
120
135
|
adView = null
|
|
136
|
+
loadedUnitId = null
|
|
137
|
+
loadedSize = null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Called from OnViewDestroys in MetaAdsModule when React drops this view
|
|
141
|
+
fun cleanup() {
|
|
142
|
+
destroyed = true
|
|
143
|
+
destroyAdView()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
override fun onAttachedToWindow() {
|
|
147
|
+
super.onAttachedToWindow()
|
|
148
|
+
destroyed = false
|
|
121
149
|
}
|
|
122
150
|
|
|
123
151
|
override fun onDetachedFromWindow() {
|
|
124
152
|
destroyAdView()
|
|
125
153
|
super.onDetachedFromWindow()
|
|
126
154
|
}
|
|
155
|
+
|
|
156
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
157
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
158
|
+
adView?.measure(
|
|
159
|
+
MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
|
|
160
|
+
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST)
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
|
165
|
+
adView?.let { it.layout(0, 0, it.measuredWidth, it.measuredHeight) }
|
|
166
|
+
}
|
|
127
167
|
}
|