diff --git a/app/build.gradle b/app/build.gradle
index ff53e7b789..d4e92b6555 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -77,9 +77,9 @@ dependencies {
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
- implementation 'com.tangem:blockchain:develop-52'
- implementation 'com.tangem.tangem-sdk-kotlin:core:develop-115'
- implementation 'com.tangem.tangem-sdk-kotlin:android:develop-115'
+ implementation 'com.tangem:blockchain:develop-59'
+ implementation 'com.tangem.tangem-sdk-kotlin:core:develop-127'
+ implementation 'com.tangem.tangem-sdk-kotlin:android:develop-127'
// WebView
implementation "androidx.browser:browser:1.3.0"
@@ -110,6 +110,9 @@ dependencies {
implementation 'com.google.firebase:firebase-config-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx'
+ // AppsFlyer
+ implementation 'com.appsflyer:af-android-sdk:6.5.1'
+
// WalletConnect
implementation 'com.github.salomonbrys.kotson:kotson:2.5.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f3c400a3ec..f358215715 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,6 +15,7 @@
+
@@ -35,7 +36,8 @@
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme"
- tools:ignore="GoogleAppIndexingWarning">
+ tools:ignore="GoogleAppIndexingWarning"
+ tools:replace="android:fullBackupContent">
{
TransactionDialog.create(state.dialog.dialogData, context)
is WalletConnectDialog.PersonalSign ->
PersonalSignDialog.create(state.dialog.data, context)
+ is WalletConnectDialog.BnbTransactionDialog ->
+ BnbTransactionDialog.create(
+ data = state.dialog.data,
+ session = state.dialog.session,
+ sessionId = state.dialog.sessionId,
+ cardId = state.dialog.cardId,
+ dAppName = state.dialog.dAppName,
+ context = context
+ )
is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context)
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
index 8ddf2f5ad9..5099d3e1cd 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
@@ -4,12 +4,66 @@ enum class AnalyticsEvent(val event: String) {
CARD_IS_SCANNED("card_is_scanned"),
TRANSACTION_IS_SENT("transaction_is_sent"),
READY_TO_SCAN("ready_to_scan"),
+
APP_RATING_DISPLAYED("rate_app_warning_displayed"),
APP_RATING_DISMISS("dismiss_rate_app_warning"),
APP_RATING_NEGATIVE("negative_rate_app_feedback"),
APP_RATING_POSITIVE("positive_rate_app_feedback"),
+
WC_SUCCESS_RESPONSE("wallet_connect_success_response"),
WC_INVALID_REQUEST("wallet_connect_invalid_request"),
WC_NEW_SESSION("wallet_connect_new_session"),
WC_SESSION_DISCONNECTED("wallet_connect_session_disconnected"),
+
+ GET_CARD("get_card"),
+}
+
+enum class AnalyticsParam(val param: String) {
+ BLOCKCHAIN("blockchain"),
+ BATCH_ID("batch_id"),
+ FIRMWARE("firmware"),
+ ACTION("action"),
+ ERROR_DESCRIPTION("error_description"),
+ ERROR_CODE("error_code"),
+ NEW_SECURITY_OPTION("new_security_option"),
+ ERROR_KEY("Tangem SDK error key"),
+
+ WALLET_CONNECT_ACTION("wallet_connect_action"),
+ WALLET_CONNECT_REQUEST("wallet_connect_request"),
+ WALLET_CONNECT_DAPP_URL("wallet_connect_dapp_url"),
+
+ SOURCE("source"),
+}
+
+enum class GetCardSourceParams(val param: String) {
+ WELCOME("welcome"),
+ ONBOARDING_BUY_MORE("wallet_onboaring_buy_more_cards"),
+ ONBOARDING("wallet_onboarding")
+}
+
+object Analytics {
+ enum class ActionToLog(val key: String) {
+ Scan("tap_scan_task"),
+ SendTransaction("send_transaction"),
+ WalletConnectSign("wallet_connect_personal_sign"),
+ WalletConnectTransaction("wallet_connect_tx_sign"),
+ ReadPinSettings("read_pin_settings"),
+ ChangeSecOptions("change_sec_options"),
+ CreateWallet("create_wallet"),
+ PurgeWallet("purge_wallet"),
+ WriteIssuerData("write_issuer_data"),
+ }
+
+ enum class WcSessionEvent { Disconnect, Connect }
+
+ enum class WcAction { PersonalSign, SignTransaction, SendTransaction }
+
+ sealed class WcAnalyticsEvent {
+ data class Error(val error: Throwable, val action: WcAction?) :
+ WcAnalyticsEvent()
+
+ data class Session(val event: WcSessionEvent, val url: String?) : WcAnalyticsEvent()
+ data class Action(val action: WcAction) : WcAnalyticsEvent()
+ data class InvalidRequest(val json: String?) : WcAnalyticsEvent()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
index 1022f85c5a..caa5f0117b 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
@@ -2,14 +2,75 @@ package com.tangem.tap.common.analytics
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
+import com.tangem.tap.common.extensions.filterNotNull
-interface AnalyticsHandler {
- fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null)
- fun logCardSdkError(
+abstract class AnalyticsHandler {
+ abstract fun triggerEvent(
+ event: AnalyticsEvent,
+ card: Card? = null,
+ blockchain: String? = null,
+ params: Map = emptyMap()
+ )
+
+ abstract fun logCardSdkError(
error: TangemSdkError,
- actionToLog: FirebaseAnalyticsHandler.ActionToLog,
- parameters: Map? = null,
+ actionToLog: Analytics.ActionToLog,
+ parameters: Map? = null,
card: Card? = null
)
+
+ abstract fun logError(
+ error: Throwable,
+ params: Map = emptyMap()
+ )
+
+ protected fun prepareParams(card: Card?, blockchain: String? = null): Map {
+ return mapOf(
+ AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue,
+ AnalyticsParam.BATCH_ID.param to card?.batchId,
+ AnalyticsParam.BLOCKCHAIN.param to blockchain,
+ ).filterNotNull()
+ }
+
+ fun logWcEvent(event: Analytics.WcAnalyticsEvent) {
+ when (event) {
+ is Analytics.WcAnalyticsEvent.Action -> {
+ triggerEvent(
+ event = AnalyticsEvent.WC_SUCCESS_RESPONSE,
+ params = mapOf(
+ AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name
+ )
+ )
+ }
+ is Analytics.WcAnalyticsEvent.Error -> {
+ val params = mapOf(
+ AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action?.name,
+ AnalyticsParam.ERROR_DESCRIPTION.param to event.error.message
+ )
+ .filterNotNull()
+ logError(event.error, params)
+ }
+ is Analytics.WcAnalyticsEvent.InvalidRequest ->
+ triggerEvent(
+ event = AnalyticsEvent.WC_INVALID_REQUEST,
+ params = mapOf(
+ AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json
+ ).filterNotNull()
+ )
+ is Analytics.WcAnalyticsEvent.Session -> {
+ val analyticsEvent = when (event.event) {
+ Analytics.WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED
+ Analytics.WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION
+ }
+ triggerEvent(
+ event = analyticsEvent,
+ params = mapOf(
+ AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url
+ ).filterNotNull()
+ )
+ }
+ }
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt
new file mode 100644
index 0000000000..9692defe86
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt
@@ -0,0 +1,33 @@
+package com.tangem.tap.common.analytics
+
+import android.app.Application
+import com.appsflyer.AppsFlyerLib
+import com.tangem.common.card.Card
+import com.tangem.common.core.TangemSdkError
+
+class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() {
+
+ override fun triggerEvent(
+ event: AnalyticsEvent,
+ card: Card?,
+ blockchain: String?,
+ params: Map
+ ) {
+ AppsFlyerLib.getInstance().logEvent(context,
+ event.event, prepareParams(card, blockchain))
+ }
+
+ override fun logCardSdkError(
+ error: TangemSdkError,
+ actionToLog: Analytics.ActionToLog,
+ parameters: Map?,
+ card: Card?
+ ) {
+ //TODO
+ }
+
+ override fun logError(error: Throwable, params: Map) {
+ //TODO
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
index 33383e1089..106c07aae9 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
@@ -7,11 +7,16 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.ktx.Firebase
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
-import com.tangem.tap.common.extensions.filterNotNull
-object FirebaseAnalyticsHandler : AnalyticsHandler {
- override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: String?) {
- Firebase.analytics.logEvent(event.event, setData(card, blockchain))
+object FirebaseAnalyticsHandler : AnalyticsHandler() {
+ override fun triggerEvent(
+ event: AnalyticsEvent,
+ card: Card?,
+ blockchain: String?,
+ params: Map
+ ) {
+ val fullParams = prepareParams(card, blockchain) + params
+ Firebase.analytics.logEvent(event.event, fullParams.toBundle())
}
fun logException(name: String, throwable: Throwable) {
@@ -22,16 +27,20 @@ object FirebaseAnalyticsHandler : AnalyticsHandler {
))
}
+ private fun Map.toBundle(): Bundle {
+ return bundleOf(*this.toList().toTypedArray())
+ }
+
override fun logCardSdkError(
error: TangemSdkError,
- actionToLog: ActionToLog,
+ actionToLog: Analytics.ActionToLog,
parameters: Map?,
card: Card?,
) {
if (error is TangemSdkError.UserCancelled) return
val params = parameters?.toMutableMap() ?: mutableMapOf()
- if (card != null) params + getParamsFromCard(card)
+ if (card != null) params + prepareParams(card)
params[AnalyticsParam.ACTION] = actionToLog.key
params[AnalyticsParam.ERROR_CODE] = error.code.toString()
params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName
@@ -44,100 +53,8 @@ object FirebaseAnalyticsHandler : AnalyticsHandler {
FirebaseCrashlytics.getInstance().recordException(cardError)
}
- private fun getParamsFromCard(card: Card): Map {
- return mapOf(
- AnalyticsParam.FIRMWARE to card.firmwareVersion.stringValue,
- AnalyticsParam.BATCH_ID to card.batchId
- ).filterNotNull()
+ override fun logError(error: Throwable, params: Map) {
+ params.forEach { (key, value) -> FirebaseCrashlytics.getInstance().setCustomKey(key, value) }
+ FirebaseCrashlytics.getInstance().recordException(error)
}
-
- private fun setData(card: Card?, blockchain: String?): Bundle {
- if (card == null) return bundleOf()
- return bundleOf(
- AnalyticsParam.BLOCKCHAIN.param to (blockchain),
- AnalyticsParam.BATCH_ID.param to card.batchId,
- AnalyticsParam.FIRMWARE.param to card.firmwareVersion.stringValue
- )
- }
-
- fun logWcEvent(event: WcAnalyticsEvent) {
- when (event) {
- is WcAnalyticsEvent.Action -> {
-
- Firebase.analytics.logEvent(
- AnalyticsEvent.WC_SUCCESS_RESPONSE.event, bundleOf(
- AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name
- )
- )
- }
- is WcAnalyticsEvent.Error -> {
- mapOf(
- AnalyticsParam.WALLET_CONNECT_ACTION to event.action?.name,
- AnalyticsParam.ERROR_DESCRIPTION to event.error.message
- )
- .filterNotNull()
- .forEach {
- FirebaseCrashlytics.getInstance().setCustomKey(it.key.param, it.value)
- }
- FirebaseCrashlytics.getInstance().recordException(event.error)
- }
- is WcAnalyticsEvent.InvalidRequest ->
- Firebase.analytics.logEvent(
- AnalyticsEvent.WC_INVALID_REQUEST.event, bundleOf(
- AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json
- )
- )
- is WcAnalyticsEvent.Session -> {
- val analyticsEvent = when (event.event) {
- WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED
- WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION
- }
- Firebase.analytics.logEvent(
- analyticsEvent.event, bundleOf(
- AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url
- )
- )
- }
- }
-
- }
-
- enum class AnalyticsParam(val param: String) {
- BLOCKCHAIN("blockchain"),
- BATCH_ID("batch_id"),
- FIRMWARE("firmware"),
- ACTION("action"),
- ERROR_DESCRIPTION("error_description"),
- ERROR_CODE("error_code"),
- NEW_SECURITY_OPTION("new_security_option"),
- ERROR_KEY("Tangem SDK error key"),
- WALLET_CONNECT_ACTION("wallet_connect_action"),
- WALLET_CONNECT_REQUEST("wallet_connect_request"),
- WALLET_CONNECT_DAPP_URL("wallet_connect_dapp_url"),
- }
-
- enum class ActionToLog(val key: String) {
- Scan("tap_scan_task"),
- SendTransaction("send_transaction"),
- WalletConnectSign("wallet_connect_personal_sign"),
- WalletConnectTransaction("wallet_connect_tx_sign"),
- ReadPinSettings("read_pin_settings"),
- ChangeSecOptions("change_sec_options"),
- CreateWallet("create_wallet"),
- PurgeWallet("purge_wallet"),
- WriteIssuerData("write_issuer_data"),
- }
-
- enum class WcSessionEvent { Disconnect, Connect }
-
- sealed class WcAnalyticsEvent {
- data class Error(val error: Throwable, val action: WcAction?) :
- WcAnalyticsEvent()
-
- data class Session(val event: WcSessionEvent, val url: String?) : WcAnalyticsEvent()
- data class Action(val action: WcAction) : WcAnalyticsEvent()
- data class InvalidRequest(val json: String?) : WcAnalyticsEvent()
- }
-
- enum class WcAction { PersonalSign, SignTransaction, SendTransaction }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt
new file mode 100644
index 0000000000..da77c37e05
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt
@@ -0,0 +1,41 @@
+package com.tangem.tap.common.analytics
+
+import android.app.Application
+import com.tangem.common.card.Card
+import com.tangem.common.core.TangemSdkError
+
+class GlobalAnalyticsHandler(val analyticsHandlers: List) :
+ AnalyticsHandler() {
+ override fun triggerEvent(
+ event: AnalyticsEvent,
+ card: Card?,
+ blockchain: String?,
+ params: Map
+ ) {
+ analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain) }
+ }
+
+ override fun logCardSdkError(
+ error: TangemSdkError,
+ actionToLog: Analytics.ActionToLog,
+ parameters: Map?,
+ card: Card?
+ ) {
+ analyticsHandlers.forEach { it.logCardSdkError(error, actionToLog, parameters, card) }
+ }
+
+ override fun logError(error: Throwable, params: Map) {
+ analyticsHandlers.forEach { it.logError(error, params) }
+ }
+
+ companion object {
+ fun createDefaultAnalyticHandlers(context: Application): GlobalAnalyticsHandler {
+ return GlobalAnalyticsHandler(
+ listOf(
+ FirebaseAnalyticsHandler,
+ AppsFlyerAnalyticsHandler(context)
+ )
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt
index 233fa3702d..9a9f5c62b1 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt
@@ -105,6 +105,7 @@ object TangemSdk {
is TangemSdkError.WrongInteractionMode -> TangemSdkError.WrongInteractionMode()
is TangemSdkError.IssuerSignatureLoadingFailed -> TangemSdkError.IssuerSignatureLoadingFailed()
is TangemSdkError.BackupFailedFirmware -> TangemSdkError.BackupFailedFirmware()
+ is TangemSdkError.UserForgotTheCode -> TangemSdkError.UserForgotTheCode()
}
}
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt b/app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt
deleted file mode 100644
index 376b56f399..0000000000
--- a/app/src/main/java/com/tangem/tap/common/extensions/ByteArray.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.tangem.tap.common.extensions
-
-/**
-[REDACTED_AUTHOR]
- */
-
-data class ByteArrayKey(val bytes: ByteArray) {
- override fun equals(other: Any?): Boolean {
- return this === other || other is ByteArrayKey && this.bytes.contentEquals(other.bytes)
- }
-
- override fun hashCode(): Int = bytes.contentHashCode()
-}
-
-fun ByteArray.toMapKey(): ByteArrayKey = ByteArrayKey(this)
-
-fun ByteArrayKey.bytes(): ByteArray = this.bytes
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt
index 60c8a1ec9d..f34b5e9ecf 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.common.extensions
import android.graphics.PorterDuff
+import android.net.Uri
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
@@ -9,6 +10,7 @@ import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.IconsUtil
import com.tangem.blockchain.common.Token
+import com.tangem.tap.domain.extensions.getCustomIconUrl
import com.tangem.wallet.R
fun Picasso.loadCurrenciesIcon(
@@ -19,7 +21,7 @@ fun Picasso.loadCurrenciesIcon(
) {
val url = if (token != null) {
- IconsUtil.getTokenIconUri(blockchain, token)
+ token.getCustomIconUrl()?.let { Uri.parse(it) } ?: IconsUtil.getTokenIconUri(blockchain, token)
} else {
IconsUtil.getBlockchainIconUri(blockchain)
}
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
index 83aa152f76..678caeb9f0 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
@@ -18,8 +18,8 @@ fun Store<*>.dispatchOnMain(action: Action) {
}
}
-suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse, addAnalytics: Boolean = true) {
- store.state.globalState.tapWalletManager.onCardScanned(scanResponse, addAnalytics)
+suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) {
+ store.state.globalState.tapWalletManager.onCardScanned(scanResponse)
}
fun Store<*>.dispatchOpenUrl(url: String) {
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt
index 70a4fcc286..3d33ceeaa8 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt
@@ -1,11 +1,11 @@
package com.tangem.tap.common.extensions
+import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.services.Result
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.amountToCreateAccount
-import com.tangem.tap.domain.extensions.isNoAccountError
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.topup.TradeCryptoHelper
import com.tangem.tap.features.wallet.redux.AddressData
@@ -26,7 +26,7 @@ suspend fun WalletManager.safeUpdate(): Result = try {
val blockchain = wallet.blockchain
val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken())
- if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) {
+ if (exception is BlockchainSdkError.AccountNotFound && amountToCreateAccount != null) {
Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString()))
} else {
val message = exception.localizedMessage ?: "An error has occurred. Try later"
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt
deleted file mode 100644
index a95488d44f..0000000000
--- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt
+++ /dev/null
@@ -1,147 +0,0 @@
-package com.tangem.tap.common.leapfrogWidget
-
-import android.animation.Animator
-import android.animation.AnimatorSet
-import android.animation.ObjectAnimator
-import android.animation.ValueAnimator
-import android.view.View
-import android.view.animation.DecelerateInterpolator
-import android.view.animation.LinearInterpolator
-
-/**
-[REDACTED_AUTHOR]
- */
-//fun View.setForeground(hasForegroundAtEnd: Boolean) {
-// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-// foreground = getForegroundDrawable()
-// }
-//}
-
-//fun View.getForegroundDrawable(): Drawable? {
-// return AppCompatResources.getDrawable(context, R.drawable.shape_rectangle_rounded_8_op_20)?.also {
-// it.alpha = 60
-// }
-//}
-
-typealias ProgressListener = (Int) -> Unit
-
-fun createProgressListener(duration: Long, listener: ProgressListener?): Animator {
- val valueAnimator = ValueAnimator.ofInt(0, 100)
- valueAnimator.duration = duration
- valueAnimator.addUpdateListener { listener?.invoke((it.animatedValue as? Int) ?: 0) }
- return valueAnimator
-}
-
-fun View.unfoldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet {
- val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
- translate.duration = animDuration
- return AnimatorSet().apply { playTogether(translate) }
-}
-
-fun View.foldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet {
- val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
- translate.duration = animDuration
-
- return AnimatorSet().apply {
- playSequentially(translate)
- }
-}
-
-fun View.leapAnimation(animDuration: Long, properties: LeapViewProperties, overLift: Float): AnimatorSet {
- val halfDuration = animDuration / 2
-
- val upTo = (height.toFloat() + overLift) * -1
- val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo)
- translateUp.duration = halfDuration
- translateUp.interpolator = LinearInterpolator()
-
- val scaleDuration = halfDuration - halfDuration / 5
- val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
- val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
- val scale = AnimatorSet().apply {
- startDelay = scaleDuration
- duration = scaleDuration
- interpolator = LinearInterpolator()
- playTogether(scaleX, scaleY)
- }
-
- val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100)
- val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
- translateDown.startDelay = halfDuration
- translateDown.duration = halfDuration
- translateDown.interpolator = LinearInterpolator()
-
- return AnimatorSet().apply {
- playTogether(translateUp, scale, elevation, translateDown)
- }
-}
-
-fun View.leapBackAnimation(animDuration: Long, properties: LeapViewProperties, calculator: PropertyCalculator): AnimatorSet {
- val halfDuration = animDuration / 2
-
- val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1
- val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo)
- translateUp.duration = halfDuration
- translateUp.interpolator = LinearInterpolator()
-
- val scaleDuration = halfDuration - halfDuration / 5
- val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
- val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
- val scale = AnimatorSet().apply {
- startDelay = scaleDuration
- duration = scaleDuration
- interpolator = LinearInterpolator()
- playTogether(scaleX, scaleY)
- }
-
- val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100)
- val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
- translateDown.startDelay = halfDuration
- translateDown.duration = halfDuration
-
- return AnimatorSet().apply {
- playTogether(translateUp, scale, elevation, translateDown)
- }
-}
-
-fun View.pullUpAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet {
- val pullDuration = leapDuration / 2
- val delayDuration = leapDuration / 2
-
- val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
- val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
- val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
- val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd)
-
- return AnimatorSet().apply {
- startDelay = delayDuration
- duration = pullDuration
- interpolator = DecelerateInterpolator()
- playTogether(translateUp, scaleX, scaleY, elevation)
- }
-}
-
-fun View.pullDownAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet {
- val pullDuration = leapDuration / 2
- val delayDuration = leapDuration / 4
-
- val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
- val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
- val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
- val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd)
-
- return AnimatorSet().apply {
- startDelay = delayDuration
- duration = pullDuration
- interpolator = DecelerateInterpolator()
- playTogether(translate, scaleX, scaleY, elevation)
- }
-}
-
-fun View.elevationAnimator(start: Float, end: Float, startDelay: Long? = null, duration: Long? = null): Animator {
- val elevationAnimator = ValueAnimator.ofFloat(start, end)
- elevationAnimator.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: start }
- startDelay?.let { elevationAnimator.startDelay = it }
- duration?.let { elevationAnimator.duration = it }
- return elevationAnimator
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt
deleted file mode 100644
index be2d63b664..0000000000
--- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.tangem.tap.common.leapfrogWidget
-
-import android.view.View
-
-/**
-[REDACTED_AUTHOR]
- */
-class LeapView(
- val view: View,
- val index: Int,
- position: Int,
- private val maximalPosition: Int,
- private val calculator: PropertyCalculator,
-) {
- var state: LeapViewState
- private set
-
- val initialState: LeapViewState
-
- init {
- val initialProperties = createInitialProperty(position)
- state = LeapViewState(index, position, position, initialProperties)
- initialState = state.copy()
- }
-
- fun initView() {
- applyState(initialState)
- }
-
- fun leap(): LeapFrogAnimation {
- val previousPosition = state.currentPosition
- val currentPosition = when (previousPosition) {
- 0 -> maximalPosition
- else -> state.currentPosition - 1
- }
-
- state = state.copy(
- currentPosition = currentPosition,
- previousPosition = previousPosition,
- properties = createLeapAnimationProperty(previousPosition, currentPosition)
- )
-
- return when {
- previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP
- else -> LeapFrogAnimation.PULL
- }
- }
-
- fun leapBack(): LeapFrogAnimation {
- val previousPosition = state.currentPosition
- val currentPosition = when (previousPosition) {
- maximalPosition -> 0
- else -> state.currentPosition + 1
- }
- state = state.copy(
- currentPosition = currentPosition,
- previousPosition = previousPosition,
- properties = createLeapAnimationProperty(previousPosition, currentPosition)
- )
-
- return when {
- previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP
- else -> LeapFrogAnimation.PULL
- }
- }
-
- fun fold() {
- state = state.copy(properties = state.properties.toFold()
- )
- }
-
- fun unfold() {
- state = state.copy(properties = state.properties.toUnfold(calculator))
- }
-
- fun applyState(state: LeapViewState) {
- this.state = state
- setProperties(this.state.properties)
- }
-
- private fun setProperties(properties: LeapViewProperties) {
- view.elevation = properties.elevationEnd
- view.translationY = properties.yTranslation
- view.scaleX = properties.scale
- view.scaleY = properties.scale
-// initialProperties.hasForeground
- }
-
- private fun createInitialProperty(endPosition: Int): LeapViewProperties {
- return LeapViewProperties(
- endPosition,
- endPosition,
- calculator.scale(endPosition),
- calculator.elevation(endPosition, maximalPosition + 1),
- calculator.elevation(endPosition, maximalPosition + 1),
- calculator.yTranslation(0),
- calculator.hasForeground(endPosition),
- )
- }
-
- private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): LeapViewProperties {
- return LeapViewProperties(
- startPosition,
- endPosition,
- calculator.scale(endPosition),
- calculator.elevation(startPosition, maximalPosition + 1),
- calculator.elevation(endPosition, maximalPosition + 1),
- calculator.yTranslation(endPosition),
- calculator.hasForeground(endPosition),
- )
- }
-
- private fun LeapViewProperties.toUnfold(calculator: PropertyCalculator): LeapViewProperties {
- return this.copy(yTranslation = calculator.yTranslation(this.positionEnd))
- }
-
- private fun LeapViewProperties.toFold(): LeapViewProperties {
- return this.copy(yTranslation = 0f)
- }
-
- override fun toString(): String = "index: $index, position: ${state.currentPosition}, code: ${view.hashCode()}"
-
-}
-
-data class LeapViewState(
- val index: Int,
- val currentPosition: Int,
- val previousPosition: Int,
- val properties: LeapViewProperties
-)
-
-enum class LeapFrogAnimation {
- LEAP, PULL
-}
-
-data class LeapViewProperties(
- val positionStart: Int,
- val positionEnd: Int,
- val scale: Float,
- val elevationStart: Float,
- val elevationEnd: Float,
- val yTranslation: Float,
- val hasForeground: Boolean,
-)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt
deleted file mode 100644
index e83f7b8b92..0000000000
--- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt
+++ /dev/null
@@ -1,249 +0,0 @@
-package com.tangem.tap.common.leapfrogWidget
-
-import android.animation.Animator
-import android.animation.AnimatorSet
-import android.view.animation.AccelerateDecelerateInterpolator
-import android.widget.FrameLayout
-import androidx.core.animation.doOnEnd
-import androidx.core.view.children
-import com.tangem.common.extensions.VoidCallback
-import com.tangem.tangem_sdk_new.extensions.dpToPx
-
-/**
-[REDACTED_AUTHOR]
- */
-class LeapfrogWidget(
- private val parentContainer: FrameLayout,
- private val calculator: PropertyCalculator = PropertyCalculator(),
-) {
- private val foldAnimationDuration = 300L
- private val leapAnimationDuration = 500L
- private val leapBackAnimationDuration = 500L
-
- private val leapViews = mutableListOf()
-
- private var foldUnfoldProgress: Int = 100
- private var leapProgress: Int = 100
- private var leapBackProgress: Int = 100
-
- init {
- calculator.setTranslationConverter { parentContainer.dpToPx(it) }
- if (viewIsFullFledged()) {
- val maxPosition = parentContainer.childCount - 1
- parentContainer.children.forEachIndexed { index, view ->
- val initialPosition = maxPosition - index
- leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator))
- }
- }
- }
-
- /**
- * Apply initial properties to the views
- */
- fun initViews() {
- leapViews.forEach { it.initView() }
- }
-
- fun fold(animate: Boolean = true, onEnd: VoidCallback = {}) {
- val animator = foldAnimator(animate)
- if (animator == null) {
- onEnd()
- } else {
- animator.doOnEnd { onEnd() }
- animator.start()
- }
- }
-
- fun unfold(animate: Boolean = true, onEnd: VoidCallback = {}) {
- val animator = unfoldAnimator(animate)
- if (animator == null) {
- onEnd()
- } else {
- animator.doOnEnd { onEnd() }
- animator.start()
- }
- }
-
- fun foldAnimator(animate: Boolean = true): Animator? {
- if (!canFoldUnfold()) return null
-
- return createFoldUnfoldAnimator(true, animate, foldAnimationDuration)
- }
-
- fun unfoldAnimator(animate: Boolean = true): Animator? {
- if (!canFoldUnfold()) return null
-
- return createFoldUnfoldAnimator(false, animate, foldAnimationDuration)
- }
-
- private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, animate: Boolean, duration: Long): Animator {
- val animatorsList = mutableListOf()
- val animateDuration = if (animate) duration else 0
- leapViews.forEach {
- if (isFoldAnimation) {
- it.fold()
- animatorsList.add(it.view.foldAnimation(animateDuration, it.state.properties))
- } else {
- it.unfold()
- animatorsList.add(it.view.unfoldAnimation(animateDuration, it.state.properties))
- }
- }
- animatorsList.add(createProgressListener(animateDuration) { foldUnfoldProgress = it })
-
- val animator = AnimatorSet()
- animator.interpolator = AccelerateDecelerateInterpolator()
- animator.playTogether(animatorsList.toList())
- return animator
- }
-
- fun leap(animate: Boolean = true, onEnd: VoidCallback = {}) {
- if (!canLeap()) {
- onEnd()
- return
- }
-
- val duration = if (animate) leapAnimationDuration else 0
- val animatorsList = mutableListOf()
- leapViews.forEach {
- when (it.leap()) {
- LeapFrogAnimation.LEAP -> {
- val overLift = calculator.overLift(leapViews.size)
- animatorsList.add(it.view.leapAnimation(duration, it.state.properties, overLift))
- }
- LeapFrogAnimation.PULL -> {
- animatorsList.add(it.view.pullUpAnimation(duration, it.state.properties))
- }
- }
- }
- animatorsList.add(createProgressListener(duration) { leapProgress = it })
-
- val animator = AnimatorSet()
- animator.playTogether(animatorsList.toList())
- animator.doOnEnd { onEnd() }
- animator.start()
- }
-
- fun leapBack(animate: Boolean = true, onEnd: VoidCallback = {}) {
- if (!canLeapBack()) {
- onEnd()
- return
- }
-
- val duration = if (animate) leapBackAnimationDuration else 0
- val animatorsList = mutableListOf()
- leapViews.forEach {
- when (it.leapBack()) {
- LeapFrogAnimation.LEAP -> {
- animatorsList.add(it.view.leapBackAnimation(duration, it.state.properties, calculator))
- }
- LeapFrogAnimation.PULL -> {
- animatorsList.add(it.view.pullDownAnimation(duration, it.state.properties))
- }
- }
- }
- animatorsList.add(createProgressListener(duration) { leapBackProgress = it })
-
- val animator = AnimatorSet()
- animator.playTogether(animatorsList.toList())
- animator.doOnEnd { onEnd() }
- animator.start()
- }
-
- fun getViewsCount(): Int = parentContainer.childCount
-
- fun getViewPositionByIndex(index: Int): Int = leapViews.first { it.index == index }.state.currentPosition
-
- fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position }
-
- fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(leapViews.map { it.state })
-
- fun applyState(state: LeapfrogWidgetState) {
- state.leapViewStates.forEach { viewState ->
- leapViews.firstOrNull { it.index == viewState.index }?.applyState(viewState)
- }
- }
-
- private fun viewIsFullFledged(): Boolean = parentContainer.childCount > 1
-
- private fun canFoldUnfold(): Boolean {
- return when {
- !viewIsFullFledged() -> false
- foldUnfoldInProgress() -> false
- leapInProgress() || leapBackInProgress() -> false
- else -> true
- }
- }
-
- private fun canLeap(): Boolean {
- return when {
- !viewIsFullFledged() -> false
- leapBackInProgress() -> false
- leapProgress < 70 -> false
- else -> true
- }
- }
-
- private fun canLeapBack(): Boolean {
- return when {
- !viewIsFullFledged() -> false
- leapInProgress() -> false
- leapBackProgress < 70 -> false
- else -> true
- }
- }
-
- private fun foldUnfoldInProgress(): Boolean = foldUnfoldProgress != 100
- private fun leapInProgress(): Boolean = leapProgress != 100
- private fun leapBackInProgress(): Boolean = leapBackProgress != 100
-}
-
-data class LeapfrogWidgetState(
- val leapViewStates: List
-)
-
-class PropertyCalculator(
- val elevationFactor: Float = 1f,
- val decreaseScaleFactor: Float = 0.15f,
- val yTranslationFactor: Float = 35f,
- val decreaseOverLiftingFactor: Float = 0.85f,
-) {
- // 0 view it is view on the top of stack, but it is last in parent.children
-
- private var translationValueConverter: ((Float) -> Float)? = null
- fun setTranslationConverter(converter: (Float) -> Float) {
- translationValueConverter = converter
- }
-
- fun scale(position: Int): Float {
- return 1f - decreaseScaleFactor * position
- }
-
- fun elevation(position: Int, count: Int): Float {
- return elevationFactor * (count - 1 - position)
- }
-
- fun yTranslation(position: Int): Float {
- val yTranslation = yTranslationFactor * position
- return convertTranslation(yTranslation)
- }
-
- fun hasForeground(position: Int): Boolean {
- return position != 0
- }
-
- fun overLift(viewCount: Int): Float {
- val initialOverLift = when {
- yTranslationFactor < 0f -> yTranslationFactor * decreaseOverLiftingFactor * viewCount * -1
- else -> 10f
- }
- val scaleOverLift = when {
- decreaseScaleFactor == 0f -> initialOverLift
- else -> initialOverLift * decreaseOverLiftingFactor - initialOverLift * decreaseScaleFactor
- }
- return convertTranslation(scaleOverLift)
- }
-
- private fun convertTranslation(value: Float): Float {
- return translationValueConverter?.invoke(value) ?: value
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt
index 1a41ff35be..36e92a0ebe 100644
--- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt
+++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt
@@ -5,6 +5,8 @@ import android.view.View
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
+import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
+import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidgetState
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.test_leapfrog_fragment.*
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
index bbea6e43f9..5f0db04ae5 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
@@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.global
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError
+import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
import com.tangem.tap.common.redux.*
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.config.ConfigManager
@@ -69,6 +70,7 @@ sealed class GlobalAction : Action {
data class SetConfigManager(val configManager: ConfigManager) : GlobalAction()
data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction()
data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction()
+ data class SetAnanlyticHandlers(val analyticsHandlers: GlobalAnalyticsHandler) : GlobalAction()
data class SendFeedback(val emailData: EmailData) : GlobalAction()
data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction()
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt
index b16fbe21c7..6200feb947 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt
@@ -4,7 +4,6 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.services.Result
import com.tangem.tap.*
-import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.withMainContext
@@ -89,9 +88,8 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState
is GlobalAction.ScanCard -> {
scope.launch {
val result = tangemSdkManager.scanProduct(
- FirebaseAnalyticsHandler,
+ store.state.globalState.analyticsHandlers,
currenciesRepository,
- action.shouldDeriveWC,
action.messageResId
)
withMainContext {
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
index e549a2f386..94621117bc 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
@@ -46,6 +46,8 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
globalState.copy(configManager = action.configManager)
}
is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager)
+ is GlobalAction.SetAnanlyticHandlers ->
+ globalState.copy(analyticsHandlers = action.analyticsHandlers)
is GlobalAction.UpdateWalletSignedHashes -> {
val card = globalState.scanResponse?.card ?: return globalState
val wallet = card.wallet(action.walletPublicKey) ?: return globalState
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt
index f634a6a0ff..c607125cb8 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt
@@ -1,5 +1,6 @@
package com.tangem.tap.common.redux.global
+import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.PayIdManager
@@ -28,6 +29,7 @@ data class GlobalState(
val dialog: StateDialog? = null,
val moonpayStatus: MoonpayStatus? = null,
val resources: AndroidResources = AndroidResources(),
+ val analyticsHandlers: AnalyticsHandler? = null,
) : StateType
diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt
index 4c3f0c78d4..2605d263c3 100644
--- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt
@@ -14,18 +14,19 @@ import com.tangem.common.core.CardIdDisplayFormat
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.Config
import com.tangem.common.core.TangemSdkError
+import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.operations.CommandResponse
+import com.tangem.operations.derivation.DerivationTaskResponse
+import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.pins.CheckUserCodesCommand
import com.tangem.operations.pins.CheckUserCodesResponse
import com.tangem.operations.pins.SetUserCodeCommand
+import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
-import com.tangem.tap.common.extensions.ByteArrayKey
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
-import com.tangem.tap.domain.tasks.DerivationTask
-import com.tangem.tap.domain.tasks.DerivationTaskResponse
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
import com.tangem.tap.domain.tasks.product.ScanProductTask
import com.tangem.tap.domain.tasks.product.ScanResponse
@@ -39,16 +40,15 @@ import kotlin.coroutines.suspendCoroutine
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
suspend fun scanProduct(
- analyticsHandler: AnalyticsHandler,
+ analyticsHandler: AnalyticsHandler?,
currenciesRepository: CurrenciesRepository,
- shouldDeriveWC: Boolean,
messageRes: Int? = null,
): CompletionResult {
- analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
+ analyticsHandler?.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header))
return runTaskAsyncReturnOnMain(ScanProductTask(null, currenciesRepository), null, message)
- .also { sendScanFailuresToAnalytics(analyticsHandler, it) }
+ .also { sendScanResultsToAnalytics(analyticsHandler, it) }
}
suspend fun createProductWallet(
@@ -61,14 +61,24 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
)
}
- private fun sendScanFailuresToAnalytics(
- analyticsHandler: AnalyticsHandler,
+ private fun sendScanResultsToAnalytics(
+ analyticsHandler: AnalyticsHandler?,
result: CompletionResult
) {
- if (result is CompletionResult.Failure && result.error is TangemSdkError) {
- (result.error as? TangemSdkError)?.let { error ->
- analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan)
- }
+ when (result) {
+ is CompletionResult.Success ->
+ analyticsHandler?.triggerEvent(
+ event = AnalyticsEvent.CARD_IS_SCANNED,
+ card = result.data.card,
+ blockchain = result.data.walletData?.blockchain
+ )
+ is CompletionResult.Failure ->
+ (result.error as? TangemSdkError)?.let { error ->
+ analyticsHandler?.logCardSdkError(
+ error = error,
+ actionToLog = Analytics.ActionToLog.Scan
+ )
+ }
}
}
@@ -81,7 +91,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
cardId: String,
derivations: Map>
): CompletionResult {
- return runTaskAsyncReturnOnMain(DerivationTask(derivations), cardId)
+ return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
}
suspend fun resetToFactorySettings(card: Card): CompletionResult {
diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
index 9f6d0f13e1..cfd628a093 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
@@ -2,8 +2,6 @@ package com.tangem.tap.domain
import com.tangem.blockchain.common.*
import com.tangem.common.services.Result
-import com.tangem.tap.common.analytics.AnalyticsEvent
-import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.redux.global.FiatCurrencyName
@@ -71,10 +69,7 @@ class TapWalletManager {
handleFiatRatesResult(results)
}
- suspend fun onCardScanned(data: ScanResponse, addAnalyticsEvent: Boolean = false) {
- if (addAnalyticsEvent) {
- FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain)
- }
+ suspend fun onCardScanned(data: ScanResponse) {
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card)
updateConfigManager(data)
diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt
index cae1861582..412537befb 100644
--- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt
@@ -11,6 +11,7 @@ data class Config(
val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
+ val appsFlyerDevKey: String = "",
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
val isSendingToPayIdEnabled: Boolean = true,
val isTopUpEnabled: Boolean = false,
@@ -84,7 +85,8 @@ class ConfigManager(
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
blockcypherTokens = values.blockcypherTokens,
infuraProjectId = values.infuraProjectId
- )
+ ),
+ appsFlyerDevKey = values.appsFlyerDevKey,
)
defaultConfig = defaultConfig.copy(
coinMarketCapKey = values.coinMarketCapKey,
@@ -95,7 +97,8 @@ class ConfigManager(
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
blockcypherTokens = values.blockcypherTokens,
infuraProjectId = values.infuraProjectId
- )
+ ),
+ appsFlyerDevKey = values.appsFlyerDevKey,
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt
index c71a4a16dd..edbbf6345c 100644
--- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt
+++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt
@@ -19,6 +19,7 @@ class ConfigValueModel(
val blockchairAuthorizationToken: String?,
val blockcypherTokens: Set?,
val infuraProjectId: String?,
+ val appsFlyerDevKey: String,
)
class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) {
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt
index 0563eb8482..673ef1787b 100644
--- a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt
@@ -6,14 +6,6 @@ import com.tangem.common.card.EllipticCurve
import java.math.BigDecimal
-fun Blockchain.isNoAccountError(exception: Throwable): Boolean {
- return when (this) {
- Blockchain.Stellar, Blockchain.XRP ->
- exception.message?.contains("Account not found") == true
- else -> false
- }
-}
-
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
return when (this) {
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ONE
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt
new file mode 100644
index 0000000000..6963ed3682
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt
@@ -0,0 +1,17 @@
+package com.tangem.tap.domain.extensions
+
+import com.tangem.blockchain.common.Token
+
+/**
+[REDACTED_AUTHOR]
+ */
+
+private val tokenCustomIconUrls = mutableMapOf()
+
+fun Token.getCustomIconUrl(): String? {
+ return tokenCustomIconUrls[this.contractAddress]
+}
+
+fun Token.setCustomIconUrl(url: String) {
+ tokenCustomIconUrls[this.contractAddress] = url
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt
index b505619739..3b58ec418e 100644
--- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt
@@ -6,7 +6,7 @@ import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.common.card.CardWallet
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.hexToBytes
-import com.tangem.tap.common.extensions.toMapKey
+import com.tangem.common.extensions.toMapKey
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.tasks.product.ScanResponse
@@ -33,13 +33,23 @@ fun WalletManagerFactory.makeWalletManagerForApp(
}
seedKey != null -> {
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
- val derivedKey = derivedKeys?.firstOrNull { it.derivationPath == blockchain.derivationPath() }
+ val derivedKey = derivedKeys?.get(blockchain.derivationPath())
?: return null
- makeWalletManager(card.cardId, environmentBlockchain, seedKey, derivedKey)
+ makeWalletManager(
+ cardId = card.cardId,
+ blockchain = environmentBlockchain,
+ seedKey = wallet.publicKey,
+ derivedKey = derivedKey
+ )
}
else -> {
- makeWalletManager(card.cardId, environmentBlockchain, wallet.publicKey, wallet.curve)
+ makeWalletManager(
+ cardId = card.cardId,
+ blockchain = environmentBlockchain,
+ walletPublicKey = wallet.publicKey,
+ curve = wallet.curve
+ )
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt
index 8f03e068b5..3c471f4bc4 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt
@@ -1,50 +1,55 @@
-package com.tangem.tap.domain.tasks
-
-import com.tangem.common.CompletionResult
-import com.tangem.common.core.CardSession
-import com.tangem.common.core.CardSessionRunnable
-import com.tangem.common.core.CompletionCallback
-import com.tangem.common.hdWallet.DerivationPath
-import com.tangem.operations.CommandResponse
-import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
-import com.tangem.operations.derivation.ExtendedPublicKeyList
-import com.tangem.tap.common.extensions.ByteArrayKey
-
-class DerivationTaskResponse(
- val entries: Map
-): CommandResponse
-
-class DerivationTask(
- private val derivations: Map>
-) : CardSessionRunnable {
-
- val response: MutableMap = mutableMapOf()
-
- override fun run(session: CardSession, callback: CompletionCallback) {
- derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback)
- }
-
- private fun derive(
- keys: List,
- index: Int,
- session: CardSession,
- callback: CompletionCallback
- ) {
- if (index == keys.count()) {
- callback(CompletionResult.Success(DerivationTaskResponse(response.toMap())))
- return
- }
-
- val key = keys[index]
- val paths = derivations[key]!!
- DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result ->
- when (result) {
- is CompletionResult.Success -> {
- response[key] = result.data
- derive(keys = keys, index = index + 1, session = session, callback = callback)
- }
- is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
- }
- }
- }
-}
\ No newline at end of file
+//package com.tangem.tap.domain.tasks
+//
+//import com.tangem.common.CompletionResult
+//import com.tangem.common.core.CardSession
+//import com.tangem.common.core.CardSessionRunnable
+//import com.tangem.common.core.CompletionCallback
+//import com.tangem.common.hdWallet.DerivationPath
+//import com.tangem.common.hdWallet.ExtendedPublicKey
+//import com.tangem.operations.CommandResponse
+//import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
+//import com.tangem.tap.common.extensions.ByteArrayKey
+//
+//class ExtendedPublicKeyList(
+// items: Collection
+//): ArrayList(items), CommandResponse
+//
+//
+//class DerivationTaskResponse(
+// val entries: Map
+//): CommandResponse
+//
+//class DerivationTask(
+// private val derivations: Map>
+//) : CardSessionRunnable {
+//
+// val response: MutableMap = mutableMapOf()
+//
+// override fun run(session: CardSession, callback: CompletionCallback) {
+// derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback)
+// }
+//
+// private fun derive(
+// keys: List,
+// index: Int,
+// session: CardSession,
+// callback: CompletionCallback
+// ) {
+// if (index == keys.count()) {
+// callback(CompletionResult.Success(DerivationTaskResponse(response.toMap())))
+// return
+// }
+//
+// val key = keys[index]
+// val paths = derivations[key]!!
+// DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result ->
+// when (result) {
+// is CompletionResult.Success -> {
+// response[key] = result.data[key]
+// derive(keys = keys, index = index + 1, session = session, callback = callback)
+// }
+// is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
+// }
+// }
+// }
+//}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt
index 89bdb12dd2..e75da7ca72 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt
@@ -6,16 +6,16 @@ import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
-import com.tangem.common.hdWallet.ExtendedPublicKey
+import com.tangem.common.extensions.toMapKey
import com.tangem.operations.CommandResponse
import com.tangem.operations.backup.PrimaryCard
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
+import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
+import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
-import com.tangem.tap.common.extensions.toMapKey
import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
-import com.tangem.tap.domain.tasks.DerivationTask
import com.tangem.tap.domain.tasks.product.CreateWalletsTask
import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey
import com.tangem.tap.domain.tasks.product.ProductCommandProcessor
@@ -24,7 +24,7 @@ import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets
data class CreateProductWalletTaskResponse(
val card: Card,
- val derivedKeys: Map> = mapOf(),
+ val derivedKeys: Map = mapOf(),
val primaryCard: PrimaryCard? = null
) : CommandResponse
@@ -170,7 +170,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor
when (result) {
is CompletionResult.Success -> {
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt
index 506f5aa9c3..05ab2ed34b 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt
@@ -12,19 +12,20 @@ import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
+import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toHexString
+import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
-import com.tangem.common.hdWallet.ExtendedPublicKey
import com.tangem.operations.CommandResponse
import com.tangem.operations.PreflightReadMode
import com.tangem.operations.PreflightReadTask
import com.tangem.operations.ScanTask
import com.tangem.operations.backup.PrimaryCard
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
+import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
+import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
-import com.tangem.tap.common.extensions.ByteArrayKey
-import com.tangem.tap.common.extensions.toMapKey
import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapSdkError
import com.tangem.tap.domain.TapWorkarounds
@@ -33,7 +34,6 @@ import com.tangem.tap.domain.TapWorkarounds.isExcluded
import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.tap.domain.extensions.getPrimaryCurve
import com.tangem.tap.domain.extensions.getSingleWallet
-import com.tangem.tap.domain.tasks.DerivationTask
import com.tangem.tap.domain.tokens.CurrenciesRepository
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.preferencesStorage
@@ -43,7 +43,7 @@ data class ScanResponse(
val productType: ProductType,
val walletData: WalletData?,
val secondTwinPublicKey: String? = null,
- val derivedKeys: Map> = mapOf(),
+ val derivedKeys: Map = mapOf(),
val primaryCard: PrimaryCard? = null
) : CommandResponse {
@@ -253,7 +253,7 @@ private class ScanWalletProcessor(
return
}
- DerivationTask(derivations).run(session) { result ->
+ DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
val response = ScanResponse(
diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt
index fc1ae2bcb2..02d42e5869 100644
--- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt
@@ -9,7 +9,10 @@ import com.squareup.moshi.Types
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.card.FirmwareVersion
+import com.tangem.common.json.MoshiJsonConverter
import com.tangem.tap.common.extensions.readJsonFileToString
+import com.tangem.tap.domain.extensions.getCustomIconUrl
+import com.tangem.tap.domain.extensions.setCustomIconUrl
import com.tangem.tap.network.createMoshi
class CurrenciesRepository(val context: Application) {
@@ -113,24 +116,28 @@ class CurrenciesRepository(val context: Application) {
if (isTestNet) BSC_TESTNET_TOKENS_FILE_NAME else BSC_TOKENS_FILE_NAME
val binanceTokensFileName =
if (isTestNet) BINANCE_TESTNET_TOKENS_FILE_NAME else BINANCE_TOKENS_FILE_NAME
+ val avalancheTokensFileName =
+ if (isTestNet) AVALANCHE_TESTNET_TOKENS_FILE_NAME else AVALANCHE_TOKENS_FILE_NAME
val ethereumTokensJson = context.assets.readJsonFileToString(ethereumTokensFileName)
val bscTokensJson = context.assets.readJsonFileToString(bscTokensFileName)
val binanceTokensJson = context.assets.readJsonFileToString(binanceTokensFileName)
+ val avalancheTokensJson = context.assets.readJsonFileToString(avalancheTokensFileName)
return tokensAdapter.fromJson(ethereumTokensJson)!!.map { it.toToken() } +
- tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } +
- tokensAdapter.fromJson(binanceTokensJson)!!.mapNotNull {
- // temporary exclude Binance BEP-8 tokens
- if (it.type != null && it.type == BINANCE_TOKEN_TYPE_BEP8) null else it.toToken()
- }
+ tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } +
+ tokensAdapter.fromJson(binanceTokensJson)!!.mapNotNull {
+ // temporary exclude Binance BEP-8 tokens
+ if (it.type != null && it.type == BINANCE_TOKEN_TYPE_BEP8) null else it.toToken()
+ } +
+ tokensAdapter.fromJson(avalancheTokensJson)!!.map { it.toToken() }
}
fun getBlockchains(
- cardFirmware: FirmwareVersion?,
+ cardFirmware: FirmwareVersion,
isTestNet: Boolean = false
): List {
- return if (cardFirmware == null || cardFirmware < FirmwareVersion.MultiWalletAvailable) {
+ return if (cardFirmware < FirmwareVersion.MultiWalletAvailable) {
Blockchain.secp256k1Blockchains(isTestNet)
} else {
Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet)
@@ -144,6 +151,9 @@ class CurrenciesRepository(val context: Application) {
private const val BSC_TESTNET_TOKENS_FILE_NAME = "bsc_tokens_testnet"
private const val BINANCE_TOKENS_FILE_NAME = "binance_tokens"
private const val BINANCE_TESTNET_TOKENS_FILE_NAME = "binance_tokens_testnet"
+ private const val AVALANCHE_TOKENS_FILE_NAME = "avalanche_tokens"
+ private const val AVALANCHE_TESTNET_TOKENS_FILE_NAME = "avalanche_tokens_testnet"
+
private const val FILE_NAME_PREFIX_TOKENS = "tokens"
private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains"
@@ -152,6 +162,18 @@ class CurrenciesRepository(val context: Application) {
fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId"
fun getFileNameForBlockchains(cardId: String): String =
"${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId"
+
+ fun injectDaoBlockchain(context: Context, tokenFileName: String, blockchain: Blockchain): String? {
+ val tokenJson = context.assets.readJsonFileToString(tokenFileName)
+ val converter = MoshiJsonConverter.default()
+ val rawTokensList = converter.fromJson>>(tokenJson) ?: return null
+
+ rawTokensList.forEach {
+ it["blockchain"] = BlockchainDao.fromBlockchain(blockchain)
+ }
+
+ return converter.toJson(rawTokensList)
+ }
}
}
@@ -163,6 +185,7 @@ data class TokenDao(
val decimalCount: Int,
@Json(name = "blockchain")
val blockchainDao: BlockchainDao,
+ val customIconUrl: String? = null,
val type: String? = null
) {
fun toToken(): Token {
@@ -172,7 +195,9 @@ data class TokenDao(
contractAddress = contractAddress,
decimals = decimalCount,
blockchain = blockchainDao.toBlockchain()
- )
+ ).apply {
+ customIconUrl?.let { this.setCustomIconUrl(it) }
+ }
}
companion object {
@@ -182,7 +207,8 @@ data class TokenDao(
symbol = token.symbol,
contractAddress = token.contractAddress,
decimalCount = token.decimals,
- blockchainDao = BlockchainDao.fromBlockchain(token.blockchain)
+ blockchainDao = BlockchainDao.fromBlockchain(token.blockchain),
+ customIconUrl = token.getCustomIconUrl()
)
}
}
@@ -205,8 +231,7 @@ data class BlockchainDao(
companion object {
fun fromBlockchain(blockchain: Blockchain): BlockchainDao {
val name = blockchain.name.removeSuffix("Testnet").lowercase()
- val isTestnet = blockchain.name.endsWith("Testnet")
- return BlockchainDao(name, isTestnet)
+ return BlockchainDao(name, blockchain.isTestnet())
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
index 861f3db801..53bd278cc7 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
@@ -11,12 +11,17 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.operations.wallet.CreateWalletResponse
-import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.analytics.Analytics
+import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.network.createMoshi
import com.tangem.tap.tangemSdkManager
-class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
+class TwinCardsManager(
+ private val card: Card,
+ assetReader: AssetReader,
+ val analyticsHandler: AnalyticsHandler?
+) {
private val currentCardId: String = card.cardId
@@ -31,9 +36,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString()
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
- FirebaseAnalyticsHandler.logCardSdkError(
+ analyticsHandler?.logCardSdkError(
error,
- FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
+ Analytics.ActionToLog.CreateWallet,
card = card
)
}
@@ -61,9 +66,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
}
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
- FirebaseAnalyticsHandler.logCardSdkError(
+ analyticsHandler?.logCardSdkError(
error,
- FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
+ Analytics.ActionToLog.CreateWallet,
card = card
)
}
@@ -81,9 +86,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
is CompletionResult.Success -> Result.Success(response.data)
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
- FirebaseAnalyticsHandler.logCardSdkError(
+ analyticsHandler?.logCardSdkError(
error,
- FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData,
+ Analytics.ActionToLog.WriteIssuerData,
card = card
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt
index c8f3c02952..becb529e80 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt
@@ -6,9 +6,10 @@ import android.animation.PropertyValuesHolder
import android.view.View
import androidx.core.animation.doOnEnd
import com.tangem.common.extensions.VoidCallback
-import com.tangem.tap.common.leapfrogWidget.LeapView
-import com.tangem.tap.common.leapfrogWidget.LeapViewState
-import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
+import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView
+import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState
+import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
+
/**
[REDACTED_AUTHOR]
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt
index 94ce765f12..37f9715e19 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt
@@ -8,7 +8,7 @@ import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.guard
-import com.tangem.common.files.FileHashHelper
+import com.tangem.operations.files.FileHashHelper
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
import com.tangem.operations.issuerAndUserData.ReadIssuerDataResponse
import com.tangem.operations.issuerAndUserData.WriteIssuerDataCommand
diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt
new file mode 100644
index 0000000000..a8f3eb83c1
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/BnbHelper.kt
@@ -0,0 +1,79 @@
+package com.tangem.tap.domain.walletconnect
+
+import com.github.salomonbrys.kotson.registerTypeAdapter
+import com.google.gson.GsonBuilder
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.common.extensions.calculateSha256
+import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
+import com.tangem.tap.features.details.redux.walletconnect.TradeData
+import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
+import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
+import com.trustwallet.walletconnect.models.binance.tradeOrderSerializer
+import timber.log.Timber
+
+class BnbHelper {
+ companion object {
+
+ fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
+ val input = order.msgs.first().inputs.first()
+ val output = order.msgs.first().inputs.first()
+
+ val currency =
+ input.coins.firstOrNull()?.denom ?: Blockchain.Binance
+ val amount = input.coins
+ .mapNotNull { if (it.denom == currency) it.amount else null }
+ .sum()
+ .toBigDecimal()
+ .movePointRight(Blockchain.Binance.decimals())
+ .toString()
+
+ val gson = GsonBuilder()
+ .registerTypeAdapter(tradeOrderSerializer)
+ .serializeNulls()
+ .create()
+
+ return BinanceMessageData.Transfer(
+ outputAddress = output.address,
+ amount = amount,
+ address = input.address,
+ data = gson.toJson(order).toByteArray().calculateSha256()
+ )
+ }
+
+ fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
+ val address = order.msgs.first().sender
+
+ val tradeData = order.msgs.map {
+ val price = it.price.toBigDecimal()
+ .movePointLeft(Blockchain.Binance.decimals())
+ .stripTrailingZeros()
+ val quantity = it.quantity.toBigDecimal()
+ .movePointLeft(Blockchain.Binance.decimals())
+ .stripTrailingZeros()
+
+ val amount = price * quantity
+ val symbol = it.symbol.substringBefore("-")
+
+ TradeData(
+ price = "$price ${Blockchain.Binance.currency}",
+ quantity = "$quantity $symbol",
+ amount = "$amount ${Blockchain.Binance.currency}",
+ symbol = symbol
+ )
+ }
+ val gson = GsonBuilder()
+ .registerTypeAdapter(tradeOrderSerializer)
+ .serializeNulls()
+ .create()
+ val serialized = gson.toJson(order)
+ Timber.d(serialized)
+
+ return BinanceMessageData.Trade(
+ tradeData = tradeData,
+ address = address,
+ data = serialized.toByteArray().calculateSha256()
+ )
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt
new file mode 100644
index 0000000000..fe8a593787
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt
@@ -0,0 +1,99 @@
+package com.tangem.tap.domain.walletconnect
+
+import com.github.salomonbrys.kotson.fromJson
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonArray
+import com.google.gson.JsonParser
+import com.google.gson.annotations.SerializedName
+import com.trustwallet.walletconnect.JSONRPC_VERSION
+import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
+
+class EthSignHelper {
+ companion object {
+ private val gson: Gson by lazy {
+ GsonBuilder()
+ .setPrettyPrinting()
+ .serializeNulls()
+ .create()
+ }
+
+ fun tryToParseEthTypedMessage(data: String): WCEthereumSignMessage? {
+ val request =
+ gson.fromJson(data)
+ return if (request.method == WCMethodExtended.ETH_SIGN_TYPE_DATA_V4) {
+ WCEthereumSignMessage(
+ listOf(
+ request.params[0].asString,
+ request.params[1].asString,
+ ),
+ WCEthereumSignMessage.WCSignType.TYPED_MESSAGE
+ )
+ } else {
+ null
+ }
+ }
+
+ fun tryToParseEthTypedMessageString(message: String): String? {
+ return try {
+ val messageString = message
+ .replace("\\", "")
+ .removePrefix("\"")
+ .removeSuffix("\"")
+ val messageJson = JsonParser().parse(messageString)
+ val filteredMap = gson.fromJson