Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/deployRelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- 'main'
- 'revamp/parameter-pasrsing'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Development Branch Leaked Into Production

The workflow includes a temporary development branch revamp/parameter-pasrsing (note the typo "pasrsing" instead of "parsing"). This appears to be temporary debug/development configuration that shouldn't be in a production release workflow, especially given the PR is about bumping to version 1.0.x.

Fix in Cursor Fix in Web

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
build:
Expand Down Expand Up @@ -40,7 +41,7 @@ jobs:

- name: Update library version
env:
PUBLISH_VERSION: 0.0.${{ github.run_number }}
PUBLISH_VERSION: 1.0.${{ github.run_number }}
run: echo PUBLISH_VERSION=$PUBLISH_VERSION > ./versions.properties

- name: Update local properties
Expand All @@ -57,4 +58,4 @@ jobs:
run: cat ./local.properties

- name: Publish to GitHub Packages
run: ./gradlew publishAllPublicationsToGithubRepository
run: ./gradlew publishAllPublicationsToGithubRepository
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,7 @@ import com.appsflyer.AppsFlyerLib
import com.appsflyer.attribution.AppsFlyerRequestListener
import com.appsflyer.deeplink.DeepLinkListener
import com.appsflyer.deeplink.DeepLinkResult
import com.metacto.kmm.appsflyer.model.getDeepLinkValue
import com.metacto.kmm.appsflyer.model.getDeepLinkMetadata
import com.metacto.kmm.appsflyer.model.getDestinationPath
import com.metacto.kmm.appsflyer.model.toError
import com.metacto.kmm.appsflyer.model.hasDescopeToken
import com.metacto.kmm.appsflyer.model.hasLoginType
import com.metacto.kmm.appsflyer.model.getLoginType
import com.metacto.kmm.appsflyer.model.getAfSub1
import com.metacto.kmm.appsflyer.model.DeeplinkSource
import com.metacto.kmm.appsflyer.model.UdlStatus
import com.metacto.kmm.appsflyer.model.GcdAfStatus
import com.metacto.kmm.appsflyer.model.*
import com.metacto.kmm.appsflyer.util.getAppAttributionResult
import com.metacto.kmm.appsflyer.model.DeepLinkResult as KMMDeepLinkResult

