novvy-ads-react-native 1.0.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/android/build.gradle +84 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/ai/novvy/reactnative/NovvyAdContainerView.kt +63 -0
- package/android/src/main/java/ai/novvy/reactnative/NovvyAdsModule.kt +390 -0
- package/android/src/main/java/ai/novvy/reactnative/NovvyAdsPackage.kt +46 -0
- package/android/src/main/java/ai/novvy/reactnative/NovvyPauseAdViewManager.kt +68 -0
- package/android/src/main/java/ai/novvy/reactnative/NovvyScrollAdViewManager.kt +72 -0
- package/android/src/main/java/ai/novvy/reactnative/RnPlayerAdapter.kt +18 -0
- package/android/src/main/java/ai/novvy/reactnative/ViewReadyEvent.kt +26 -0
- package/app.plugin.js +4 -0
- package/ios/NovvyAds.mm +168 -0
- package/ios/NovvyAdsImpl.swift +409 -0
- package/ios/NovvyPauseAdView.mm +79 -0
- package/ios/NovvyScrollAdView.mm +80 -0
- package/ios/RnPlayerAdapter.swift +14 -0
- package/lib/commonjs/NovvyAds.js +87 -0
- package/lib/commonjs/NovvyAds.js.map +1 -0
- package/lib/commonjs/ads/NovvyAd.js +121 -0
- package/lib/commonjs/ads/NovvyAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyEndAd.js +19 -0
- package/lib/commonjs/ads/NovvyEndAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyFullScreenAd.js +49 -0
- package/lib/commonjs/ads/NovvyFullScreenAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyInterstitialAd.js +19 -0
- package/lib/commonjs/ads/NovvyInterstitialAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyMidAd.js +19 -0
- package/lib/commonjs/ads/NovvyMidAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyPauseAd.js +116 -0
- package/lib/commonjs/ads/NovvyPauseAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyRewardedAd.js +53 -0
- package/lib/commonjs/ads/NovvyRewardedAd.js.map +1 -0
- package/lib/commonjs/ads/NovvyScrollAd.js +136 -0
- package/lib/commonjs/ads/NovvyScrollAd.js.map +1 -0
- package/lib/commonjs/eventBus.js +60 -0
- package/lib/commonjs/eventBus.js.map +1 -0
- package/lib/commonjs/index.js +90 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/spec/NativeNovvyAds.js +27 -0
- package/lib/commonjs/spec/NativeNovvyAds.js.map +1 -0
- package/lib/commonjs/spec/NovvyPauseAdViewNativeComponent.js +16 -0
- package/lib/commonjs/spec/NovvyPauseAdViewNativeComponent.js.map +1 -0
- package/lib/commonjs/spec/NovvyScrollAdViewNativeComponent.js +17 -0
- package/lib/commonjs/spec/NovvyScrollAdViewNativeComponent.js.map +1 -0
- package/lib/commonjs/spec/codegen-event-emitter.d.js +2 -0
- package/lib/commonjs/spec/codegen-event-emitter.d.js.map +1 -0
- package/lib/commonjs/types.js +54 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/views/NovvyPauseAdView.js +76 -0
- package/lib/commonjs/views/NovvyPauseAdView.js.map +1 -0
- package/lib/commonjs/views/NovvyScrollAdView.js +63 -0
- package/lib/commonjs/views/NovvyScrollAdView.js.map +1 -0
- package/lib/module/NovvyAds.js +83 -0
- package/lib/module/NovvyAds.js.map +1 -0
- package/lib/module/ads/NovvyAd.js +117 -0
- package/lib/module/ads/NovvyAd.js.map +1 -0
- package/lib/module/ads/NovvyEndAd.js +15 -0
- package/lib/module/ads/NovvyEndAd.js.map +1 -0
- package/lib/module/ads/NovvyFullScreenAd.js +45 -0
- package/lib/module/ads/NovvyFullScreenAd.js.map +1 -0
- package/lib/module/ads/NovvyInterstitialAd.js +15 -0
- package/lib/module/ads/NovvyInterstitialAd.js.map +1 -0
- package/lib/module/ads/NovvyMidAd.js +15 -0
- package/lib/module/ads/NovvyMidAd.js.map +1 -0
- package/lib/module/ads/NovvyPauseAd.js +112 -0
- package/lib/module/ads/NovvyPauseAd.js.map +1 -0
- package/lib/module/ads/NovvyRewardedAd.js +49 -0
- package/lib/module/ads/NovvyRewardedAd.js.map +1 -0
- package/lib/module/ads/NovvyScrollAd.js +132 -0
- package/lib/module/ads/NovvyScrollAd.js.map +1 -0
- package/lib/module/eventBus.js +55 -0
- package/lib/module/eventBus.js.map +1 -0
- package/lib/module/index.js +22 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/spec/NativeNovvyAds.js +28 -0
- package/lib/module/spec/NativeNovvyAds.js.map +1 -0
- package/lib/module/spec/NovvyPauseAdViewNativeComponent.js +13 -0
- package/lib/module/spec/NovvyPauseAdViewNativeComponent.js.map +1 -0
- package/lib/module/spec/NovvyScrollAdViewNativeComponent.js +14 -0
- package/lib/module/spec/NovvyScrollAdViewNativeComponent.js.map +1 -0
- package/lib/module/spec/codegen-event-emitter.d.js +2 -0
- package/lib/module/spec/codegen-event-emitter.d.js.map +1 -0
- package/lib/module/types.js +56 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/views/NovvyPauseAdView.js +70 -0
- package/lib/module/views/NovvyPauseAdView.js.map +1 -0
- package/lib/module/views/NovvyScrollAdView.js +57 -0
- package/lib/module/views/NovvyScrollAdView.js.map +1 -0
- package/lib/typescript/NovvyAds.d.ts +69 -0
- package/lib/typescript/NovvyAds.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyAd.d.ts +57 -0
- package/lib/typescript/ads/NovvyAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyEndAd.d.ts +7 -0
- package/lib/typescript/ads/NovvyEndAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyFullScreenAd.d.ts +19 -0
- package/lib/typescript/ads/NovvyFullScreenAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyInterstitialAd.d.ts +7 -0
- package/lib/typescript/ads/NovvyInterstitialAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyMidAd.d.ts +7 -0
- package/lib/typescript/ads/NovvyMidAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyPauseAd.d.ts +45 -0
- package/lib/typescript/ads/NovvyPauseAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyRewardedAd.d.ts +19 -0
- package/lib/typescript/ads/NovvyRewardedAd.d.ts.map +1 -0
- package/lib/typescript/ads/NovvyScrollAd.d.ts +57 -0
- package/lib/typescript/ads/NovvyScrollAd.d.ts.map +1 -0
- package/lib/typescript/eventBus.d.ts +15 -0
- package/lib/typescript/eventBus.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +15 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/spec/NativeNovvyAds.d.ts +73 -0
- package/lib/typescript/spec/NativeNovvyAds.d.ts.map +1 -0
- package/lib/typescript/spec/NovvyPauseAdViewNativeComponent.d.ts +18 -0
- package/lib/typescript/spec/NovvyPauseAdViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/spec/NovvyScrollAdViewNativeComponent.d.ts +19 -0
- package/lib/typescript/spec/NovvyScrollAdViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +77 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/views/NovvyPauseAdView.d.ts +21 -0
- package/lib/typescript/views/NovvyPauseAdView.d.ts.map +1 -0
- package/lib/typescript/views/NovvyScrollAdView.d.ts +22 -0
- package/lib/typescript/views/NovvyScrollAdView.d.ts.map +1 -0
- package/novvy-ads-react-native.podspec +71 -0
- package/package.json +84 -0
- package/plugin/build/index.d.ts +11 -0
- package/plugin/build/index.js +53 -0
- package/react-native.config.js +7 -0
- package/src/NovvyAds.ts +98 -0
- package/src/ads/NovvyAd.ts +125 -0
- package/src/ads/NovvyEndAd.ts +13 -0
- package/src/ads/NovvyFullScreenAd.ts +46 -0
- package/src/ads/NovvyInterstitialAd.ts +13 -0
- package/src/ads/NovvyMidAd.ts +13 -0
- package/src/ads/NovvyPauseAd.ts +125 -0
- package/src/ads/NovvyRewardedAd.ts +49 -0
- package/src/ads/NovvyScrollAd.ts +138 -0
- package/src/eventBus.ts +59 -0
- package/src/index.tsx +29 -0
- package/src/spec/NativeNovvyAds.ts +134 -0
- package/src/spec/NovvyPauseAdViewNativeComponent.ts +20 -0
- package/src/spec/NovvyScrollAdViewNativeComponent.ts +21 -0
- package/src/spec/codegen-event-emitter.d.ts +10 -0
- package/src/types.ts +82 -0
- package/src/views/NovvyPauseAdView.tsx +80 -0
- package/src/views/NovvyScrollAdView.tsx +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) NovvyAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# novvy-ads-react-native
|
|
2
|
+
|
|
3
|
+
React Native SDK for **Novvy Ads** — short-drama vertical-video ad formats
|
|
4
|
+
(interstitial, rewarded, mid, end, scroll, pause). It wraps the native Novvy
|
|
5
|
+
Android & iOS SDKs behind a single TypeScript API, mirroring the design of the
|
|
6
|
+
official Flutter plugin.
|
|
7
|
+
|
|
8
|
+
- **New Architecture only** (TurboModule + Fabric). Requires **React Native ≥ 0.76**.
|
|
9
|
+
- **Framework-agnostic** — plain RN native module, **zero Expo runtime dependency**.
|
|
10
|
+
- Ships an **optional Expo config plugin** (build-time only) for Expo hosts.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install novvy-ads-react-native
|
|
18
|
+
# or
|
|
19
|
+
yarn add novvy-ads-react-native
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### iOS
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
cd ios && pod install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- The podspec downloads `NovvyAds.xcframework` (currently pinned to **1.1.0**) from the [NovvyAds cocoapods release](https://github.com/NovvyAI/novvy-ads-cocoapods/releases) automatically — no manual binary integration.
|
|
29
|
+
- Minimum deployment target **iOS 13.0**. Raise your Podfile's `platform :ios` to 13.0+ if it's lower.
|
|
30
|
+
- **No AdMob / Google-Mobile-Ads-SDK dependency** (removed in 1.1.0).
|
|
31
|
+
- The vendored framework ships **arm64 simulator only** (x86_64 simulator is excluded), so simulator builds require an **Apple Silicon Mac**. Intel Macs can only build for physical devices.
|
|
32
|
+
- Local development against an unreleased NovvyAds iOS SDK: set `NOVVY_ADS_LOCAL_XCFRAMEWORK=<path>` before `pod install` and the podspec will skip the download and use that path instead.
|
|
33
|
+
|
|
34
|
+
### Android
|
|
35
|
+
|
|
36
|
+
No extra steps — autolinking wires it up.
|
|
37
|
+
|
|
38
|
+
- The native SDK **`ai.novvy.android:sdk:1.1.0`** is pulled from **Maven Central**. Make sure `mavenCentral()` is in your root `build.gradle`'s `allprojects.repositories` (default in RN templates).
|
|
39
|
+
- Minimum **`minSdkVersion 24`** (Android 7.0). Raise `android/build.gradle`'s `minSdkVersion` if it's lower.
|
|
40
|
+
- Compiles with **JDK 17**; `compileSdkVersion` 34+ recommended.
|
|
41
|
+
|
|
42
|
+
### Expo (optional)
|
|
43
|
+
|
|
44
|
+
For managed / CNG (prebuild) Expo apps, add the config plugin. It only runs at
|
|
45
|
+
build time and adds **no** runtime dependency. Non-Expo hosts do **not** need it.
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"expo": {
|
|
50
|
+
"plugins": [
|
|
51
|
+
["novvy-ads-react-native", {
|
|
52
|
+
"iosDeploymentTarget": "13.0",
|
|
53
|
+
"androidMinSdkVersion": 24,
|
|
54
|
+
"userTrackingUsageDescription": "We use your data to show relevant ads."
|
|
55
|
+
}]
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then run `npx expo prebuild`. All options are optional:
|
|
62
|
+
|
|
63
|
+
| Option | Type | Default | Effect |
|
|
64
|
+
| ------ | ------ | ------ | ------ |
|
|
65
|
+
| `iosDeploymentTarget` | `string` | `"13.0"` | Raises `Podfile.properties`'s `ios.deploymentTarget` to at least this value |
|
|
66
|
+
| `androidMinSdkVersion` | `number` | `24` | Raises `gradle.properties`'s `android.minSdkVersion` to at least this value |
|
|
67
|
+
| `userTrackingUsageDescription` | `string` | — | If set, injects `NSUserTrackingUsageDescription` into `Info.plist` (iOS ATT copy) |
|
|
68
|
+
|
|
69
|
+
> New Architecture must be enabled (default on RN 0.76+). For Expo, ensure
|
|
70
|
+
> `newArchEnabled` is true.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import {
|
|
78
|
+
NovvyAds,
|
|
79
|
+
NovvyRewardedAd,
|
|
80
|
+
} from 'novvy-ads-react-native';
|
|
81
|
+
|
|
82
|
+
// 1. Initialize once at app startup.
|
|
83
|
+
const ok = await NovvyAds.initialize({
|
|
84
|
+
appId: 'YOUR_APP_ID',
|
|
85
|
+
endpoint: 'https://bid.novvy.ai',
|
|
86
|
+
apiKey: 'YOUR_API_KEY',
|
|
87
|
+
userContext: { userId: 'u_123', isPaidUser: false },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 2. Set content context before requesting ads (required for drama fill).
|
|
91
|
+
NovvyAds.setContentContext({ seriesName: 'My Drama', episodeNumber: 3 });
|
|
92
|
+
|
|
93
|
+
// 3. Create an ad, wire callbacks, load & show.
|
|
94
|
+
const ad = new NovvyRewardedAd('YOUR_AD_UNIT_ID');
|
|
95
|
+
let earned = false;
|
|
96
|
+
ad.onAdLoaded = () => ad.show();
|
|
97
|
+
ad.onUserEarnedReward = () => { earned = true; };
|
|
98
|
+
ad.onAdDismissed = () => { if (earned) grantReward(); };
|
|
99
|
+
ad.onAdFailedToLoad = (e) => console.warn('load failed', e);
|
|
100
|
+
ad.load();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Ad formats
|
|
106
|
+
|
|
107
|
+
| Class | Kind | Lifecycle |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| `NovvyInterstitialAd` | full-screen | `load()` → `onAdLoaded` → `show()` → `onAdDismissed` |
|
|
110
|
+
| `NovvyRewardedAd` | rewarded video | `load()` → `show()` → `onUserEarnedReward` → `onAdDismissed` |
|
|
111
|
+
| `NovvyMidAd` | full-screen (mid-roll) | `load()` → `show()` → `onAdDismissed` |
|
|
112
|
+
| `NovvyEndAd` | full-screen (end-roll) | `load()` → `show()` → `onAdDismissed` |
|
|
113
|
+
| `NovvyScrollAd` | inline feed video | `load()` → `<NovvyScrollAdView>` → `setPlaying()` |
|
|
114
|
+
| `NovvyPauseAd` | realtime pause card (260×90) | `<NovvyPauseAdView>` (no `load()`) |
|
|
115
|
+
|
|
116
|
+
> There is **no banner / native / app-open** format, and **no click callback**.
|
|
117
|
+
> `NovvyRewardedAd` grants a parameterless reward (no amount/type payload).
|
|
118
|
+
|
|
119
|
+
### Full-screen (interstitial / mid / end)
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { NovvyInterstitialAd } from 'novvy-ads-react-native';
|
|
123
|
+
|
|
124
|
+
const ad = new NovvyInterstitialAd('AD_UNIT_ID');
|
|
125
|
+
ad.onAdLoaded = () => ad.show();
|
|
126
|
+
ad.onAdShowed = () => {};
|
|
127
|
+
ad.onAdDismissed = () => {}; // ad auto-destroys after dismiss
|
|
128
|
+
ad.onAdFailedToLoad = (e) => {};
|
|
129
|
+
ad.load();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Scroll (inline feed) ad
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { NovvyScrollAd, NovvyScrollAdView } from 'novvy-ads-react-native';
|
|
136
|
+
|
|
137
|
+
const ad = useMemo(() => new NovvyScrollAd('AD_UNIT_ID', /* episodeNumber */ 3), []);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
ad.onAdLoaded = () => ad.setPlaying(true);
|
|
141
|
+
ad.onAdPlaybackTick = (remaining) => {/* update your skip UI */};
|
|
142
|
+
ad.load();
|
|
143
|
+
return () => ad.destroy();
|
|
144
|
+
}, [ad]);
|
|
145
|
+
|
|
146
|
+
// Give the view a fixed height in your feed item:
|
|
147
|
+
<View style={{ height: 640 }}>
|
|
148
|
+
<NovvyScrollAdView ad={ad} durationSeconds={5} useDefaultSwipeOverlay />
|
|
149
|
+
</View>
|
|
150
|
+
|
|
151
|
+
// Toggle playback from viewport visibility:
|
|
152
|
+
// ad.setPlaying(true) // scrolled into view
|
|
153
|
+
// ad.setPlaying(false) // scrolled out
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Pause card ad
|
|
157
|
+
|
|
158
|
+
`NovvyPauseAd` has **no load step** — mounting `<NovvyPauseAdView>` issues the
|
|
159
|
+
request. You must supply a `NovvyPlayerAdapter` that reports your video player's
|
|
160
|
+
position; the SDK uses it to detect resume and hide the card.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { NovvyPauseAd, NovvyPauseAdView, type NovvyPlayerAdapter } from 'novvy-ads-react-native';
|
|
164
|
+
|
|
165
|
+
// Adapter backed by your player's current state (read live each access):
|
|
166
|
+
const player: NovvyPlayerAdapter = {
|
|
167
|
+
get currentPositionMs() { return videoRef.current?.positionMs ?? 0; },
|
|
168
|
+
get isPlaying() { return videoRef.current?.isPlaying ?? false; },
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const ad = useMemo(() => new NovvyPauseAd('AD_UNIT_ID'), []);
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
ad.onAdShowed = () => {};
|
|
174
|
+
ad.onAdHidden = () => ad.destroy(); // resumed
|
|
175
|
+
return () => ad.destroy();
|
|
176
|
+
}, [ad]);
|
|
177
|
+
|
|
178
|
+
// Position within your player overlay (fixed 260×90):
|
|
179
|
+
<NovvyPauseAdView ad={ad} player={player} />
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## SDK API (`NovvyAds`)
|
|
185
|
+
|
|
186
|
+
| Method | Description |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `initialize(opts)` | `Promise<boolean>` — init with `appId` / `endpoint` / `apiKey` / optional `userContext`. |
|
|
189
|
+
| `setUserContext(ctx)` / `updateUserContext(partial)` | Replace / merge user targeting context. |
|
|
190
|
+
| `setContentContext(ctx)` / `updateContentContext(partial)` | Replace / merge series/episode context. Call before requesting drama ads. |
|
|
191
|
+
| `helperHashEmail(email)` | SHA-256 of the trimmed/lowercased email (pure JS). |
|
|
192
|
+
|
|
193
|
+
### Privacy / consent
|
|
194
|
+
|
|
195
|
+
There is no explicit GDPR/CCPA consent API. Privacy targeting is expressed
|
|
196
|
+
through `NovvyUserContext` / `NovvyContentContext`. On iOS, present the ATT
|
|
197
|
+
prompt yourself (the config plugin can add `NSUserTrackingUsageDescription`).
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## How it works
|
|
202
|
+
|
|
203
|
+
A single native → JS event (`onAdEvent`, carrying `adObjectId`) multiplexes
|
|
204
|
+
every ad's lifecycle; each ad object filters the stream by its own id, exactly
|
|
205
|
+
like the Flutter plugin. `adObjectId`s are generated JS-side. Inline formats use
|
|
206
|
+
Fabric view components that register a native container keyed by `adObjectId`.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.getExtOrDefault = { name ->
|
|
3
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NovvyAds_" + name]
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
repositories {
|
|
7
|
+
google()
|
|
8
|
+
mavenCentral()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
dependencies {
|
|
12
|
+
classpath "com.android.tools.build:gradle:8.6.0"
|
|
13
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def reactNativeArchitectures() {
|
|
18
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
19
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def isNewArchitectureEnabled() {
|
|
23
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
apply plugin: "com.android.library"
|
|
27
|
+
apply plugin: "kotlin-android"
|
|
28
|
+
|
|
29
|
+
if (isNewArchitectureEnabled()) {
|
|
30
|
+
apply plugin: "com.facebook.react"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def getExtOrIntegerDefault = { name ->
|
|
34
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NovvyAds_" + name]).toInteger()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
android {
|
|
38
|
+
namespace "ai.novvy.reactnative"
|
|
39
|
+
|
|
40
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
41
|
+
|
|
42
|
+
defaultConfig {
|
|
43
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
44
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
buildFeatures {
|
|
48
|
+
buildConfig true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
compileOptions {
|
|
52
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
53
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
kotlinOptions {
|
|
57
|
+
jvmTarget = "17"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
sourceSets {
|
|
61
|
+
main {
|
|
62
|
+
if (isNewArchitectureEnabled()) {
|
|
63
|
+
java.srcDirs += ["generated/java", "generated/jni"]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
repositories {
|
|
70
|
+
google()
|
|
71
|
+
mavenCentral()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
dependencies {
|
|
75
|
+
implementation "com.facebook.react:react-android"
|
|
76
|
+
|
|
77
|
+
// Novvy native Android ad SDK (published to Maven Central).
|
|
78
|
+
implementation "ai.novvy.android:sdk:1.1.0"
|
|
79
|
+
// For publishing the host Activity as a ViewTreeLifecycleOwner on the ad
|
|
80
|
+
// container so the SDK's inline host can observe background/foreground (pauses
|
|
81
|
+
// a VIDEO creative on click-through). Provided at runtime by RN + the Novvy
|
|
82
|
+
// SDK, so compileOnly avoids forcing a version.
|
|
83
|
+
compileOnly "androidx.lifecycle:lifecycle-runtime-ktx:2.8.7"
|
|
84
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package ai.novvy.reactnative
|
|
2
|
+
|
|
3
|
+
import android.widget.FrameLayout
|
|
4
|
+
import androidx.lifecycle.LifecycleOwner
|
|
5
|
+
import androidx.lifecycle.setViewTreeLifecycleOwner
|
|
6
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Container [FrameLayout] that hosts an SDK-rendered ad creative.
|
|
10
|
+
*
|
|
11
|
+
* Two RN/New-Arch integration gaps are patched here:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Layout of imperatively-added children.** Fabric only measures/lays out
|
|
14
|
+
* views in the JS shadow tree, and RN-managed ViewGroups swallow normal
|
|
15
|
+
* `requestLayout()` propagation. The Novvy SDK mounts its creative (video /
|
|
16
|
+
* image / native) via `addView` at runtime — that child is NOT in the shadow
|
|
17
|
+
* tree, so it never gets measured and stays 0×0 (blank/black slot; a VIDEO's
|
|
18
|
+
* ExoPlayer still decodes audio). [requestLayout] is overridden to post a
|
|
19
|
+
* manual measure + layout of our own subtree (the standard react-native-video
|
|
20
|
+
* / -webview / google-mobile-ads workaround). Converges rather than
|
|
21
|
+
* busy-looping because the SDK only re-fits on bounds *change*.
|
|
22
|
+
*
|
|
23
|
+
* 2. **Activity for click-through.** The SDK resolves the Activity used to open
|
|
24
|
+
* a creative's `cta_url` via `container.context as? Activity` (see
|
|
25
|
+
* `NovvyPlacementAd.attach` / `ClickOpenPipeline`). Under RN the view's
|
|
26
|
+
* context is a [ThemedReactContext], never an Activity, so that cast returns
|
|
27
|
+
* null and every click silently no-ops. `View.getContext()` is final and
|
|
28
|
+
* cannot be overridden, so we construct the view with the current Activity as
|
|
29
|
+
* its base context — `getContext()` then returns the Activity and the cast
|
|
30
|
+
* (and CTA open) works. RN-side event dispatch must read [reactContext]
|
|
31
|
+
* instead of `getContext()`, which is no longer the ReactContext.
|
|
32
|
+
*/
|
|
33
|
+
class NovvyAdContainerView(
|
|
34
|
+
/** The RN context. Kept separately because getContext() now returns the Activity. */
|
|
35
|
+
val reactContext: ThemedReactContext,
|
|
36
|
+
) : FrameLayout(reactContext.currentActivity ?: reactContext) {
|
|
37
|
+
|
|
38
|
+
init {
|
|
39
|
+
// RN does not reliably expose a ViewTreeLifecycleOwner on its view tree, so
|
|
40
|
+
// the SDK's inline host's `container.findViewTreeLifecycleOwner()` returns
|
|
41
|
+
// null and it never registers its onStop/onStart observer — meaning a VIDEO
|
|
42
|
+
// creative keeps playing (audio included) when a click-through opens the
|
|
43
|
+
// browser on top. Publish the host Activity (a LifecycleOwner) on this
|
|
44
|
+
// container so the SDK can observe background/foreground and pause/resume.
|
|
45
|
+
(reactContext.currentActivity as? LifecycleOwner)?.let { setViewTreeLifecycleOwner(it) }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private val measureAndLayout = Runnable {
|
|
49
|
+
measure(
|
|
50
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
51
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
|
|
52
|
+
)
|
|
53
|
+
layout(left, top, right, bottom)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun requestLayout() {
|
|
57
|
+
super.requestLayout()
|
|
58
|
+
// React Native won't lay out children that aren't in the shadow tree, so
|
|
59
|
+
// force a measure + layout pass; without it the SDK's imperatively-added
|
|
60
|
+
// creative stays 0×0 and the slot renders as a blank (black) container.
|
|
61
|
+
post(measureAndLayout)
|
|
62
|
+
}
|
|
63
|
+
}
|