Skip to content

Commit ec156b7

Browse files
committed
Add SDK parity attribution APIs
1 parent 1699688 commit ec156b7

23 files changed

Lines changed: 777 additions & 109 deletions

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Supported `eventType` values:
8484
Notes:
8585

8686
- Use `eventType: AppSprintEventType.custom` together with the optional `name` argument for custom event names.
87-
- Revenue fields are accepted through `params['revenue']` and `params['currency']`.
87+
- Revenue fields are accepted through `params['revenue']` or `params['price']`, plus `params['currency']`.
8888
- If an event cannot be delivered, it is queued locally and retried on the next initialization or explicit flush.
8989

9090
## Public API
@@ -106,7 +106,8 @@ Available methods:
106106
- `setCustomerUserId(userId)` updates the customer user id locally and remotely when possible.
107107
- `getAppSprintId()` returns the cached AppSprint install identifier, if available.
108108
- `getAttribution()` returns the last cached attribution result, if available.
109-
- `enableAppleAdsAttribution()` re-enables Apple Ads attribution in the current runtime config.
109+
- `getAttributionParams()` returns the partner-ready attribution payload.
110+
- `enableAppleAdsAttribution()` re-enables Apple Ads attribution on iOS and returns `false` on Android.
110111
- `isInitialized()` reports whether `configure()` completed.
111112
- `destroy()` removes SDK listeners.
112113

android/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ dependencies {
4343
implementation(name: 'appsprint-sdk', ext: 'aar')
4444
implementation "androidx.lifecycle:lifecycle-process:2.8.7"
4545
implementation "com.google.android.gms:play-services-ads-identifier:18.0.1"
46+
implementation "com.android.installreferrer:installreferrer:2.2"
4647
}

android/libs/appsprint-sdk.aar

5.38 KB
Binary file not shown.

android/src/main/kotlin/com/appsprint/flutter/AppSprintFlutterPlugin.kt

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.content.Context
44
import com.appsprint.sdk.AppSprint
55
import com.appsprint.sdk.AppSprintConfig
66
import com.appsprint.sdk.AppSprintEventType
7+
import com.appsprint.sdk.AttributionResult
78
import io.flutter.embedding.engine.plugins.FlutterPlugin
89
import io.flutter.plugin.common.MethodCall
910
import io.flutter.plugin.common.MethodChannel
@@ -43,7 +44,7 @@ class AppSprintFlutterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
4344
customerUserId = call.argument<String>("customerUserId"),
4445
)
4546
sdk().configure(config)
46-
result.success(null)
47+
result.success(true)
4748
} catch (e: Exception) {
4849
result.error("CONFIGURE_ERROR", e.message, null)
4950
}
@@ -58,12 +59,12 @@ class AppSprintFlutterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
5859
val name = call.argument<String>("name")
5960
val params = mutableMapOf<String, Any?>()
6061
call.argument<Map<String, Any?>>("parameters")?.forEach { (k, v) -> params[k] = v }
61-
val revenue = call.argument<Double>("revenue")
62+
val revenue = numberArgument(call, "revenue")
6263
val currency = call.argument<String>("currency")
6364
if (revenue != null && revenue != 0.0) params["revenue"] = revenue
6465
if (currency != null) params["currency"] = currency
6566
sdk().sendEvent(type, name, if (params.isNotEmpty()) params else null)
66-
result.success(null)
67+
result.success(true)
6768
} catch (e: Exception) {
6869
result.error("SEND_EVENT_ERROR", e.message, null)
6970
}
@@ -93,11 +94,13 @@ class AppSprintFlutterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
9394
}
9495

9596
"clearData" -> {
96-
try {
97-
sdk().clearData()
98-
result.success(null)
99-
} catch (e: Exception) {
100-
result.error("CLEAR_DATA_ERROR", e.message, null)
97+
thread(start = true) {
98+
try {
99+
sdk().clearData()
100+
result.success(null)
101+
} catch (e: Exception) {
102+
result.error("CLEAR_DATA_ERROR", e.message, null)
103+
}
101104
}
102105
}
103106

@@ -114,8 +117,13 @@ class AppSprintFlutterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
114117
}
115118

116119
"enableAppleAdsAttribution" -> {
117-
sdk().enableAppleAdsAttribution()
118-
result.success(null)
120+
thread(start = true) {
121+
try {
122+
result.success(sdk().enableAppleAdsAttribution())
123+
} catch (e: Exception) {
124+
result.error("APPLE_ADS_ERROR", e.message, null)
125+
}
126+
}
119127
}
120128

121129
"getAppSprintId" -> result.success(sdk().getAppSprintId())
@@ -126,24 +134,24 @@ class AppSprintFlutterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
126134
result.success(null)
127135
return
128136
}
129-
val map = mutableMapOf<String, Any?>(
130-
"source" to attr.source,
131-
"confidence" to attr.confidence,
132-
)
133-
attr.campaignName?.let { map["campaignName"] = it }
134-
attr.utmSource?.let { map["utmSource"] = it }
135-
attr.utmMedium?.let { map["utmMedium"] = it }
136-
attr.utmCampaign?.let { map["utmCampaign"] = it }
137-
result.success(map)
137+
result.success(attributionToMap(attr))
138138
}
139139

140+
"getAttributionParams" -> result.success(sdk().getAttributionParams())
141+
140142
"isInitialized" -> result.success(sdk().isInitialized())
141143

142144
"isSdkDisabled" -> result.success(sdk().isSdkDisabled())
143145

144146
"destroy" -> {
145-
sdk().destroy()
146-
result.success(null)
147+
thread(start = true) {
148+
try {
149+
sdk().destroy()
150+
result.success(null)
151+
} catch (e: Exception) {
152+
result.error("DESTROY_ERROR", e.message, null)
153+
}
154+
}
147155
}
148156

149157
// Utility
@@ -170,4 +178,38 @@ class AppSprintFlutterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
170178
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
171179
channel.setMethodCallHandler(null)
172180
}
181+
182+
private fun numberArgument(call: MethodCall, key: String): Double? {
183+
val value = call.argument<Any>(key) ?: return null
184+
return when (value) {
185+
is Number -> value.toDouble()
186+
is String -> value.trim().toDoubleOrNull()
187+
else -> null
188+
}
189+
}
190+
191+
private fun attributionToMap(attr: AttributionResult): Map<String, Any?> {
192+
val map = mutableMapOf<String, Any?>(
193+
"isAttributed" to attr.isAttributed,
194+
"source" to attr.source,
195+
"confidence" to attr.confidence,
196+
)
197+
attr.matchType?.let { map["matchType"] = it }
198+
attr.campaignName?.let { map["campaignName"] = it }
199+
attr.link?.let { map["link"] = mapOf("id" to it.id, "name" to it.name) }
200+
attr.appleAds?.let {
201+
map["appleAds"] = mutableMapOf<String, Any?>("campaignId" to it.campaignId).apply {
202+
it.adGroupId?.let { value -> put("adGroupId", value) }
203+
it.keywordId?.let { value -> put("keywordId", value) }
204+
it.countryOrRegion?.let { value -> put("countryOrRegion", value) }
205+
it.conversionType?.let { value -> put("conversionType", value) }
206+
}
207+
}
208+
attr.utmSource?.let { map["utmSource"] = it }
209+
attr.utmMedium?.let { map["utmMedium"] = it }
210+
attr.utmCampaign?.let { map["utmCampaign"] = it }
211+
attr.utmContent?.let { map["utmContent"] = it }
212+
attr.utmTerm?.let { map["utmTerm"] = it }
213+
return map
214+
}
173215
}
Binary file not shown.

0 commit comments

Comments
 (0)