@advergic-ads/react-native-sdk 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/AdvergicReactNative.podspec +32 -0
- package/LICENSE +21 -0
- package/README.md +329 -0
- package/android/advergic-bridge/build.gradle +58 -0
- package/android/advergic-bridge/libs/advergic-sdk.aar +0 -0
- package/android/advergic-bridge/src/main/java/com/advergic/reactnative/AdvergicBannerViewManager.kt +194 -0
- package/android/advergic-bridge/src/main/java/com/advergic/reactnative/AdvergicReactNativeModule.java +401 -0
- package/android/advergic-bridge/src/main/java/com/advergic/reactnative/AdvergicReactNativePackage.java +25 -0
- package/android/app/build.gradle +144 -0
- package/android/app/libs/advergic-sdk.aar +0 -0
- package/android/app/src/main/AndroidManifest.xml +30 -0
- package/android/app/src/main/java/com/advergic/reactnative/AdvergicReactNativePackage.java +26 -0
- package/android/app/src/main/java/com/facebook/react/viewmanagers/AdvergicBannerViewManagerInterface.java +8 -0
- package/android/libs/advergic-sdk.aar +0 -0
- package/android/settings.gradle +7 -0
- package/index.d.ts +135 -0
- package/index.js +27 -0
- package/install.js +158 -0
- package/ios/AdvergicBannerView.swift +59 -0
- package/ios/AdvergicBannerViewManager.m +18 -0
- package/ios/AdvergicBannerViewManager.swift +24 -0
- package/ios/AdvergicReactNativeModule.m +36 -0
- package/ios/AdvergicReactNativeModule.swift +363 -0
- package/package.json +57 -0
- package/react-native.config.js +18 -0
- package/src/advergic/AdvergicBannerAd.tsx +51 -0
- package/src/advergic/AdvergicSDK.ts +318 -0
- package/src/advergic/index.ts +11 -0
- package/src/advergic/types.ts +23 -0
|
@@ -0,0 +1,32 @@
|
|
|
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 = "AdvergicReactNative"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.author = package["author"]
|
|
12
|
+
s.platforms = { :ios => "15.0" }
|
|
13
|
+
s.source = { :git => "https://github.com/advergic/react-native-sdk.git", :tag => "#{s.version}" }
|
|
14
|
+
|
|
15
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
16
|
+
s.swift_version = "5.0"
|
|
17
|
+
s.requires_arc = true
|
|
18
|
+
|
|
19
|
+
# React Native core
|
|
20
|
+
s.dependency "React-Core"
|
|
21
|
+
|
|
22
|
+
# Native Advergic iOS SDK. It is NOT on the CocoaPods trunk — it is published
|
|
23
|
+
# via a podspec hosted at app.advergic.com (which always serves the latest
|
|
24
|
+
# version). The consuming app must therefore add the following line to its
|
|
25
|
+
# Podfile so CocoaPods can resolve this dependency:
|
|
26
|
+
#
|
|
27
|
+
# pod 'AdvergicSDK', :podspec => 'https://app.advergic.com/ios/sdk/AdvergicSDK.podspec'
|
|
28
|
+
#
|
|
29
|
+
# A `>=` floor (not `~>`) is used so future releases served at that single,
|
|
30
|
+
# always-latest podspec URL keep resolving.
|
|
31
|
+
s.dependency "AdvergicSDK", ">= 0.1.4"
|
|
32
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Advergic
|
|
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,329 @@
|
|
|
1
|
+
# Getting Started with Advergic React Native SDK
|
|
2
|
+
|
|
3
|
+
Welcome! This guide will help you integrate mobile advertising into your React Native app in just a few minutes.
|
|
4
|
+
|
|
5
|
+
## 📋 Prerequisites
|
|
6
|
+
|
|
7
|
+
- **React Native**: 0.85.3 or higher
|
|
8
|
+
- **Node.js**: 18.0 or higher
|
|
9
|
+
- **Android**: API 24+ (Android 7.0) for Android apps
|
|
10
|
+
- **iOS**: 15.0+ for iOS apps
|
|
11
|
+
- **API Key**: Get one from [app.advergic.com](https://app.advergic.com)
|
|
12
|
+
|
|
13
|
+
## 🚀 5-Minute Setup
|
|
14
|
+
|
|
15
|
+
### Step 1: Install the SDK
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @advergic-ads/react-native-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
or with Yarn:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
yarn add @advergic-ads/react-native-sdk
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Step 2: Android Setup (One Line!)
|
|
28
|
+
|
|
29
|
+
Add your Google Ads Application ID to `android/app/src/main/AndroidManifest.xml`:
|
|
30
|
+
|
|
31
|
+
```xml
|
|
32
|
+
<application>
|
|
33
|
+
<meta-data
|
|
34
|
+
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
|
35
|
+
android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />
|
|
36
|
+
</application>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Get your ID from [AdMob Console](https://admob.google.com).
|
|
40
|
+
|
|
41
|
+
**That's it!** React Native auto-linking handles the rest.
|
|
42
|
+
|
|
43
|
+
### Step 3: iOS Setup
|
|
44
|
+
|
|
45
|
+
The React Native module is auto-linked, but the native `AdvergicSDK` is **not**
|
|
46
|
+
published on the CocoaPods trunk — it ships from Advergic's own podspec. Add
|
|
47
|
+
that one line to your `Podfile` (alongside the iOS 15.0 deployment target),
|
|
48
|
+
then install:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# ios/Podfile
|
|
52
|
+
platform :ios, '15.0'
|
|
53
|
+
|
|
54
|
+
target 'YourApp' do
|
|
55
|
+
# ...existing config (use_react_native! etc.)...
|
|
56
|
+
|
|
57
|
+
# Required: makes the native Advergic iOS SDK resolvable by CocoaPods.
|
|
58
|
+
pod 'AdvergicSDK', :podspec => 'https://app.advergic.com/ios/sdk/AdvergicSDK.podspec'
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd ios && pod install && cd ..
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Once that line is present, CocoaPods resolves `AdvergicReactNative` (auto-linked)
|
|
67
|
+
and its `AdvergicSDK` dependency, and pulls in Google Mobile Ads automatically.
|
|
68
|
+
Prebid is bundled inside the `AdvergicSDK` framework.
|
|
69
|
+
|
|
70
|
+
> **Why the extra line?** CocoaPods can only resolve a pod's dependencies from
|
|
71
|
+
> the trunk or from sources declared in *your* `Podfile`. Since `AdvergicSDK` is
|
|
72
|
+
> distributed via a hosted podspec rather than the trunk, the consuming app must
|
|
73
|
+
> point CocoaPods at it. The hosted URL always serves the latest SDK version.
|
|
74
|
+
|
|
75
|
+
> **Note:** iOS supports banner, interstitial, rewarded, and app-open ads.
|
|
76
|
+
> Native ads are currently Android-only.
|
|
77
|
+
|
|
78
|
+
### Step 4: Initialize in Your App
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import React, { useEffect } from "react";
|
|
82
|
+
import Advergic from "@advergic-ads/react-native-sdk";
|
|
83
|
+
|
|
84
|
+
export default function App() {
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
// Initialize the SDK on app launch
|
|
87
|
+
Advergic.initialize("YOUR_API_KEY").then((initialized) => {
|
|
88
|
+
if (initialized) {
|
|
89
|
+
console.log("✓ Advergic SDK ready!");
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
// Your app content
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Step 5: Add Your First Banner Ad
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import { AdvergicBannerAd } from "@advergic-ads/react-native-sdk";
|
|
104
|
+
|
|
105
|
+
export default function Home() {
|
|
106
|
+
return (
|
|
107
|
+
<View>
|
|
108
|
+
{/* Your content */}
|
|
109
|
+
|
|
110
|
+
<AdvergicBannerAd
|
|
111
|
+
adUnitId="/23315275002/home_banner_320x50_ad_unit_id"
|
|
112
|
+
style={{ width: 320, height: 50, alignSelf: "center" }}
|
|
113
|
+
onAdLoaded={() => console.log("Ad loaded!")}
|
|
114
|
+
onAdFailedToLoad={(error) => console.error("Ad failed:", error)}
|
|
115
|
+
/>
|
|
116
|
+
</View>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
That's it! You now have banner ads running. 🎉
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 📦 Ad Formats
|
|
126
|
+
|
|
127
|
+
The SDK supports 4 ad formats:
|
|
128
|
+
|
|
129
|
+
### 1. **Banner Ads** ✓ Easiest
|
|
130
|
+
|
|
131
|
+
Rectangular ads that display inline with your content.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
<AdvergicBannerAd
|
|
135
|
+
adUnitId="/YOUR_NETWORK_ID/YOUR_BANNER_AD_UNIT"
|
|
136
|
+
style={{ width: 320, height: 50 }}
|
|
137
|
+
/>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Common sizes:**
|
|
141
|
+
- `320x50` — Standard mobile banner
|
|
142
|
+
- `300x250` — Medium rectangle
|
|
143
|
+
- `320x100` — Large banner
|
|
144
|
+
- `300x600` — Half page
|
|
145
|
+
|
|
146
|
+
### 2. **Interstitial Ads** — Full Screen
|
|
147
|
+
|
|
148
|
+
Full-screen ads shown between content (e.g., between game levels, after article read).
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import Advergic from "@advergic-ads/react-native-sdk";
|
|
152
|
+
|
|
153
|
+
// Show when appropriate
|
|
154
|
+
Advergic.showInterstitialAd("/YOUR_NETWORK_ID/YOUR_INTERSTITIAL_AD_UNIT");
|
|
155
|
+
|
|
156
|
+
// Listen for close
|
|
157
|
+
Advergic.onInterstitialAdClosed(() => {
|
|
158
|
+
console.log("User closed the ad");
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 3. **Rewarded Ads** — Earn Points
|
|
163
|
+
|
|
164
|
+
Video ads that reward users for watching (unlock premium, earn coins, etc.).
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
// Show when user wants premium content
|
|
168
|
+
Advergic.showRewardedAd("/YOUR_NETWORK_ID/YOUR_REWARDED_AD_UNIT");
|
|
169
|
+
|
|
170
|
+
// Listen for reward grant
|
|
171
|
+
Advergic.onRewardedAdUserEarnedReward(() => {
|
|
172
|
+
unlockPremiumContent();
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 4. **Native Ads** — Custom Design
|
|
177
|
+
|
|
178
|
+
Get ad data and render it your own way.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
const nativeAd = await Advergic.loadNativeAd("/YOUR_NETWORK_ID/YOUR_NATIVE_AD_UNIT");
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<View>
|
|
185
|
+
<Image source={{ uri: nativeAd.mainImage }} />
|
|
186
|
+
<Text>{nativeAd.title}</Text>
|
|
187
|
+
<Button title={nativeAd.callToAction} />
|
|
188
|
+
</View>
|
|
189
|
+
);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 📱 Your First Complete App
|
|
195
|
+
|
|
196
|
+
Here's a minimal app with both banner and interstitial ads:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import React, { useEffect, useState } from "react";
|
|
200
|
+
import { View, Text, Button, StyleSheet, ScrollView } from "react-native";
|
|
201
|
+
import Advergic, { AdvergicBannerAd } from "@advergic-ads/react-native-sdk";
|
|
202
|
+
|
|
203
|
+
const API_KEY = "YOUR_API_KEY";
|
|
204
|
+
|
|
205
|
+
export default function App() {
|
|
206
|
+
const [initialized, setInitialized] = useState(false);
|
|
207
|
+
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
// Initialize SDK
|
|
210
|
+
Advergic.initialize(API_KEY).then(setInitialized);
|
|
211
|
+
|
|
212
|
+
// Listen for interstitial close
|
|
213
|
+
const sub = Advergic.onInterstitialAdClosed(() => {
|
|
214
|
+
console.log("Interstitial closed");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return () => sub.remove();
|
|
218
|
+
}, []);
|
|
219
|
+
|
|
220
|
+
const showInterstitial = () => {
|
|
221
|
+
if (initialized) {
|
|
222
|
+
Advergic.showInterstitialAd(
|
|
223
|
+
"/23315275002/start_game_interstitial_ad_unit_id"
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<ScrollView style={styles.container}>
|
|
230
|
+
<Text style={styles.title}>My App</Text>
|
|
231
|
+
|
|
232
|
+
{/* Top banner ad */}
|
|
233
|
+
<AdvergicBannerAd
|
|
234
|
+
adUnitId="/23315275002/home_banner_320x50_ad_unit_id"
|
|
235
|
+
style={{ width: 320, height: 50, alignSelf: "center" }}
|
|
236
|
+
/>
|
|
237
|
+
|
|
238
|
+
{/* App content */}
|
|
239
|
+
<Text style={styles.text}>Welcome to your app!</Text>
|
|
240
|
+
|
|
241
|
+
<Button
|
|
242
|
+
title="Show Interstitial Ad"
|
|
243
|
+
onPress={showInterstitial}
|
|
244
|
+
disabled={!initialized}
|
|
245
|
+
/>
|
|
246
|
+
|
|
247
|
+
{/* Bottom banner ad */}
|
|
248
|
+
<AdvergicBannerAd
|
|
249
|
+
adUnitId="/23315275002/home_screen_top_banner_video_300x250_ad_unit_id"
|
|
250
|
+
style={{ width: 300, height: 250, alignSelf: "center", marginVertical: 20 }}
|
|
251
|
+
/>
|
|
252
|
+
</ScrollView>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const styles = StyleSheet.create({
|
|
257
|
+
container: { flex: 1, padding: 20, backgroundColor: "#f5f5f5" },
|
|
258
|
+
title: { fontSize: 28, fontWeight: "bold", marginBottom: 20 },
|
|
259
|
+
text: { fontSize: 16, marginBottom: 20, color: "#333" },
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 🔑 Getting Your API Key & Ad Unit IDs
|
|
266
|
+
|
|
267
|
+
1. Sign up at [app.advergic.com](https://app.advergic.com)
|
|
268
|
+
2. Create a new app
|
|
269
|
+
3. Copy your **API Key** (use in `Advergic.initialize()`)
|
|
270
|
+
4. Go to **Inventory → Ad Units** to create ad units
|
|
271
|
+
5. Copy the **Ad Unit IDs** and use them in your components
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 🐛 Troubleshooting
|
|
276
|
+
|
|
277
|
+
### "Banner is blank"
|
|
278
|
+
- Ensure `Advergic.initialize()` is called before rendering banners
|
|
279
|
+
- Check that the app has internet access
|
|
280
|
+
- Verify the ad unit ID is correct
|
|
281
|
+
- Add `onAdFailedToLoad` callback to see error details
|
|
282
|
+
|
|
283
|
+
### "Interstitial doesn't show"
|
|
284
|
+
- Make sure you're using the correct ad unit ID (should be an interstitial, not banner)
|
|
285
|
+
- Call `showInterstitialAd()` after the app is initialized
|
|
286
|
+
- Some devices may not have ads available; add error handling
|
|
287
|
+
|
|
288
|
+
### "Module not found" on Android
|
|
289
|
+
- Run `npm install` again
|
|
290
|
+
- Try `npm start -- --reset-cache` to clear React Native cache
|
|
291
|
+
- Delete `node_modules` and reinstall if still broken
|
|
292
|
+
|
|
293
|
+
### Debug Mode
|
|
294
|
+
|
|
295
|
+
Enable verbose logging in Android logcat:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
adb logcat | grep -E "Advergic|Banner"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 📚 Next Steps
|
|
304
|
+
|
|
305
|
+
- **Read the full docs**: [README.md](./README.md)
|
|
306
|
+
- **Check examples**: [examples/dramafy/](./examples/dramafy/)
|
|
307
|
+
- **API Reference**: [README.md → API Reference](./README.md#api-reference)
|
|
308
|
+
- **Troubleshooting**: [README.md → Troubleshooting](./README.md#troubleshooting)
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 💡 Tips
|
|
313
|
+
|
|
314
|
+
✅ **Do:**
|
|
315
|
+
- Use fixed pixel dimensions for banner size (not percentages)
|
|
316
|
+
- Initialize SDK early in your app lifecycle
|
|
317
|
+
- Test with your own app before going live
|
|
318
|
+
- Monitor fill rates in the dashboard
|
|
319
|
+
|
|
320
|
+
❌ **Don't:**
|
|
321
|
+
- Change ad unit IDs frequently (disrupts analytics)
|
|
322
|
+
- Show multiple ads simultaneously in the same location
|
|
323
|
+
- Initialize the SDK multiple times
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 🤝 Need Help?
|
|
328
|
+
|
|
329
|
+
- **Email**: hello@advergic.com
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
plugins {
|
|
2
|
+
id 'com.android.library'
|
|
3
|
+
id 'org.jetbrains.kotlin.android'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
android {
|
|
7
|
+
namespace 'com.advergic.reactnative'
|
|
8
|
+
compileSdk 35
|
|
9
|
+
|
|
10
|
+
defaultConfig {
|
|
11
|
+
minSdk 24
|
|
12
|
+
targetSdk 35
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
compileOptions {
|
|
16
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
17
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
repositories {
|
|
22
|
+
flatDir {
|
|
23
|
+
// Try multiple locations for the AAR
|
|
24
|
+
dirs file("${projectDir}/libs")
|
|
25
|
+
dirs file("${projectDir.absolutePath}/../libs")
|
|
26
|
+
dirs file("${rootDir}/app/libs")
|
|
27
|
+
dirs file(rootDir).parentFile.absolutePath + "/node_modules/@advergic/advergic-react-native/android/libs"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
dependencies {
|
|
32
|
+
api(name: 'advergic-sdk', ext: 'aar')
|
|
33
|
+
|
|
34
|
+
// React Native
|
|
35
|
+
compileOnly 'com.facebook.react:react-android'
|
|
36
|
+
compileOnly 'com.facebook.react:react-native'
|
|
37
|
+
|
|
38
|
+
// AndroidX and Lifecycle
|
|
39
|
+
api 'androidx.lifecycle:lifecycle-livedata-ktx:2.9.0'
|
|
40
|
+
api 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.0'
|
|
41
|
+
api 'androidx.room:room-runtime:2.7.0'
|
|
42
|
+
api 'androidx.room:room-ktx:2.7.0'
|
|
43
|
+
|
|
44
|
+
// Google Play Services and Material
|
|
45
|
+
api 'com.google.android.material:material:1.9.0'
|
|
46
|
+
api 'com.google.code.gson:gson:2.10.1'
|
|
47
|
+
api 'com.google.android.gms:play-services-ads:24.6.0'
|
|
48
|
+
api 'com.google.android.gms:play-services-ads-identifier:18.2.0'
|
|
49
|
+
|
|
50
|
+
// Third-party libraries
|
|
51
|
+
api 'com.applovin:applovin-sdk:13.2.0'
|
|
52
|
+
api 'com.squareup.retrofit2:retrofit:2.9.0'
|
|
53
|
+
api 'com.squareup.retrofit2:converter-gson:2.9.0'
|
|
54
|
+
api 'com.squareup.okhttp3:okhttp:4.11.0'
|
|
55
|
+
api 'com.github.bumptech.glide:glide:4.15.1'
|
|
56
|
+
api 'io.opentelemetry:opentelemetry-api:1.42.1'
|
|
57
|
+
api 'androidx.multidex:multidex:2.0.1'
|
|
58
|
+
}
|
|
Binary file
|
package/android/advergic-bridge/src/main/java/com/advergic/reactnative/AdvergicBannerViewManager.kt
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
package com.advergic.reactnative
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.view.ViewTreeObserver
|
|
7
|
+
import android.widget.FrameLayout
|
|
8
|
+
import com.advergic.sdk.core.AdvergicSdk
|
|
9
|
+
import com.advergic.sdk.interfaces.BannerAdCallback
|
|
10
|
+
import com.facebook.react.bridge.Arguments
|
|
11
|
+
import com.facebook.react.bridge.ReadableArray
|
|
12
|
+
import com.facebook.react.bridge.WritableMap
|
|
13
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
14
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
17
|
+
import com.facebook.react.uimanager.events.Event
|
|
18
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
19
|
+
|
|
20
|
+
private const val TAG = "AdvergicBanner"
|
|
21
|
+
|
|
22
|
+
class AdvergicBannerViewManager : SimpleViewManager<FrameLayout>() {
|
|
23
|
+
|
|
24
|
+
companion object {
|
|
25
|
+
const val REACT_CLASS = "AdvergicBannerView"
|
|
26
|
+
private const val COMMAND_LOAD_AD = "loadAd"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
override fun getName(): String = REACT_CLASS
|
|
30
|
+
|
|
31
|
+
override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
|
|
32
|
+
return AdvergicBannerContainerView(reactContext)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@ReactProp(name = "adUnitId")
|
|
36
|
+
fun setAdUnitId(view: FrameLayout, adUnitId: String?) {
|
|
37
|
+
if (view is AdvergicBannerContainerView && adUnitId != null) {
|
|
38
|
+
view.setAdUnitId(adUnitId)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any>? {
|
|
43
|
+
return mapOf(
|
|
44
|
+
"onAdLoaded" to mapOf("phasedRegistrationNames" to mapOf("bubbled" to "onAdLoaded")),
|
|
45
|
+
"onAdFailedToLoad" to mapOf("phasedRegistrationNames" to mapOf("bubbled" to "onAdFailedToLoad")),
|
|
46
|
+
"onAdClicked" to mapOf("phasedRegistrationNames" to mapOf("bubbled" to "onAdClicked")),
|
|
47
|
+
"onAdImpression" to mapOf("phasedRegistrationNames" to mapOf("bubbled" to "onAdImpression"))
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override fun receiveCommand(view: FrameLayout, commandId: String, args: ReadableArray?) {
|
|
52
|
+
if (COMMAND_LOAD_AD == commandId && view is AdvergicBannerContainerView) {
|
|
53
|
+
view.loadAd()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class AdvergicBannerContainerView(private val reactContext: ThemedReactContext) : FrameLayout(reactContext) {
|
|
58
|
+
|
|
59
|
+
private var adUnitId: String? = null
|
|
60
|
+
private var adLoaded = false
|
|
61
|
+
private var hasLoadedAd = false
|
|
62
|
+
private var loadRetries = 0
|
|
63
|
+
private val maxLoadRetries = 10
|
|
64
|
+
|
|
65
|
+
fun setAdUnitId(id: String) {
|
|
66
|
+
adUnitId = id
|
|
67
|
+
if (!hasLoadedAd) {
|
|
68
|
+
loadAd()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fun loadAd() {
|
|
73
|
+
val activity = reactContext.currentActivity
|
|
74
|
+
Log.d(TAG, "loadAd called - activity=$activity, adUnitId=$adUnitId, viewId=$id")
|
|
75
|
+
|
|
76
|
+
if (activity == null || adUnitId == null) {
|
|
77
|
+
Log.e(TAG, "Cannot load ad: activity=$activity, adUnitId=$adUnitId")
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
hasLoadedAd = true
|
|
82
|
+
activity.runOnUiThread {
|
|
83
|
+
loadAdWithRetry(activity)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private fun loadAdWithRetry(activity: Activity) {
|
|
88
|
+
try {
|
|
89
|
+
Log.d(TAG, "Calling showBannerAd for $adUnitId (attempt ${loadRetries + 1})")
|
|
90
|
+
AdvergicSdk.showBannerAd(activity, adUnitId!!, this, object : BannerAdCallback {
|
|
91
|
+
override fun onAdLoaded() {
|
|
92
|
+
Log.d(TAG, "Banner ad loaded: $adUnitId")
|
|
93
|
+
adLoaded = true
|
|
94
|
+
post { forceLayoutPass() }
|
|
95
|
+
postDelayed({
|
|
96
|
+
applyCreativeSizeFixOnAllChildren()
|
|
97
|
+
forceLayoutPass()
|
|
98
|
+
sendEvent("onAdLoaded", null)
|
|
99
|
+
}, 100)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
override fun onAdFailedToLoad(e: String) {
|
|
103
|
+
Log.e(TAG, "Banner ad failed: $adUnitId - $e")
|
|
104
|
+
if (e.contains("not initialized", ignoreCase = true) && loadRetries < maxLoadRetries) {
|
|
105
|
+
loadRetries++
|
|
106
|
+
postDelayed({ loadAdWithRetry(activity) }, 300L * loadRetries)
|
|
107
|
+
Log.d(TAG, "Retrying ad load in ${300L * loadRetries}ms")
|
|
108
|
+
} else {
|
|
109
|
+
sendEvent("onAdFailedToLoad", createErrorMap(e))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
override fun onAdClicked() {
|
|
114
|
+
Log.d(TAG, "Banner ad clicked: $adUnitId")
|
|
115
|
+
sendEvent("onAdClicked", null)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun onAdImpression() {
|
|
119
|
+
Log.d(TAG, "Banner ad impression: $adUnitId")
|
|
120
|
+
sendEvent("onAdImpression", null)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
} catch (e: Exception) {
|
|
124
|
+
Log.e(TAG, "Exception calling showBannerAd: ${e.message}", e)
|
|
125
|
+
sendEvent("onAdFailedToLoad", createErrorMap(e.message ?: "Unknown error"))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private fun createErrorMap(error: String): WritableMap {
|
|
130
|
+
val map = Arguments.createMap()
|
|
131
|
+
map.putString("error", error)
|
|
132
|
+
return map
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private inner class BannerAdEvent(
|
|
136
|
+
val eventNameValue: String,
|
|
137
|
+
val params: WritableMap?
|
|
138
|
+
) : Event<BannerAdEvent>(id) {
|
|
139
|
+
override fun getEventName() = eventNameValue
|
|
140
|
+
override fun dispatch(rctEventEmitter: RCTEventEmitter) {
|
|
141
|
+
rctEventEmitter.receiveEvent(id, eventNameValue, params)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
146
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcher(reactContext)
|
|
147
|
+
if (eventDispatcher != null) {
|
|
148
|
+
eventDispatcher.dispatchEvent(BannerAdEvent(eventName, params))
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private fun dpToPx(dp: Int): Int {
|
|
153
|
+
return (dp * resources.displayMetrics.density).toInt()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private fun forceLayoutPass() {
|
|
157
|
+
val w = width
|
|
158
|
+
val h = height
|
|
159
|
+
if (w <= 0 || h <= 0) return
|
|
160
|
+
measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
|
|
161
|
+
MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY))
|
|
162
|
+
layout(0, 0, w, h)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private fun applyCreativeSizeFixOnAllChildren() {
|
|
166
|
+
for (i in 0 until childCount) {
|
|
167
|
+
applyCreativeSizeFix(getChildAt(i), 0)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private fun applyCreativeSizeFix(view: View?, attempt: Int) {
|
|
172
|
+
if (view == null) return
|
|
173
|
+
if (attempt > 10) return
|
|
174
|
+
|
|
175
|
+
val w = view.width
|
|
176
|
+
val h = view.height
|
|
177
|
+
|
|
178
|
+
if (w > 0 && h > dpToPx(10)) {
|
|
179
|
+
measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
|
|
180
|
+
MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY))
|
|
181
|
+
layout(0, 0, w, h)
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
view.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
|
|
186
|
+
override fun onPreDraw(): Boolean {
|
|
187
|
+
view.viewTreeObserver.removeOnPreDrawListener(this)
|
|
188
|
+
applyCreativeSizeFix(view, attempt + 1)
|
|
189
|
+
return true
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|