Expand All @@ -34,87 +24,71 @@ actual class OneLinkService actual constructor(
override fun onConversionDataSuccess(p0: MutableMap<String, Any>?) {
if (p0 != null) {
val appConversionResult = this@OneLinkService.getAppAttributionResult(p0)
options.listener.onAppAttribution(
appConversionResult.isOrganic,
appConversionResult.extras
)

val gcdMediaSource = appConversionResult.extras["media_source"]?.toString()
val gcdCampaign = appConversionResult.extras["campaign"]?.toString()
@Suppress("UNCHECKED_CAST")
val extras = appConversionResult.extras as Map<Any?, *>
@Suppress("UNCHECKED_CAST")
val conversion = p0 as Map<Any?, *>

val result = KMMDeepLinkResult(
destination = null,
campaign = gcdCampaign,
campaignId = null,
clickHttpReferrer = null,
isDeferred = false,
mediaSource = gcdMediaSource,
matchType = null,
clickEventJson = null,
metadata = null,
deeplinkSource = DeeplinkSource.GCD,
hasDescopeToken = hasDescopeToken(extras),
hasLoginType = hasLoginType(extras),
loginType = getLoginType(extras),
val isFirstLaunch = (extras["is_first_launch"] as? String)?.toBoolean()
?: (extras["is_first_launch"] as? Boolean)
?: false

if (!isFirstLaunch) return

val deepLinkValue = extras.getDeepLinkValue()
val destination = deepLinkValue?.parseDestination()
val gcdStatus = if (appConversionResult.isOrganic) GcdAfStatus.ORGANIC else GcdAfStatus.NON_ORGANIC

val result = extras.buildDeepLinkResult(
source = DeeplinkSource.GCD,
udlStatus = null,
udlMatchType = null,
gcdAfStatus = if (appConversionResult.isOrganic) GcdAfStatus.ORGANIC else GcdAfStatus.NON_ORGANIC,
gcdMediaSource = gcdMediaSource,
gcdCampaign = gcdCampaign,
afSub1 = getAfSub1(extras),
extraLink = null
gcdStatus = gcdStatus,
isDeferred = true,
destination = destination,
clickEvent = extras,
conversion = conversion
)
options.listener.onDeepLinkingResult(result)

options.listener.onAttributionData(result)
}
}

override fun onConversionDataFail(p0: String?) {
// No-op
if (p0 != null) {
options.listener.onDeepLinkingError(DeepLinkError.Generic(p0))
}
}

override fun onAppOpenAttribution(p0: MutableMap<String, String>?) {
if (p0 != null) {
val appConversionResult = this@OneLinkService.getAppAttributionResult(p0)
options.listener.onAppAttribution(
appConversionResult.isOrganic,
appConversionResult.extras
)

val gcdMediaSource = appConversionResult.extras["media_source"]?.toString()
val gcdCampaign = appConversionResult.extras["campaign"]?.toString()
@Suppress("UNCHECKED_CAST")
val extras = appConversionResult.extras as Map<Any?, *>
@Suppress("UNCHECKED_CAST")
val conversion = p0 as Map<Any?, *>

val result = KMMDeepLinkResult(
destination = null,
campaign = gcdCampaign,
campaignId = null,
clickHttpReferrer = null,
isDeferred = false,
mediaSource = gcdMediaSource,
matchType = null,
clickEventJson = null,
metadata = null,
deeplinkSource = DeeplinkSource.GCD,
hasDescopeToken = hasDescopeToken(extras),
hasLoginType = hasLoginType(extras),
loginType = getLoginType(extras),
val deepLinkValue = extras.getDeepLinkValue()
val destination = deepLinkValue?.parseDestination()
val gcdStatus = if (appConversionResult.isOrganic) GcdAfStatus.ORGANIC else GcdAfStatus.NON_ORGANIC

val result = extras.buildDeepLinkResult(
source = DeeplinkSource.GCD,
udlStatus = null,
udlMatchType = null,
gcdAfStatus = if (appConversionResult.isOrganic) GcdAfStatus.ORGANIC else GcdAfStatus.NON_ORGANIC,
gcdMediaSource = gcdMediaSource,
gcdCampaign = gcdCampaign,
afSub1 = getAfSub1(extras),
extraLink = null
gcdStatus = gcdStatus,
isDeferred = false,
destination = destination,
clickEvent = extras,
conversion = conversion
)
options.listener.onDeepLinkingResult(result)

options.listener.onAttributionData(result)
}
}

override fun onAttributionFailure(p0: String?) {
// No-op
if (p0 != null) {
options.listener.onDeepLinkingError(DeepLinkError.Generic(p0))
}
}
}

Expand All @@ -123,36 +97,24 @@ actual class OneLinkService actual constructor(
DeepLinkResult.Status.FOUND -> {
val deepLink = deepLinkResult.deepLink
val clickEventValues = deepLink.clickEvent.toMap()
val fullDeepLinkValue = deepLink.deepLinkValue ?: this.getDeepLinkValue(clickEventValues)
val metadata = this.getDeepLinkMetadata(fullDeepLinkValue, clickEventValues)

val result = KMMDeepLinkResult(
destination = fullDeepLinkValue?.let { this.getDestinationPath(fullDeepLinkValue) },
campaign = deepLink.campaign,
campaignId = deepLink.campaignId,
clickHttpReferrer = deepLink.clickHttpReferrer,
isDeferred = deepLink.isDeferred,
mediaSource = deepLink.mediaSource,
matchType = deepLink.matchType,
clickEventJson = deepLink.clickEvent.toString(),
metadata = metadata,
deeplinkSource = DeeplinkSource.UDL,
hasDescopeToken = hasDescopeToken(metadata.extras),
hasLoginType = hasLoginType(metadata.extras),
loginType = getLoginType(metadata.extras),
val deepLinkValue = deepLink.deepLinkValue ?: clickEventValues.getDeepLinkValue()
val destination = deepLinkValue?.parseDestination()

val result = clickEventValues.buildDeepLinkResult(
source = DeeplinkSource.UDL,
udlStatus = UdlStatus.FOUND,
udlMatchType = deepLink.matchType,
gcdAfStatus = null,
gcdMediaSource = null,
gcdCampaign = null,
afSub1 = getAfSub1(clickEventValues),
extraLink = deepLink.clickEvent.getString("link")
gcdStatus = null,
isDeferred = deepLink.isDeferred,
destination = destination,
clickEvent = clickEventValues,
conversion = null
)

options.listener.onDeepLinkingResult(result)
}

DeepLinkResult.Status.NOT_FOUND -> {
options.listener.onDeepLinkingResult(null)
options.listener.onDeepLinkNotFound(KMMDeepLinkResult.notFound())
}

else -> {
Expand All @@ -166,9 +128,9 @@ actual class OneLinkService actual constructor(
options.enableDebugLog?.let { setDebugLog(it) }
options.minTimeBetweenSessions?.let { setMinTimeBetweenSessions(it) }
if (options.appInviteOneLinkTemplateId != null) {
// Set the OneLink template id for share invite links
setAppInviteOneLink(options.appInviteOneLinkTemplateId)
}

subscribeForDeepLink(deepLinkListener)
init(options.devAppKey, conversionListener, options.context as Context)
}
Expand Down Expand Up @@ -206,6 +168,7 @@ actual class OneLinkService actual constructor(
*options.oneLinkCustomDomains?.toTypedArray().orEmpty()
)
}

initialize()
AppsFlyerLib.getInstance().start(options.context as Context, options.devAppKey)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.metacto.kmm.appsflyer.model

import com.appsflyer.deeplink.DeepLinkResult

actual open class DeepLinkError {
actual open class DeepLinkError(
open val message: String? = null
) {
data object Timeout : DeepLinkError()
data object Network : DeepLinkError()
data object HttpStatusCode : DeepLinkError()
data object Unexpected : DeepLinkError()
data object DeveloperError : DeepLinkError()
data class Generic(override val message: String) : DeepLinkError(message)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Platform Mismatch Blocks Error Details.

Platform mismatch in DeepLinkError implementation. Android uses an open class with nested subclasses (Timeout, Network, etc.), while iOS uses a data class with an NSError parameter. The Android implementation's data objects don't pass values to the parent constructor's message parameter, making the error details inaccessible through the base class on Android, breaking cross-platform API consistency.

Fix in Cursor Fix in Web

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import com.metacto.kmm.appsflyer.model.DeepLinkError
import com.metacto.kmm.appsflyer.model.DeepLinkResult

interface OneLinkListener {
fun onAppAttribution(
isOrganic: Boolean,
extras: Map<Any, Any?>?
)

fun onDeepLinkingResult(result: DeepLinkResult?)
fun onDeepLinkingResult(result: DeepLinkResult)
fun onDeepLinkNotFound(result: DeepLinkResult)
fun onAttributionData(result: DeepLinkResult)
fun onDeepLinkingError(error: DeepLinkError)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ enum class DeeplinkSource {
GCD
}

enum class LoginType {
EMAIL,
PHONE
}

enum class UdlStatus {
FOUND,
NOT_FOUND
Expand Down
Loading