@1selfworld/adchain-sdk-react-native 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +211 -0
- package/adchain-sdk-react-native.podspec +25 -0
- package/android/build.gradle +38 -0
- package/android/src/main/java/com/adchain/exposdk/AdchainOfferwallViewManager.kt +288 -0
- package/android/src/main/java/com/adchain/exposdk/AdchainSdkModule.kt +742 -0
- package/android/src/main/java/com/adchain/exposdk/AdchainSdkPackage.kt +14 -0
- package/app.plugin.js +17 -0
- package/ios/AdchainOfferwallViewManager.m +17 -0
- package/ios/AdchainOfferwallViewManager.swift +242 -0
- package/ios/AdchainSdk.m +87 -0
- package/ios/AdchainSdk.swift +792 -0
- package/lib/module/index.js +162 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +88 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +153 -0
- package/plugin/src/withAdchainAndroid.js +56 -0
- package/plugin/src/withAdchainConfig.js +22 -0
- package/plugin/src/withAdchainIOS.js +60 -0
- package/react-native.config.js +8 -0
- package/src/index.tsx +274 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 1SelfWorld Labs
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# @adchain/expo-sdk
|
|
2
|
+
|
|
3
|
+
AdChain SDK for React Native with full Expo support.
|
|
4
|
+
|
|
5
|
+
## π¨ Important Notices
|
|
6
|
+
|
|
7
|
+
### Expo Go Not Supported
|
|
8
|
+
This SDK includes native code and **does not work in Expo Go**.
|
|
9
|
+
|
|
10
|
+
You must use:
|
|
11
|
+
- β
Development Build: `npx expo run:android` or `npx expo run:ios`
|
|
12
|
+
- β
EAS Build: `eas build --profile development`
|
|
13
|
+
|
|
14
|
+
### π Security Notice
|
|
15
|
+
app.jsonμ μ μ₯λ API ν€λ μ± λ²λ€μ ν¬ν¨λ©λλ€.
|
|
16
|
+
|
|
17
|
+
**κΆμ₯μ¬ν:**
|
|
18
|
+
- β
**κ°λ°/ν
μ€νΈ:** app.json μ¬μ© OK
|
|
19
|
+
- β οΈ **νλ‘λμ
:** νκ²½λ³ ν€ λΆλ¦¬ κΆμ₯
|
|
20
|
+
- Android: `buildConfigField` μ¬μ©
|
|
21
|
+
- iOS: `xcconfig` νμΌ μ¬μ©
|
|
22
|
+
- β **μ λ κΈμ§:** Private API ν€, κ²°μ /μΈμ¦ λΉλ°ν€ λ
ΈμΆ
|
|
23
|
+
|
|
24
|
+
**λμ:**
|
|
25
|
+
```typescript
|
|
26
|
+
// λ°νμμ μλ²μμ ν ν° λ°κΈ
|
|
27
|
+
const token = await fetchTokenFromServer();
|
|
28
|
+
await AdchainSDK.initializeWithToken(token);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx expo install @adchain/expo-sdk expo-constants
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Note:** `expo-constants` is a peer dependency. Install it separately.
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
Add to your `app.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"expo": {
|
|
46
|
+
"plugins": [
|
|
47
|
+
[
|
|
48
|
+
"@adchain/expo-sdk",
|
|
49
|
+
{
|
|
50
|
+
"appKey": "YOUR_APP_KEY",
|
|
51
|
+
"appSecret": "YOUR_APP_SECRET",
|
|
52
|
+
"environment": "PRODUCTION"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { useEffect } from 'react';
|
|
64
|
+
import AdchainSDK from '@adchain/expo-sdk';
|
|
65
|
+
|
|
66
|
+
export default function App() {
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
// Auto-initialize using app.json config
|
|
69
|
+
AdchainSDK.autoInitialize()
|
|
70
|
+
.then(() => console.log('SDK ready'))
|
|
71
|
+
.catch(console.error);
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
const handleLogin = async () => {
|
|
75
|
+
await AdchainSDK.login({ userId: 'user123' });
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleOfferwall = async () => {
|
|
79
|
+
await AdchainSDK.openOfferwall('placement-id');
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## API Reference
|
|
85
|
+
|
|
86
|
+
### Core Methods
|
|
87
|
+
- `autoInitialize()` - Auto-initialize using app.json config
|
|
88
|
+
- `initialize(config)` - Manual initialization
|
|
89
|
+
- `login(user)` - User login
|
|
90
|
+
- `logout()` - User logout
|
|
91
|
+
- `isLoggedIn()` - Check login status
|
|
92
|
+
- `getCurrentUser()` - Get current user info
|
|
93
|
+
|
|
94
|
+
### Offerwall
|
|
95
|
+
- `openOfferwall(placementId?)` - Open offerwall
|
|
96
|
+
- `openAdjoeOfferwall(placementId?)` - Open ADJOE offerwall
|
|
97
|
+
|
|
98
|
+
### Quiz & Mission
|
|
99
|
+
- `loadQuizList(unitId)` - Load quiz list
|
|
100
|
+
- `clickQuiz(unitId, quizId)` - Click quiz
|
|
101
|
+
- `loadMissionList(unitId)` - Load mission list
|
|
102
|
+
- `clickMission(unitId, missionId)` - Click mission
|
|
103
|
+
- `claimReward(unitId)` - Claim reward
|
|
104
|
+
|
|
105
|
+
### Debug
|
|
106
|
+
- `getUserId()` - Get current user ID
|
|
107
|
+
- `getIFA()` - Get advertising ID
|
|
108
|
+
- `isInitialized()` - Check SDK initialization
|
|
109
|
+
|
|
110
|
+
## π§ Technical Notes
|
|
111
|
+
|
|
112
|
+
### Swift 5.5 (Not 5.9)
|
|
113
|
+
This SDK uses **Swift 5.5** due to known issues with optional parameter passing
|
|
114
|
+
in React Native bridges with Swift 5.9.
|
|
115
|
+
|
|
116
|
+
**Background:** In production, we experienced bugs where optional parameters in
|
|
117
|
+
Swift 5.9 were not properly passed to React Native, causing silent failures.
|
|
118
|
+
|
|
119
|
+
Tested and stable with:
|
|
120
|
+
- Xcode 14.x - 15.x
|
|
121
|
+
- Swift 5.5
|
|
122
|
+
- iOS 14.0+
|
|
123
|
+
|
|
124
|
+
### Kotlin Standard Library
|
|
125
|
+
The SDK includes `kotlin-stdlib:1.9.21` as required by the native AdChain SDK.
|
|
126
|
+
If you experience version conflicts, Gradle will automatically resolve to the
|
|
127
|
+
host app's version.
|
|
128
|
+
|
|
129
|
+
### React Native Dependency
|
|
130
|
+
The SDK uses `compileOnly` for `react-android` to avoid duplicate classes in
|
|
131
|
+
the AAR. The dependency is provided by the host application.
|
|
132
|
+
|
|
133
|
+
## Build & Run
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Prebuild (generate native code)
|
|
137
|
+
npx expo prebuild
|
|
138
|
+
|
|
139
|
+
# Verify plugin applied
|
|
140
|
+
cat android/settings.gradle | grep jitpack
|
|
141
|
+
cat ios/Podfile | grep AdChainSDK
|
|
142
|
+
|
|
143
|
+
# Run on Android
|
|
144
|
+
npx expo run:android
|
|
145
|
+
|
|
146
|
+
# Run on iOS
|
|
147
|
+
npx expo run:ios
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Requirements
|
|
151
|
+
|
|
152
|
+
- Expo SDK 50+
|
|
153
|
+
- React Native 0.74+
|
|
154
|
+
- iOS 14.0+
|
|
155
|
+
- Android API 24+
|
|
156
|
+
- **Development Build** (Expo Go not supported)
|
|
157
|
+
|
|
158
|
+
## Troubleshooting
|
|
159
|
+
|
|
160
|
+
### "SDK not initialized" error
|
|
161
|
+
- Make sure you added the plugin to app.json
|
|
162
|
+
- Check that appKey and appSecret are correct
|
|
163
|
+
- Verify you called `autoInitialize()` before other methods
|
|
164
|
+
|
|
165
|
+
### "Expo config not found" error
|
|
166
|
+
- You're probably using Expo Go (not supported)
|
|
167
|
+
- Use: `npx expo run:android` or `npx expo run:ios`
|
|
168
|
+
|
|
169
|
+
### "expo-constants not found"
|
|
170
|
+
- Install peer dependency: `npx expo install expo-constants`
|
|
171
|
+
|
|
172
|
+
### Prebuild fails
|
|
173
|
+
- Check @expo/config-plugins is in devDependencies
|
|
174
|
+
- Try: `rm -rf node_modules && npm install`
|
|
175
|
+
|
|
176
|
+
### JitPack not found (Android)
|
|
177
|
+
- Verify settings.gradle has maven { url "https://jitpack.io" }
|
|
178
|
+
- Check internet connection
|
|
179
|
+
- Try: cd android && ./gradlew clean
|
|
180
|
+
|
|
181
|
+
### Pod install fails (iOS)
|
|
182
|
+
- Check Git URL is correct
|
|
183
|
+
- Verify tag v1.0.42 exists
|
|
184
|
+
- Try: cd ios && rm -rf Pods Podfile.lock && pod install
|
|
185
|
+
|
|
186
|
+
## Version Compatibility
|
|
187
|
+
|
|
188
|
+
| Package Version | Android SDK | iOS SDK | Swift | Expo SDK |
|
|
189
|
+
|----------------|-------------|---------|-------|----------|
|
|
190
|
+
| 1.0.0 | v1.0.28 | v1.0.42 | 5.5 | 50-53 |
|
|
191
|
+
|
|
192
|
+
## Known Limitations
|
|
193
|
+
|
|
194
|
+
### v1.0.0
|
|
195
|
+
- OTA updates (expo-updates) not yet supported - use Development Build
|
|
196
|
+
- Swift 5.9 optional parameter bug - staying on 5.5
|
|
197
|
+
- CocoaPods Trunk: AdChainSDK uses Git Pod (not on Trunk yet)
|
|
198
|
+
|
|
199
|
+
### Planned for v1.1.0
|
|
200
|
+
- expo-updates fallback support
|
|
201
|
+
- Conditional iOS plugin (Trunk vs Git)
|
|
202
|
+
- Server-side token initialization
|
|
203
|
+
- Enhanced error messages
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
Proprietary
|
|
208
|
+
|
|
209
|
+
## Support
|
|
210
|
+
|
|
211
|
+
Email: contacts@1self.world
|
|
@@ -0,0 +1,25 @@
|
|
|
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 = "adchain-sdk-react-native"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => "14.0" }
|
|
14
|
+
s.source = { :git => "https://github.com/1selfworld-labs/adchain-sdk-react-native.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
|
|
18
|
+
# Swift 5.5 maintained (avoiding 5.9 optional parameter bugs)
|
|
19
|
+
# In production, we experienced bugs where optional parameters in Swift 5.9
|
|
20
|
+
# were not properly passed to React Native bridges, causing silent failures.
|
|
21
|
+
s.swift_version = '5.5'
|
|
22
|
+
|
|
23
|
+
s.dependency "React-Core"
|
|
24
|
+
s.dependency "AdChainSDK"
|
|
25
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
def safeExtGet(prop, fallback) {
|
|
2
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
apply plugin: 'com.android.library'
|
|
6
|
+
apply plugin: 'org.jetbrains.kotlin.android'
|
|
7
|
+
|
|
8
|
+
android {
|
|
9
|
+
namespace "com.adchain.exposdk"
|
|
10
|
+
|
|
11
|
+
compileSdk safeExtGet('compileSdkVersion', 34)
|
|
12
|
+
|
|
13
|
+
defaultConfig {
|
|
14
|
+
minSdkVersion safeExtGet('minSdkVersion', 24)
|
|
15
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 34)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
compileOptions {
|
|
19
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
20
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
kotlinOptions {
|
|
24
|
+
jvmTarget = '17'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
lintOptions {
|
|
28
|
+
abortOnError false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Repositories are managed by the host app's settings.gradle
|
|
33
|
+
|
|
34
|
+
dependencies {
|
|
35
|
+
implementation 'com.facebook.react:react-native:+'
|
|
36
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.21"
|
|
37
|
+
implementation 'com.github.1selfworld-labs:adchain-sdk-android:v1.0.28'
|
|
38
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
package com.adchain.exposdk
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
5
|
+
import com.facebook.react.bridge.ReactContext
|
|
6
|
+
import com.facebook.react.bridge.ReadableArray
|
|
7
|
+
import com.facebook.react.bridge.WritableMap
|
|
8
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
9
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
10
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
|
+
import com.adchain.sdk.core.AdchainSdk
|
|
12
|
+
import com.adchain.sdk.offerwall.AdchainOfferwallView
|
|
13
|
+
import com.adchain.sdk.offerwall.OfferwallCallback
|
|
14
|
+
import com.adchain.sdk.offerwall.OfferwallEventCallback
|
|
15
|
+
|
|
16
|
+
class AdchainOfferwallViewManager : SimpleViewManager<AdchainOfferwallView>() {
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
private const val TAG = "OfferwallViewManager"
|
|
20
|
+
private const val REACT_CLASS = "AdchainOfferwallView"
|
|
21
|
+
private const val COMMAND_LOAD_OFFERWALL = 1
|
|
22
|
+
private const val COMMAND_HANDLE_BACK_PRESS = 2
|
|
23
|
+
private const val COMMAND_SEND_DATA_RESPONSE = 3 // NEW
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override fun getName() = REACT_CLASS
|
|
27
|
+
|
|
28
|
+
override fun createViewInstance(reactContext: ThemedReactContext): AdchainOfferwallView {
|
|
29
|
+
Log.d(TAG, "createViewInstance called")
|
|
30
|
+
return AdchainOfferwallView(reactContext)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun getCommandsMap(): Map<String, Int> {
|
|
34
|
+
Log.d(TAG, "getCommandsMap called")
|
|
35
|
+
return mapOf(
|
|
36
|
+
"loadOfferwall" to COMMAND_LOAD_OFFERWALL,
|
|
37
|
+
"handleBackPress" to COMMAND_HANDLE_BACK_PRESS,
|
|
38
|
+
"sendDataResponse" to COMMAND_SEND_DATA_RESPONSE // NEW
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Store pending data request responses
|
|
43
|
+
private val pendingDataResponses = mutableMapOf<String, Map<String, Any?>>()
|
|
44
|
+
|
|
45
|
+
override fun receiveCommand(
|
|
46
|
+
root: AdchainOfferwallView,
|
|
47
|
+
commandId: String?,
|
|
48
|
+
args: ReadableArray?
|
|
49
|
+
) {
|
|
50
|
+
Log.d(TAG, "receiveCommand called - commandId: $commandId, args: $args")
|
|
51
|
+
when (commandId) {
|
|
52
|
+
"loadOfferwall" -> {
|
|
53
|
+
val placementId = args?.getString(0) ?: ""
|
|
54
|
+
Log.d(TAG, "loadOfferwall command - placementId: $placementId")
|
|
55
|
+
loadOfferwall(root, placementId)
|
|
56
|
+
}
|
|
57
|
+
"handleBackPress" -> {
|
|
58
|
+
Log.d(TAG, "handleBackPress command")
|
|
59
|
+
val handled = root.handleBackPress()
|
|
60
|
+
Log.d(TAG, "handleBackPress result: $handled")
|
|
61
|
+
}
|
|
62
|
+
"sendDataResponse" -> { // NEW
|
|
63
|
+
val requestId = args?.getString(0) ?: ""
|
|
64
|
+
val responseData = args?.getMap(1)?.toHashMap()
|
|
65
|
+
Log.d(TAG, "sendDataResponse command - requestId: $requestId, data: $responseData")
|
|
66
|
+
if (responseData != null) {
|
|
67
|
+
pendingDataResponses[requestId] = responseData
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else -> {
|
|
71
|
+
Log.w(TAG, "Unknown command: $commandId")
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private fun loadOfferwall(view: AdchainOfferwallView, placementId: String) {
|
|
77
|
+
Log.d(TAG, "loadOfferwall method called - placementId: $placementId")
|
|
78
|
+
|
|
79
|
+
if (placementId.isEmpty()) {
|
|
80
|
+
Log.w(TAG, "placementId is empty, returning")
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Get SDK config, user, and offerwall URL
|
|
86
|
+
val config = AdchainSdk.getConfig()
|
|
87
|
+
val user = AdchainSdk.getCurrentUser()
|
|
88
|
+
val offerwallUrl = AdchainSdk.getOfferwallUrl()
|
|
89
|
+
|
|
90
|
+
Log.d(TAG, "SDK state - config: ${config != null}, user: ${user != null}, url: $offerwallUrl")
|
|
91
|
+
|
|
92
|
+
if (config == null) {
|
|
93
|
+
Log.e(TAG, "SDK not initialized")
|
|
94
|
+
sendErrorEvent(view, "SDK not initialized")
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (user == null) {
|
|
99
|
+
Log.e(TAG, "User not logged in")
|
|
100
|
+
sendErrorEvent(view, "User not logged in")
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (offerwallUrl.isNullOrEmpty()) {
|
|
105
|
+
Log.e(TAG, "Offerwall URL not available")
|
|
106
|
+
sendErrorEvent(view, "Offerwall URL not available")
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Log.d(TAG, "About to call view.loadOfferwall()")
|
|
111
|
+
Log.d(TAG, "Parameters - baseUrl: $offerwallUrl, userId: ${user.userId}, appKey: ${config.appKey}, placementId: $placementId")
|
|
112
|
+
|
|
113
|
+
// Set callback for offerwall events
|
|
114
|
+
Log.d(TAG, "Setting callback...")
|
|
115
|
+
view.setCallback(object : OfferwallCallback {
|
|
116
|
+
override fun onOpened() {
|
|
117
|
+
sendEvent(view, "onOfferwallOpened", Arguments.createMap())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
override fun onClosed() {
|
|
121
|
+
sendEvent(view, "onOfferwallClosed", Arguments.createMap())
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
override fun onError(message: String) {
|
|
125
|
+
val map = Arguments.createMap().apply {
|
|
126
|
+
putString("error", message)
|
|
127
|
+
}
|
|
128
|
+
sendEvent(view, "onOfferwallError", map)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
override fun onRewardEarned(amount: Int) {
|
|
132
|
+
val map = Arguments.createMap().apply {
|
|
133
|
+
putInt("amount", amount)
|
|
134
|
+
}
|
|
135
|
+
sendEvent(view, "onRewardEarned", map)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
override fun onHeightChanged(height: Int) {
|
|
139
|
+
Log.d(TAG, "onHeightChanged callback received - height: $height")
|
|
140
|
+
val map = Arguments.createMap().apply {
|
|
141
|
+
putInt("height", height)
|
|
142
|
+
}
|
|
143
|
+
sendEvent(view, "onHeightChange", map)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// NEW: Set event callback for custom events
|
|
148
|
+
Log.d(TAG, "Setting event callback...")
|
|
149
|
+
view.setEventCallback(object : OfferwallEventCallback {
|
|
150
|
+
override fun onCustomEvent(eventType: String, payload: Map<String, Any?>) {
|
|
151
|
+
Log.d(TAG, "onCustomEvent received - type: $eventType, payload: $payload")
|
|
152
|
+
// Sample μ±μμ μ²λ¦¬νλλ‘ μ΄λ²€νΈλ§ μ λ¬ (Toast νμ μ ν¨)
|
|
153
|
+
|
|
154
|
+
val map = Arguments.createMap().apply {
|
|
155
|
+
putString("eventType", eventType)
|
|
156
|
+
putMap("payload", convertMapToWritableMap(payload))
|
|
157
|
+
}
|
|
158
|
+
sendEvent(view, "onCustomEvent", map)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
override fun onDataRequest(
|
|
162
|
+
requestId: String,
|
|
163
|
+
requestType: String,
|
|
164
|
+
params: Map<String, Any?>
|
|
165
|
+
): Map<String, Any?>? {
|
|
166
|
+
Log.d(TAG, "onDataRequest received - id: $requestId, type: $requestType, params: $params")
|
|
167
|
+
|
|
168
|
+
// Send event to React Native
|
|
169
|
+
val map = Arguments.createMap().apply {
|
|
170
|
+
putString("requestId", requestId)
|
|
171
|
+
putString("requestType", requestType)
|
|
172
|
+
putMap("params", convertMapToWritableMap(params))
|
|
173
|
+
}
|
|
174
|
+
sendEvent(view, "onDataRequest", map)
|
|
175
|
+
|
|
176
|
+
// Wait for response from React Native (with timeout)
|
|
177
|
+
val startTime = System.currentTimeMillis()
|
|
178
|
+
val timeout = 5000L // 5 seconds
|
|
179
|
+
|
|
180
|
+
while (System.currentTimeMillis() - startTime < timeout) {
|
|
181
|
+
val response = pendingDataResponses.remove(requestId)
|
|
182
|
+
if (response != null) {
|
|
183
|
+
Log.d(TAG, "Data response received for request: $requestId")
|
|
184
|
+
return response
|
|
185
|
+
}
|
|
186
|
+
Thread.sleep(10)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
Log.w(TAG, "Data request timeout: $requestId")
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// Load offerwall
|
|
195
|
+
Log.d(TAG, "Calling view.loadOfferwall()...")
|
|
196
|
+
view.loadOfferwall(
|
|
197
|
+
baseUrl = offerwallUrl,
|
|
198
|
+
userId = user.userId,
|
|
199
|
+
appKey = config.appKey,
|
|
200
|
+
placementId = placementId
|
|
201
|
+
)
|
|
202
|
+
Log.d(TAG, "view.loadOfferwall() call completed")
|
|
203
|
+
|
|
204
|
+
} catch (e: Exception) {
|
|
205
|
+
Log.e(TAG, "Exception in loadOfferwall", e)
|
|
206
|
+
e.printStackTrace()
|
|
207
|
+
sendErrorEvent(view, "Failed to load offerwall: ${e.message}")
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private fun sendEvent(view: AdchainOfferwallView, eventName: String, params: WritableMap) {
|
|
212
|
+
Log.d(TAG, "sendEvent called - eventName: $eventName, params: $params")
|
|
213
|
+
val reactContext = view.context as ReactContext
|
|
214
|
+
try {
|
|
215
|
+
reactContext
|
|
216
|
+
.getJSModule(com.facebook.react.uimanager.events.RCTEventEmitter::class.java)
|
|
217
|
+
.receiveEvent(view.id, eventName, params)
|
|
218
|
+
Log.d(TAG, "sendEvent completed successfully")
|
|
219
|
+
} catch (e: Exception) {
|
|
220
|
+
Log.e(TAG, "sendEvent failed", e)
|
|
221
|
+
e.printStackTrace()
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private fun sendErrorEvent(view: AdchainOfferwallView, error: String) {
|
|
226
|
+
val map = Arguments.createMap().apply {
|
|
227
|
+
putString("error", error)
|
|
228
|
+
}
|
|
229
|
+
sendEvent(view, "onOfferwallError", map)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
233
|
+
return mapOf(
|
|
234
|
+
"onOfferwallOpened" to mapOf("registrationName" to "onOfferwallOpened"),
|
|
235
|
+
"onOfferwallClosed" to mapOf("registrationName" to "onOfferwallClosed"),
|
|
236
|
+
"onOfferwallError" to mapOf("registrationName" to "onOfferwallError"),
|
|
237
|
+
"onRewardEarned" to mapOf("registrationName" to "onRewardEarned"),
|
|
238
|
+
"onHeightChange" to mapOf("registrationName" to "onHeightChange"),
|
|
239
|
+
"onCustomEvent" to mapOf("registrationName" to "onCustomEvent"), // NEW
|
|
240
|
+
"onDataRequest" to mapOf("registrationName" to "onDataRequest") // NEW
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Helper function to convert Map to WritableMap
|
|
245
|
+
private fun convertMapToWritableMap(map: Map<String, Any?>): WritableMap {
|
|
246
|
+
val writableMap = Arguments.createMap()
|
|
247
|
+
map.forEach { (key, value) ->
|
|
248
|
+
when (value) {
|
|
249
|
+
null -> writableMap.putNull(key)
|
|
250
|
+
is String -> writableMap.putString(key, value)
|
|
251
|
+
is Int -> writableMap.putInt(key, value)
|
|
252
|
+
is Double -> writableMap.putDouble(key, value)
|
|
253
|
+
is Boolean -> writableMap.putBoolean(key, value)
|
|
254
|
+
is Map<*, *> -> {
|
|
255
|
+
@Suppress("UNCHECKED_CAST")
|
|
256
|
+
writableMap.putMap(key, convertMapToWritableMap(value as Map<String, Any?>))
|
|
257
|
+
}
|
|
258
|
+
is List<*> -> {
|
|
259
|
+
writableMap.putArray(key, convertListToWritableArray(value))
|
|
260
|
+
}
|
|
261
|
+
else -> Log.w(TAG, "Unsupported value type for key: $key")
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return writableMap
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private fun convertListToWritableArray(list: List<*>): com.facebook.react.bridge.WritableArray {
|
|
268
|
+
val writableArray = Arguments.createArray()
|
|
269
|
+
list.forEach { item ->
|
|
270
|
+
when (item) {
|
|
271
|
+
null -> writableArray.pushNull()
|
|
272
|
+
is String -> writableArray.pushString(item)
|
|
273
|
+
is Int -> writableArray.pushInt(item)
|
|
274
|
+
is Double -> writableArray.pushDouble(item)
|
|
275
|
+
is Boolean -> writableArray.pushBoolean(item)
|
|
276
|
+
is Map<*, *> -> {
|
|
277
|
+
@Suppress("UNCHECKED_CAST")
|
|
278
|
+
writableArray.pushMap(convertMapToWritableMap(item as Map<String, Any?>))
|
|
279
|
+
}
|
|
280
|
+
is List<*> -> {
|
|
281
|
+
writableArray.pushArray(convertListToWritableArray(item))
|
|
282
|
+
}
|
|
283
|
+
else -> Log.w(TAG, "Unsupported item type in list")
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return writableArray
|
|
287
|
+
}
|
|
288
|
+
}
|