Updated on 2026-08-14
This commit is contained in:
commit
6311ce2e3c
4 changed files with 118 additions and 37 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.common.analytics.handlers.appsflyer
|
||||
|
||||
import android.content.Context
|
||||
import com.appsflyer.AppsFlyerConversionListener
|
||||
import com.appsflyer.AppsFlyerLib
|
||||
import com.appsflyer.attribution.AppsFlyerRequestListener
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
|
|
@ -17,18 +18,49 @@ internal class AppsFlyerClient(
|
|||
|
||||
private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance()
|
||||
private val eventConverter = UnderscoreAnalyticsEventConverter()
|
||||
private val logEventListener = object : AppsFlyerRequestListener {
|
||||
override fun onSuccess() {
|
||||
Timber.tag("AppsFlyerClient").i("AppsFlyerRequestListener send")
|
||||
}
|
||||
|
||||
override fun onError(p0: Int, p1: String) {
|
||||
Timber.tag("AppsFlyerClient").e("AppsFlyerRequestListener onError: $p0, $p1")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
appsFlyerLib.init(key, null, context)
|
||||
appsFlyerLib.start(context, key, object : AppsFlyerRequestListener {
|
||||
override fun onSuccess() {
|
||||
Timber.d("AppsFlyer initialized successfully")
|
||||
val conversionListener = object : AppsFlyerConversionListener {
|
||||
override fun onConversionDataSuccess(p0: Map<String?, Any?>?) {
|
||||
Timber.i("AppsFlyer conversion data success: ${p0.orEmpty()}")
|
||||
}
|
||||
override fun onConversionDataFail(p0: String?) {
|
||||
Timber.e("AppsFlyer conversion data failure: ${p0.orEmpty()}")
|
||||
}
|
||||
override fun onAppOpenAttribution(p0: Map<String?, String?>?) {
|
||||
Timber.i("AppsFlyer app open attribution: ${p0.orEmpty()}")
|
||||
}
|
||||
override fun onAttributionFailure(p0: String?) {
|
||||
Timber.e("AppsFlyer attribution failure: ${p0.orEmpty()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(p0: Int, p1: String) {
|
||||
Timber.e("AppsFlyer initialization error: $p0, $p1")
|
||||
}
|
||||
})
|
||||
appsFlyerLib.run {
|
||||
setAppId(context.packageName)
|
||||
setDebugLog(true)
|
||||
|
||||
init(key, conversionListener, context)
|
||||
Timber.i("Starting AppsFlyer SDK")
|
||||
start(context, key, object : AppsFlyerRequestListener {
|
||||
override fun onSuccess() {
|
||||
Timber.i("AppsFlyer initialized successfully")
|
||||
}
|
||||
|
||||
override fun onError(p0: Int, p1: String) {
|
||||
Timber.e("AppsFlyer initialization error: $p0, $p1")
|
||||
}
|
||||
})
|
||||
Timber.i("AppsFlyer SDK started")
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUserId(userId: String) {
|
||||
|
|
@ -40,10 +72,12 @@ internal class AppsFlyerClient(
|
|||
}
|
||||
|
||||
override fun logEvent(event: String, params: Map<String, String>) {
|
||||
Timber.tag("AppsFlyer").i("Logging event: $event with params: $params")
|
||||
appsFlyerLib.logEvent(
|
||||
context,
|
||||
event,
|
||||
eventConverter.convertEventParams(params),
|
||||
logEventListener,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,19 +6,26 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
import com.tangem.core.analytics.models.AppsFlyerIncludedEvent
|
||||
import com.tangem.core.analytics.models.AppsFlyerOnlyEvent
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import timber.log.Timber
|
||||
|
||||
class AppsFlyerAnalyticsHandler(
|
||||
private val client: AppsFlyerAnalyticsClient,
|
||||
) : AnalyticsHandler, AnalyticsUserIdHandler {
|
||||
|
||||
init {
|
||||
Timber.tag("AppsFlyer").i("AppsFlyer Analytics Handler created")
|
||||
}
|
||||
|
||||
override fun id(): String = ID
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
when (event) {
|
||||
is AppsFlyerOnlyEvent -> {
|
||||
Timber.tag("AppsFlyer").i("Sending event to AppsFlyer: ${event.id} with params: ${event.params}")
|
||||
client.logEvent(event.id, event.params)
|
||||
}
|
||||
is AppsFlyerIncludedEvent -> {
|
||||
Timber.tag("AppsFlyer").i("Sending event to AppsFlyer: ${event.id} with params: ${event.params}")
|
||||
client.logEvent(
|
||||
event = AnalyticsEvent(category = event.category, event = event.appsFlyerReplacedEvent).id,
|
||||
params = event.params,
|
||||
|
|
@ -40,10 +47,16 @@ class AppsFlyerAnalyticsHandler(
|
|||
}
|
||||
|
||||
class Builder : AnalyticsHandlerBuilder {
|
||||
init {
|
||||
Timber.tag("AppsFlyer").i("AppsFlyer Analytics Handler Builder created")
|
||||
}
|
||||
|
||||
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler = AppsFlyerAnalyticsHandler(
|
||||
client = if (data.logConfig.isAppsflyerLogEnabled) {
|
||||
Timber.tag("AppsFlyer").i("AppsFlyer log enabled, mock client created")
|
||||
AppsFlyerLogClient(data.jsonConverter)
|
||||
} else {
|
||||
Timber.tag("AppsFlyer").i("AppsFlyer log disabled, real client created")
|
||||
AppsFlyerClient(data.application, data.config.appsFlyerApiKey)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1119,6 +1119,7 @@ internal class SwapModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun onTokenSelect(id: String) {
|
||||
val tokens = dataState.tokensDataState ?: return
|
||||
val (foundToken, foundAccount) = getSelectedTokenAndAccount(tokens, id)
|
||||
|
|
@ -1149,6 +1150,8 @@ internal class SwapModel @Inject constructor(
|
|||
coin = newToken,
|
||||
isFromCurrency = true,
|
||||
)
|
||||
} else {
|
||||
fromTokenBalanceJobHolder.cancel()
|
||||
}
|
||||
} else {
|
||||
fromToken = initialFromStatus
|
||||
|
|
@ -1167,6 +1170,8 @@ internal class SwapModel @Inject constructor(
|
|||
coin = newToken,
|
||||
isFromCurrency = false,
|
||||
)
|
||||
} else {
|
||||
toTokenBalanceJobHolder.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1221,6 +1226,7 @@ internal class SwapModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
private fun subscribeToCoinBalanceUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
coin: CryptoCurrency.Coin,
|
||||
|
|
@ -1243,20 +1249,25 @@ internal class SwapModel @Inject constructor(
|
|||
).getOrNull() ?: currencyStatus,
|
||||
)
|
||||
|
||||
uiState = if (isFromCurrency) {
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = currencyStatus,
|
||||
fromAccount = account,
|
||||
)
|
||||
stateBuilder.updateSendCurrencyBalance(uiState, currencyStatus)
|
||||
} else {
|
||||
dataState = dataState.copy(
|
||||
toCryptoCurrency = currencyStatus,
|
||||
toAccount = account,
|
||||
)
|
||||
stateBuilder.updateReceiveCurrencyBalance(uiState, currencyStatus)
|
||||
uiState = when {
|
||||
isFromCurrency && currencyStatus.currency.id == dataState.fromCryptoCurrency?.currency?.id -> {
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = currencyStatus,
|
||||
fromAccount = account,
|
||||
)
|
||||
stateBuilder.updateSendCurrencyBalance(uiState, currencyStatus)
|
||||
}
|
||||
!isFromCurrency && currencyStatus.currency.id == dataState.toCryptoCurrency?.currency?.id -> {
|
||||
dataState = dataState.copy(
|
||||
toCryptoCurrency = currencyStatus,
|
||||
toAccount = account,
|
||||
)
|
||||
stateBuilder.updateReceiveCurrencyBalance(uiState, currencyStatus)
|
||||
}
|
||||
else -> {
|
||||
uiState
|
||||
}
|
||||
}
|
||||
|
||||
startLoadingQuotesFromLastState(isSilent = true)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1276,14 +1287,19 @@ internal class SwapModel @Inject constructor(
|
|||
).getOrNull() ?: status,
|
||||
)
|
||||
|
||||
uiState = if (isFromCurrency) {
|
||||
dataState = dataState.copy(fromCryptoCurrency = status)
|
||||
stateBuilder.updateSendCurrencyBalance(uiState, status)
|
||||
} else {
|
||||
dataState = dataState.copy(toCryptoCurrency = status)
|
||||
stateBuilder.updateReceiveCurrencyBalance(uiState, status)
|
||||
uiState = when {
|
||||
isFromCurrency && status.currency.id == dataState.fromCryptoCurrency?.currency?.id -> {
|
||||
dataState = dataState.copy(fromCryptoCurrency = status)
|
||||
stateBuilder.updateSendCurrencyBalance(uiState, status)
|
||||
}
|
||||
!isFromCurrency && status.currency.id == dataState.toCryptoCurrency?.currency?.id -> {
|
||||
dataState = dataState.copy(toCryptoCurrency = status)
|
||||
stateBuilder.updateReceiveCurrencyBalance(uiState, status)
|
||||
}
|
||||
else -> {
|
||||
uiState
|
||||
}
|
||||
}
|
||||
|
||||
startLoadingQuotesFromLastState(isSilent = true)
|
||||
}
|
||||
}.flowOn(dispatchers.main)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -9,13 +10,17 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.TangemPayMainScreenBlock
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayCardMainBlock(
|
||||
|
|
@ -33,8 +38,9 @@ internal fun TangemPayCardMainBlock(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(IntrinsicSize.Min)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
.padding(horizontal = 12.dp, vertical = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_visa_36),
|
||||
|
|
@ -42,20 +48,15 @@ internal fun TangemPayCardMainBlock(
|
|||
modifier = Modifier.size(36.dp),
|
||||
)
|
||||
|
||||
Spacer(Modifier.width(12.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.tangempay_payment_account),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(6.dp))
|
||||
|
||||
Text(
|
||||
text = state.lastFourDigits.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
|
|
@ -64,6 +65,7 @@ internal fun TangemPayCardMainBlock(
|
|||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -72,14 +74,30 @@ internal fun TangemPayCardMainBlock(
|
|||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = state.balanceSymbol.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TangemPayCardMainBlockPreview() {
|
||||
TangemThemePreview {
|
||||
TangemPayMainScreenBlock(
|
||||
TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*1234"),
|
||||
balanceText = TextReference.Str("$ 0.00"),
|
||||
balanceSymbol = TextReference.Str("USDC"),
|
||||
onClick = {},
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue