Updated on 2026-08-14

This commit is contained in:
Tangem 2023-07-24 13:45:56 +03:00
commit ea661f55ab
250 changed files with 6169 additions and 949 deletions

View file

@ -25,11 +25,13 @@ dependencies {
implementation(project(":domain:legacy"))
implementation(project(":domain:models"))
implementation(project(":domain:core"))
implementation(project(":domain:card"))
implementation(projects.domain.card)
implementation(project(":domain:wallets"))
implementation(project(":domain:wallets:models"))
implementation(projects.domain.tokens)
implementation(project(":common"))
implementation(project(":core:analytics"))
implementation(projects.core.analytics.models)
implementation(project(":core:navigation"))
implementation(project(":core:featuretoggles"))
implementation(project(":core:res"))
@ -39,6 +41,9 @@ dependencies {
implementation(project(":libs:crypto"))
implementation(project(":libs:auth"))
implementation(project(":data:source:preferences"))
implementation(projects.data.card)
implementation(projects.data.tokens)
implementation(projects.data.common)
/** Features */
implementation(project(":features:onboarding"))
@ -119,13 +124,6 @@ dependencies {
implementation(deps.appsflyer)
implementation(deps.amplitude)
implementation(deps.kotsonGson)
//TODO: refactoring: remove it when all network services moved to the datasource module
implementation(deps.retrofit)
implementation(deps.retrofit.moshi)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.okHttp)
implementation(deps.okHttp.logging)
implementation(deps.zendesk.chat)
implementation(deps.zendesk.messaging)
implementation(deps.spongecastle.core)
@ -144,6 +142,7 @@ dependencies {
implementation(deps.kotlin.serialization)
implementation(deps.walletConnectCore)
implementation(deps.walletConnectWeb3)
implementation(deps.prettyLogger)
/** Testing libraries */
testImplementation(deps.test.junit)

View file

@ -10,10 +10,11 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemSdk
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.data.card.sdk.CardSdkLifecycleObserver
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
@ -56,7 +57,6 @@ import java.lang.ref.WeakReference
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
lateinit var tangemSdk: TangemSdk
lateinit var tangemSdkManager: TangemSdkManager
lateinit var backupService: BackupService
internal var lockUserWalletsTimer: LockUserWalletsTimer? = null
@ -88,7 +88,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
lateinit var testerRouter: TesterRouter
@Inject
lateinit var injectedTangemSdk: TangemSdk
lateinit var cardSdkLifecycleObserver: CardSdkLifecycleObserver
@Inject
lateinit var cardSdkConfigRepository: CardSdkConfigRepository
@Inject
lateinit var injectedTangemSdkManager: TangemSdkManager
@ -122,11 +125,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
systemActions()
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
tangemSdk = injectedTangemSdk
cardSdkLifecycleObserver.onCreate(context = this)
tangemSdkManager = injectedTangemSdkManager
appStateHolder.tangemSdkManager = tangemSdkManager
appStateHolder.tangemSdk = tangemSdk
backupService = BackupService.init(tangemSdk, this)
backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
lockUserWalletsTimer = LockUserWalletsTimer(owner = this)
initUserWalletsListManager()
@ -144,10 +147,37 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
walletRouter = walletRouter,
walletConnectInteractor = walletConnectInteractor,
tokenDetailsRouter = tokenDetailsRouter,
cardSdkConfigRepository = cardSdkConfigRepository,
),
)
}
override fun onStart() {
super.onStart()
dialogManager.onStart(this)
}
override fun onResume() {
super.onResume()
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
navigateToInitialScreenIfNeededOnResume(intent)
}
override fun onStop() {
notificationsHandler = null
dialogManager.onStop()
super.onStop()
}
override fun onDestroy() {
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
intentProcessor.removeAll()
cardSdkLifecycleObserver.onDestroy()
super.onDestroy()
}
private fun initIntentHandlers() {
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider))
@ -183,13 +213,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
override fun onResume() {
super.onResume()
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
navigateToInitialScreenIfNeededOnResume(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
scope.launch {
@ -197,23 +220,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
override fun onStart() {
super.onStart()
dialogManager.onStart(this)
}
override fun onStop() {
notificationsHandler = null
dialogManager.onStop()
super.onStop()
}
override fun onDestroy() {
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
intentProcessor.removeAll()
super.onDestroy()
}
override fun showSnackbar(text: Int, buttonTitle: Int?, action: View.OnClickListener?) {
if (snackbar != null) return

View file

@ -5,6 +5,8 @@ import android.content.Context
import android.content.pm.PackageManager
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.Log
import com.tangem.LogFormat
import com.tangem.blockchain.common.BlockchainSdkConfig
@ -14,12 +16,14 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
import com.tangem.data.source.preferences.PreferencesDataSource
import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import com.tangem.datasource.asset.AssetReader
import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.config.FeaturesLocalLoader
import com.tangem.datasource.config.models.Config
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.DomainLayer
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.common.LogConfig
import com.tangem.domain.wallets.legacy.WalletManagersRepository
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
@ -59,7 +63,6 @@ import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.wallet.BuildConfig
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.runBlocking
import okhttp3.logging.HttpLoggingInterceptor
import org.rekotlin.Store
import timber.log.Timber
import javax.inject.Inject
@ -151,6 +154,9 @@ class TapApplication : Application(), ImageLoaderFactory {
@Inject
lateinit var tokenDetailsFeatureToggles: TokenDetailsFeatureToggles
@Inject
lateinit var scanCardProcessor: ScanCardProcessor
override fun onCreate() {
super.onCreate()
@ -168,12 +174,20 @@ class TapApplication : Application(), ImageLoaderFactory {
walletConnectRepository = walletConnect2Repository,
walletConnectSessionsRepository = walletConnectSessionsRepository,
tokenDetailsFeatureToggles = tokenDetailsFeatureToggles,
scanCardProcessor = scanCardProcessor,
),
),
)
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
Logger.addLogAdapter(AndroidLogAdapter())
Timber.plant(
object : Timber.DebugTree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
Logger.log(priority, tag, message, t)
}
},
)
}
foregroundActivityObserver = ForegroundActivityObserver()
@ -192,7 +206,7 @@ class TapApplication : Application(), ImageLoaderFactory {
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY },
createNetworkLoggingInterceptor(),
)
}

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]
@ -13,7 +13,6 @@ sealed class MainScreen(
class ScreenOpened : MainScreen("Screen opened")
class ButtonScanCard : MainScreen("Button - Scan Card")
class ButtonMyWallets : MainScreen("Button - My Wallets")
class ButtonBuy : MainScreen("Button - Buy")
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
event = "Enable Biometric",

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.extensions.filterNotNull

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
sealed class MyWallets(
event: String,

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]
@ -72,26 +72,8 @@ sealed class Onboarding(
class SetupFinished : Twins("Twin Setup Finished")
}
class PinScreenOpened : Onboarding("Onboarding", "PIN screen opened")
class ButtonSetPinCode : Onboarding("Onboarding", "Button - Set PIN Code")
class CardConnectionScreenOpened : Onboarding("Onboarding", "Card Connection Screen Opened")
class ButtonConnect : Onboarding("Onboarding", "Button - Connect")
class PinCodeSet : Onboarding("Onboarding", "PIN Code Set")
class KYCScreenOpened : Onboarding("Onboarding", "KYC screen opened")
class KYCStarted : Onboarding("Onboarding", "KYC Started")
class KYCInProgress : Onboarding("Onboarding", "KYC In Progress")
class KYCRejected : Onboarding("Onboarding", "KYC Rejected")
class ClaimScreenOpened : Onboarding("Onboarding", "Claim Screen Opened")
class ButtonClaim : Onboarding("Onboarding", "Button - Claim")
class ClaimWasSuccessfully : Onboarding("Onboarding", "Claim Was Successfully")
class ButtonChat : Onboarding("Onboarding", "Button - Chat")
class NotEnoughGasError : Onboarding("Onboarding", "Not Enough Gas Error")
class CardNotPassedError : Onboarding("Onboarding", "Card Not Passed Error")
class EnableBiometrics(state: AnalyticsParam.OnOffState) : Onboarding(
category = "Onboarding / Biometric",
event = "Enable Biometric",

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.features.details.ui.details.SocialNetwork
/**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.extensions.filterNotNull
/**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType
/**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics.handlers.appsFlyer
import com.appsflyer.AFInAppEventType
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.events.Shop

View file

@ -1,9 +1,9 @@
package com.tangem.tap.common.analytics.handlers.firebase
import com.google.firebase.analytics.FirebaseAnalytics
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.ErrorEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
import com.tangem.tap.common.analytics.events.Shop

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse

View file

@ -1,14 +1,14 @@
package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.models.scan.ScanResponse
/**
[REDACTED_AUTHOR]
*/
class LinkedCardContextInterceptor(
private val scanResponse: ScanResponse,
scanResponse: ScanResponse,
val parent: LinkedCardContextInterceptor? = null,
) : ParamsInterceptor {

View file

@ -9,6 +9,7 @@ import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.common.analytics.converters.TopUpEventConverter
@ -17,7 +18,6 @@ import com.tangem.tap.common.extensions.copy
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
import com.tangem.tap.domain.walletStores.WalletStoresManager
import com.tangem.tap.features.wallet.models.Currency

View file

@ -12,6 +12,7 @@ import com.tangem.tap.common.chat.opener.implementation.SprinklrChatOpener
import com.tangem.tap.common.chat.opener.implementation.ZendeskChatOpener
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Store
import java.io.File
class ChatManager(
private val preferencesStorage: PreferencesDataSource,
@ -20,7 +21,7 @@ class ChatManager(
) {
private val openers = mutableMapOf<ChatConfig, ChatOpener>()
fun open(config: ChatConfig, feedbackDataBuilder: (Context) -> String) {
fun open(config: ChatConfig, createLogsFile: (Context) -> File?, createFeedbackFile: (Context) -> File?) {
val opener = openers.getOrPut(config) {
when (config) {
is SprinklrConfig -> SprinklrChatOpener(getSprinklrUserId(), config, store, foregroundActivityObserver)
@ -28,7 +29,10 @@ class ChatManager(
}
}
opener.open(feedbackDataBuilder)
opener.open(
createFeedbackFile = createFeedbackFile,
createLogsFile = createLogsFile,
)
}
private fun getZendeskUserId(): String {

View file

@ -1,7 +1,8 @@
package com.tangem.tap.common.chat.opener
import android.content.Context
import java.io.File
internal interface ChatOpener {
fun open(feedbackDataBuilder: (Context) -> String)
fun open(createFeedbackFile: (Context) -> File?, createLogsFile: (Context) -> File?)
}

View file

@ -2,14 +2,15 @@ package com.tangem.tap.common.chat.opener.implementation
import android.content.Context
import android.content.Intent
import com.tangem.datasource.config.models.SprinklrConfig
import com.tangem.tap.ForegroundActivityObserver
import com.tangem.tap.common.chat.opener.ChatOpener
import com.tangem.datasource.config.models.SprinklrConfig
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.sprinklr.redux.SprinklrAction
import com.tangem.tap.features.sprinklr.ui.SprinklrActivity
import com.tangem.tap.withForegroundActivity
import org.rekotlin.Store
import java.io.File
internal class SprinklrChatOpener(
private val userId: String,
@ -17,7 +18,7 @@ internal class SprinklrChatOpener(
private val store: Store<AppState>,
private val foregroundActivityObserver: ForegroundActivityObserver,
) : ChatOpener {
override fun open(feedbackDataBuilder: (Context) -> String) {
override fun open(createFeedbackFile: (Context) -> File?, createLogsFile: (Context) -> File?) {
store.dispatch(SprinklrAction.Init(userId, config))
foregroundActivityObserver.withForegroundActivity { activity ->
val intent = Intent(activity, SprinklrActivity::class.java)

View file

@ -2,34 +2,34 @@ package com.tangem.tap.common.chat.opener.implementation
import android.content.Context
import com.tangem.core.analytics.Analytics
import com.tangem.datasource.config.models.ZendeskConfig
import com.tangem.domain.common.LogConfig
import com.tangem.tap.ForegroundActivityObserver
import com.tangem.tap.common.chat.opener.ChatOpener
import com.tangem.datasource.config.models.ZendeskConfig
import com.tangem.tap.withForegroundActivity
import com.tangem.wallet.R
import com.zendesk.logger.Logger
import zendesk.chat.Chat
import zendesk.chat.ChatConfiguration
import zendesk.chat.ChatEngine
import zendesk.chat.ChatProvidersConfiguration
import zendesk.chat.VisitorInfo
import timber.log.Timber
import zendesk.chat.*
import zendesk.configurations.Configuration
import zendesk.messaging.MessagingActivity
import java.io.File
internal class ZendeskChatOpener(
private val userId: String,
private val config: ZendeskConfig,
private val foregroundActivityObserver: ForegroundActivityObserver,
) : ChatOpener {
private var isInitialized = false
override fun open(feedbackDataBuilder: (Context) -> String) {
override fun open(createFeedbackFile: (Context) -> File?, createLogsFile: (Context) -> File?) {
foregroundActivityObserver.withForegroundActivity { activity ->
initZendeskIfNeeded(activity.applicationContext)
setChatVisitorInfo()
setChatVisitorNote(feedbackDataBuilder(activity))
showMessagingActivity(activity)
sendFeedbackFile(createFeedbackFile(activity))
sendLogsFile(createLogsFile(activity))
}
}
@ -48,8 +48,20 @@ internal class ZendeskChatOpener(
ChatProvidersConfiguration.builder().withVisitorInfo(visitorInfo).build()
}
private fun setChatVisitorNote(note: String) {
Chat.INSTANCE.providers()?.profileProvider()?.setVisitorNote(note)
private fun sendFeedbackFile(feedbackFile: File?) {
if (isInitialized && feedbackFile != null) {
Chat.INSTANCE.providers()?.chatProvider()?.sendFile(feedbackFile) { _, bytesUploaded, _ ->
Timber.d("Log file sent", "bytesUploaded: $bytesUploaded")
}
}
}
private fun sendLogsFile(logsFile: File?) {
if (isInitialized && logsFile != null) {
Chat.INSTANCE.providers()?.chatProvider()?.sendFile(logsFile) { _, bytesUploaded, _ ->
Timber.d("Log file sent", "bytesUploaded: $bytesUploaded")
}
}
}
private fun showMessagingActivity(context: Context) {

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common.di.domain.wallets
import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -17,4 +18,10 @@ object WalletsDomainModule {
fun providesGetWalletsUseCase(walletsStateHolder: WalletsStateHolder): GetWalletsUseCase {
return GetWalletsUseCase(walletsStateHolder = walletsStateHolder)
}
@Provides
@ViewModelScoped
fun providesSaveWalletUseCase(walletsStateHolder: WalletsStateHolder): SaveWalletUseCase {
return SaveWalletUseCase(walletsStateHolder = walletsStateHolder)
}
}

View file

@ -24,13 +24,17 @@ fun BigDecimal.toFormattedString(
df.decimalFormatSymbols = symbols
df.maximumFractionDigits = decimals
df.minimumFractionDigits = 0
df.isGroupingUsed = false
df.isGroupingUsed = true
df.roundingMode = roundingMode
return df.format(this)
}
/**
* To formatted crypto currency string
* Specific method because there is no crypto currency codes in Locale
*/
@Suppress("MagicNumber")
fun BigDecimal.toFormattedCurrencyString(
fun BigDecimal.toFormattedCryptoCurrencyString(
decimals: Int,
currency: String,
roundingMode: RoundingMode = RoundingMode.DOWN,
@ -41,16 +45,34 @@ fun BigDecimal.toFormattedCurrencyString(
} else {
decimals
}
try {
val locale = Locale.getDefault()
val formatter = NumberFormat.getCurrencyInstance(locale)
// first create currency instance for "USD" with Locale default to replace currency to crypto later
Currency.getInstance("USD")?.let { currencyTmp ->
formatter.currency = currencyTmp
formatter.maximumFractionDigits = decimalsForRounding
formatter.minimumFractionDigits = 0
formatter.isGroupingUsed = true
formatter.roundingMode = roundingMode
// cause formatter created for USD, replace Currency with Crypto symbol on right by Locale place
return formatter.format(this).replace(currencyTmp.getSymbol(locale), "$currency")
}
} catch (e: IllegalArgumentException) {
Timber.e(e, "can't parse currency")
}
// if something went wrong - use old way to format
val formattedAmount = this.toFormattedString(
decimals = decimalsForRounding,
roundingMode = roundingMode,
locale = Locale.getDefault(),
)
return "$formattedAmount $currency"
return "$formattedAmount $currency"
}
fun BigDecimal.toFiatRateString(fiatCurrencyName: String, fiatCode: String): String {
try {
val formatter = NumberFormat.getCurrencyInstance()
val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
Currency.getInstance(fiatCode)?.let { currency ->
formatter.currency = currency
formatter.maximumFractionDigits = 2
@ -91,7 +113,7 @@ fun BigDecimal.toFormattedFiatValue(
formatWithSpaces: Boolean = false,
): String {
try {
val formatter = NumberFormat.getCurrencyInstance()
val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
Currency.getInstance(fiatCode)?.let { currency ->
formatter.currency = currency
formatter.maximumFractionDigits = 2

View file

@ -1,17 +1,12 @@
package com.tangem.tap.common.feedback
import android.os.Build
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.Address
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
class AdditionalFeedbackInfo {
class EmailWalletInfo(

View file

@ -1,9 +1,9 @@
package com.tangem.tap.common.feedback
import android.content.Context
import com.tangem.datasource.config.models.ChatConfig
import com.tangem.domain.common.TapWorkarounds
import com.tangem.tap.common.chat.ChatManager
import com.tangem.datasource.config.models.ChatConfig
import com.tangem.tap.common.extensions.sendEmail
import com.tangem.tap.common.log.TangemLogCollector
import com.tangem.tap.foregroundActivityObserver
@ -22,32 +22,61 @@ class FeedbackManager(
private val chatManager: ChatManager,
) {
private var sessionFeedbackFile: File? = null
private var sessionLogsFile: File? = null
fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) {
feedbackData.prepare(infoHolder)
foregroundActivityObserver.withForegroundActivity { activity ->
val fileLog = createLogFile(activity)
activity.sendEmail(
email = getSupportEmail(),
subject = activity.getString(feedbackData.subjectResId),
message = feedbackData.joinTogether(activity, infoHolder),
file = fileLog,
file = getLogFile(activity),
onFail = onFail,
)
}
}
fun openChat(config: ChatConfig, feedbackData: FeedbackData) {
chatManager.open(config) { context ->
feedbackData.run {
chatManager.open(
config = config,
createLogsFile = ::getLogFile,
createFeedbackFile = { context -> getFeedbackFile(context, feedbackData) },
)
}
private fun getFeedbackFile(context: Context, feedbackData: FeedbackData): File? {
return try {
if (sessionFeedbackFile != null) {
return sessionFeedbackFile
}
val file = File(context.filesDir, FEEDBACK_FILE)
file.delete()
file.createNewFile()
val feedback = feedbackData.run {
prepare(infoHolder)
joinTogether(context, infoHolder)
}
val fileWriter = FileWriter(file)
fileWriter.write(feedback)
fileWriter.close()
sessionFeedbackFile = file
sessionFeedbackFile
} catch (ex: Exception) {
Timber.e(ex, "Can't create the logs file")
null
}
}
private fun createLogFile(context: Context): File? {
private fun getLogFile(context: Context): File? {
return try {
val file = File(context.filesDir, "logs.txt")
if (sessionLogsFile != null) {
return sessionLogsFile
}
val file = File(context.filesDir, LOGS_FILE)
file.delete()
file.createNewFile()
@ -57,7 +86,8 @@ class FeedbackManager(
fileWriter.write(stringWriter.toString())
fileWriter.close()
logCollector.clearLogs()
file
sessionLogsFile = file
sessionLogsFile
} catch (ex: Exception) {
Timber.e(ex, "Can't create the logs file")
null
@ -75,5 +105,7 @@ class FeedbackManager(
companion object {
const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com"
const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com"
const val FEEDBACK_FILE = "feedback.txt"
const val LOGS_FILE = "logs.txt"
}
}

View file

@ -4,8 +4,8 @@ import android.content.Context
import android.util.Log
import coil.ImageLoader
import coil.util.Logger
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
private const val COIL_LOG_TAG = "COIL"
@ -18,14 +18,7 @@ fun createCoilImageLoader(context: Context, logEnabled: Boolean = false): ImageL
logger(CoilTimberLogger())
okHttpClient {
OkHttpClient.Builder()
.addNetworkInterceptor(
HttpLoggingInterceptor { message ->
Timber.tag(COIL_LOG_TAG).d(message)
}
.apply {
level = HttpLoggingInterceptor.Level.BODY
},
)
.addNetworkInterceptor(createNetworkLoggingInterceptor())
.build()
}
}

View file

@ -8,6 +8,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE
import me.dm7.barcodescanner.zxing.ZXingScannerView
@ -16,11 +17,15 @@ import me.dm7.barcodescanner.zxing.ZXingScannerView
[REDACTED_AUTHOR]
*/
class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
private lateinit var mScannerView: ZXingScannerView
override fun onCreate(state: Bundle?) {
super.onCreate(state)
mScannerView = ZXingScannerView(this)
mScannerView = ZXingScannerView(this).apply {
setFormats(listOf(BarcodeFormat.QR_CODE))
}
setContentView(mScannerView)
if (!permissionIsGranted()) requestPermission()

View file

@ -3,7 +3,8 @@ package com.tangem.tap.common.redux
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import org.rekotlin.Middleware
class AccessCodeRequestPolicyMiddleware {
@ -19,9 +20,8 @@ class AccessCodeRequestPolicyMiddleware {
}
private fun updateAccessCodeRequestPolicy(scanResponse: ScanResponse) {
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes &&
scanResponse.card.isAccessCodeSet,
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes && scanResponse.card.isAccessCodeSet,
)
}
}

View file

@ -1,42 +1,43 @@
package com.tangem.tap.di
import android.content.Context
import androidx.fragment.app.FragmentActivity
import com.tangem.TangemSdk
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.sdk.extensions.initWithBiometrics
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
import com.tangem.tap.userTokensRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(ActivityComponent::class)
@InstallIn(SingletonComponent::class)
internal object ActivityModule {
@Provides
@ActivityScoped
fun provideTangemSdk(@ActivityContext context: Context): TangemSdk {
return TangemSdk.initWithBiometrics(context as FragmentActivity, TangemSdkManager.config)
@Singleton
fun provideTangemSdkManager(
@ApplicationContext context: Context,
cardSdkConfigRepository: CardSdkConfigRepository,
): TangemSdkManager {
return TangemSdkManager(cardSdkConfigRepository = cardSdkConfigRepository, resources = context.resources)
}
@Provides
@ActivityScoped
fun provideTangemSdkManager(@ActivityContext context: Context, tangemSdk: TangemSdk): TangemSdkManager {
return TangemSdkManager(tangemSdk, context)
}
@Provides
@ActivityScoped
fun provideScanCardUseCase(tangemSdk: TangemSdk, tangemSdkManager: TangemSdkManager): ScanCardUseCase {
@Singleton
fun provideScanCardUseCase(
tangemSdkManager: TangemSdkManager,
cardSdkConfigRepository: CardSdkConfigRepository,
): ScanCardUseCase {
return ScanCardUseCase(
tangemSdk = tangemSdk,
scanCardRepository = DefaultScanCardRepository(userTokensRepository, tangemSdkManager),
cardSdkConfigRepository = cardSdkConfigRepository,
scanCardRepository = DefaultScanCardRepository(
userTokensRepository = userTokensRepository,
tangemSdkManager = tangemSdkManager,
),
)
}
}

View file

@ -0,0 +1,46 @@
package com.tangem.tap.di.domain
import com.tangem.domain.card.GetAccessCodeSavingStatusUseCase
import com.tangem.domain.card.GetBiometricsStatusUseCase
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.SetAccessCodeRequestPolicyUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object CardDomainModule {
@Provides
@Singleton
fun provideScanCardUseCase(): ScanCardProcessor = DefaultScanCardProcessor()
@Provides
@Singleton
fun provideGetBiometricsStatusUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
): GetBiometricsStatusUseCase {
return GetBiometricsStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
}
@Provides
@Singleton
fun provideSetAccessCodeRequestPolicyUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
): SetAccessCodeRequestPolicyUseCase {
return SetAccessCodeRequestPolicyUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
}
@Provides
@Singleton
fun provideGetAccessCodeSavingStatusUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
): GetAccessCodeSavingStatusUseCase {
return GetAccessCodeSavingStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
}
}

View file

@ -0,0 +1,34 @@
package com.tangem.tap.di.domain
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.repository.TokensRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(ViewModelComponent::class)
internal object TokensDomainModule {
@Provides
@ViewModelScoped
fun provideGetTokenListUseCase(
tokensRepository: TokensRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetTokenListUseCase {
return GetTokenListUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideToggleTokenListSortingUseCase(dispatchers: CoroutineDispatcherProvider): ToggleTokenListSortingUseCase {
return ToggleTokenListSortingUseCase(dispatchers)
}
}

View file

@ -1,27 +1,25 @@
package com.tangem.tap.domain
import android.content.Context
import android.content.res.Resources
import androidx.annotation.StringRes
import com.tangem.Message
import com.tangem.TangemSdk
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.*
import com.tangem.common.biometric.BiometricManager
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.*
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics
import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.ScanTask
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.operations.usersetttings.SetUserCodeRecoveryAllowedTask
import com.tangem.tap.common.analytics.events.Basic
@ -38,7 +36,10 @@ import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
@Suppress("TooManyFunctions")
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigRepository, private val resources: Resources) {
private val tangemSdk: TangemSdk
get() = cardSdkConfigRepository.sdk
private val userCodeRepository by lazy {
UserCodeRepository(
@ -63,7 +64,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
messageRes: Int? = null,
allowsRequestAccessCodeFromRepository: Boolean = false,
): CompletionResult<ScanResponse> {
val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header))
val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
return runTaskAsyncReturnOnMain(
runnable = ScanProductTask(
card = null,
@ -80,7 +81,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsync(
CreateProductWalletTask(scanResponse.cardTypesResolver),
scanResponse.card.cardId,
Message(context.getString(R.string.initial_message_create_wallet_body)),
Message(resources.getString(R.string.initial_message_create_wallet_body)),
)
}
@ -92,7 +93,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
is CompletionResult.Success -> runTaskAsync(
CreateProductWalletTask(scanResponse.cardTypesResolver, seedResult.data),
scanResponse.card.cardId,
Message(context.getString(R.string.initial_message_create_wallet_body)),
Message(resources.getString(R.string.initial_message_create_wallet_body)),
)
is CompletionResult.Failure -> CompletionResult.Failure(seedResult.error)
@ -111,7 +112,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
CreateWalletAndRescanTask(),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)),
initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)),
)
.map { CardDTO(it) }
}
@ -127,7 +128,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
runnable = ResetToFactorySettingsTask(),
cardId = cardId,
initialMessage = Message(context.getString(R.string.card_settings_reset_card_to_factory)),
initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)),
)
.map { CardDTO(it) }
}
@ -154,7 +155,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
SetUserCodeCommand.changePasscode(null),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_change_passcode_body)),
initialMessage = Message(resources.getString(R.string.initial_message_change_passcode_body)),
)
}
@ -162,7 +163,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
SetUserCodeCommand.changeAccessCode(null),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_change_access_code_body)),
initialMessage = Message(resources.getString(R.string.initial_message_change_access_code_body)),
)
}
@ -170,15 +171,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
SetUserCodeCommand.resetUserCodes(),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)),
)
}
suspend fun checkUserCodes(cardId: String?): CompletionResult<CheckUserCodesResponse> {
return runTaskAsyncReturnOnMain(
CheckUserCodesCommand(),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)),
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
)
}
@ -186,7 +179,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
SetUserCodeRecoveryAllowedTask(enabled),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)),
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
)
}
@ -197,7 +190,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain(
runnable = ScanTask(allowRequestAccessCodeFromRepository),
cardId = cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)),
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
)
.map { CardDTO(it) }
}
@ -233,8 +226,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
}
}
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
return context.getString(stringResId, *formatArgs)
return resources.getString(stringResId, *formatArgs)
}
fun setAccessCodeRequestPolicy(useBiometricsForAccessCode: Boolean) {

View file

@ -1,24 +1,17 @@
package com.tangem.tap.domain.extensions
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.userwallets.Artwork
import com.tangem.operations.attestation.CardVerifyAndGetInfo
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.features.wallet.redux.Artwork
val CardDTO.remainingSignatures: Int?
get() = this.wallets.firstOrNull()?.remainingSignatures
val CardDTO.isWalletDataSupported: Boolean
get() = this.firmwareVersion.major >= 4
val CardDTO.isFirmwareMultiwalletAllowed: Boolean
get() = firmwareVersion >= FirmwareVersion.MultiWalletAvailable && settings.maxWalletsCount > 1
val CardDTO.isHdWalletAllowedByApp: Boolean
get() = settings.isHDWalletAllowed
@ -58,16 +51,4 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInf
is Result.Failure -> ifAnyError()
}
}
fun CardDTO.getArtworkUrl(artworkId: String?): String? {
return when {
artworkId != null -> {
OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> null
}
}

View file

@ -31,7 +31,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
val seedKey = wallet.extendedPublicKey
return when {
scanResponse.cardTypesResolver.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
makeTwinWalletManager(
createTwinWalletManager(
walletPublicKey = wallet.publicKey,
pairPublicKey = scanResponse.secondTwinPublicKey!!.hexToBytes(),
blockchain = environmentBlockchain,
@ -47,7 +47,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
val derivedKey = derivedKeys?.get(derivationPath)
?: return null
makeWalletManager(
createWalletManager(
blockchain = environmentBlockchain,
seedKey = wallet.publicKey,
derivedKey = derivedKey,
@ -55,7 +55,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
)
}
else -> {
makeWalletManager(
createLegacyWalletManager(
blockchain = environmentBlockchain,
walletPublicKey = wallet.publicKey,
curve = wallet.curve,

View file

@ -2,21 +2,22 @@ package com.tangem.tap.domain.scanCard
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
// TODO: Remove this object after feature toggle was removed and use ScanCardUseCase instead
internal object ScanCardProcessor {
internal class DefaultScanCardProcessor : ScanCardProcessor {
private val isNewCardScanningEnabled: Boolean
get() = store.state.daggerGraphState
.get(DaggerGraphState::customTokenFeatureToggles)
.isNewCardScanningEnabled
suspend fun scan(
cardId: String? = null,
allowsRequestAccessCodeFromRepository: Boolean = false,
override suspend fun scan(
cardId: String?,
allowsRequestAccessCodeFromRepository: Boolean,
): CompletionResult<ScanResponse> {
return if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository)
@ -26,15 +27,15 @@ internal object ScanCardProcessor {
}
@Suppress("LongParameterList")
suspend fun scan(
analyticsEvent: AnalyticsEvent? = null,
cardId: String? = null,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {},
onScanStateChange: suspend (scanInProgress: Boolean) -> Unit = {},
onWalletNotCreated: suspend () -> Unit = {},
disclaimerWillShow: () -> Unit = {},
onFailure: suspend (error: TangemError) -> Unit = {},
onSuccess: suspend (scanResponse: ScanResponse) -> Unit = {},
override suspend fun scan(
analyticsEvent: AnalyticsEvent?,
cardId: String?,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
onScanStateChange: suspend (scanInProgress: Boolean) -> Unit,
onWalletNotCreated: suspend () -> Unit,
disclaimerWillShow: () -> Unit,
onFailure: suspend (error: TangemError) -> Unit,
onSuccess: suspend (scanResponse: ScanResponse) -> Unit,
) {
if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(

View file

@ -6,7 +6,7 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.canSkipBackup

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.scanCard
import arrow.fx.coroutines.resourceScope
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.card.ScanCardException

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.scanCard.chains
import arrow.core.Either
import arrow.core.right
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.core.chain.Chain
import com.tangem.domain.models.scan.ScanResponse

View file

@ -31,8 +31,7 @@ class ResetToFactorySettingsTask : CardSessionRunnable<Card> {
}
private fun resetBackup(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
val backupStatus = session.environment.card?.backupStatus
if (backupStatus == null || backupStatus == Card.BackupStatus.NoBackup) {
if (session.environment.card?.backupStatus?.isActive != true) {
callback(CompletionResult.Success(session.environment.card!!))
return
}

View file

@ -10,7 +10,7 @@ import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.files.AndroidFileReader
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.models.scan.CardDTO
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.tap.domain.tokens.converters.CurrencyConverter
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.models.Currency

View file

@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.analytics.events.WalletConnect
@ -36,7 +37,6 @@ import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
import java.util.*
import java.util.concurrent.TimeUnit
@ -56,8 +56,9 @@ class WalletConnectManager {
.addInterceptor(RetryInterceptor())
.build()
}
private val interceptor by lazy {
HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }
createNetworkLoggingInterceptor()
}
private var sessions: MutableMap<Topic, WalletConnectActiveData> = mutableMapOf()

View file

@ -6,6 +6,7 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.Companion.toKeccak
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.extensions.hexToBigDecimal
@ -35,8 +36,8 @@ import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTrans
import com.tangem.tap.features.details.redux.walletconnect.*
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import com.tangem.tap.tangemSdkManager
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage.WCSignType.*
import timber.log.Timber
@ -76,7 +77,8 @@ class WalletConnectSdkHelper {
val transactionData = TransactionData(
amount = Amount(value, wallet.blockchain),
fee = Amount(fee, wallet.blockchain),
// TODO refactoring
fee = Fee.Common(Amount(fee, wallet.blockchain)),
sourceAddress = transaction.from,
destinationAddress = transaction.to!!,
extras = EthereumTransactionExtras(
@ -161,7 +163,10 @@ class WalletConnectSdkHelper {
private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? {
val result = (data.walletManager as TransactionSender).send(
transactionData = data.transaction,
signer = CommonSigner(tangemSdk, cardId),
signer = CommonSigner(
tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk,
cardId = cardId,
),
)
return when (result) {
SimpleResult.Success -> {
@ -182,7 +187,6 @@ class WalletConnectSdkHelper {
transactionData = data.transaction,
nonce = null,
blockchain = data.walletManager.wallet.blockchain,
gasLimit = null,
) ?: return null
val command = SignHashCommand(

View file

@ -6,6 +6,8 @@ import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.toBlockchainSdkError
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.CompletionResult
@ -84,13 +86,13 @@ object DemoHelper {
class DemoTransactionSender(private val walletManager: WalletManager) : TransactionSender {
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
override suspend fun getFee(amount: Amount, destination: String): Result<TransactionFee> {
val blockchain = walletManager.wallet.blockchain
return Result.Success(
listOf(
Amount(0.0001.toBigDecimal(), blockchain),
Amount(0.0002.toBigDecimal(), blockchain),
Amount(0.0003.toBigDecimal(), blockchain),
TransactionFee.Choosable(
minimum = Fee.Common(Amount(minimumFee, blockchain)),
normal = Fee.Common(Amount(normalFee, blockchain)),
priority = Fee.Common(Amount(priorityFee, blockchain)),
),
)
}
@ -120,5 +122,9 @@ class DemoTransactionSender(private val walletManager: WalletManager) : Transact
companion object {
val ID = DemoTransactionSender::class.java.simpleName
private val minimumFee = 0.0001.toBigDecimal()
private val normalFee = 0.0002.toBigDecimal()
private val priorityFee = 0.0003.toBigDecimal()
}
}

View file

@ -12,6 +12,8 @@ import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -23,9 +25,6 @@ import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
import com.tangem.tap.domain.userWalletList.isLockedSync
@ -33,6 +32,7 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
@ -74,7 +74,8 @@ class DetailsMiddleware {
is DetailsAction.AccessCodeRecovery -> accessCodeRecoveryMiddleware.handle(state, action)
DetailsAction.ScanCard -> {
scope.launch {
ScanCardProcessor.scan(allowsRequestAccessCodeFromRepository = true)
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor)
.scan(allowsRequestAccessCodeFromRepository = true)
.doOnSuccess { scanResponse ->
// if we use biometric, scanResponse in GlobalState is null, and crashes NPE on twin cards
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
@ -371,9 +372,9 @@ class DetailsMiddleware {
Analytics.send(Settings.AppSettings.SaveAccessCodeSwitcherChanged(AnalyticsParam.OnOffState.On))
preferencesStorage.shouldSaveAccessCodes = true
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = scanResponse?.card?.isAccessCodeSet == true,
)
store.state.daggerGraphState
.get(DaggerGraphState::cardSdkConfigRepository)
.setAccessCodeRequestPolicy(isBiometricsRequestPolicy = scanResponse?.card?.isAccessCodeSet == true)
return CompletionResult.Success(Unit)
}
@ -384,9 +385,9 @@ class DetailsMiddleware {
Analytics.send(Settings.AppSettings.SaveAccessCodeSwitcherChanged(AnalyticsParam.OnOffState.Off))
preferencesStorage.shouldSaveAccessCodes = false
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = false,
)
store.state.daggerGraphState
.get(DaggerGraphState::cardSdkConfigRepository)
.setAccessCodeRequestPolicy(isBiometricsRequestPolicy = false)
}
.doOnFailure { error ->
Timber.e(error, "Unable to delete saved access codes")

View file

@ -11,6 +11,7 @@ import androidx.activity.OnBackPressedCallback
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
import com.otaliastudios.cameraview.CameraView
import com.tangem.core.navigation.NavigationAction
@ -38,10 +39,19 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (!permissionIsGranted()) requestPermission()
scannerView = ZXingScannerView(activity)
scannerView = ZXingScannerView(activity).apply {
setFormats(listOf(BarcodeFormat.QR_CODE))
}
return scannerView
}
override fun onResume() {
super.onResume()
scannerView?.setResultHandler(this)
scannerView?.startCamera()
}
override fun onPause() {
super.onPause()
scannerView?.stopCamera()
@ -52,12 +62,6 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
setFitSystemWindows(fit = false)
}
override fun onResume() {
super.onResume()
scannerView?.setResultHandler(this)
scannerView?.startCamera()
}
override fun handleResult(result: Result) {
store.dispatch(NavigationAction.PopBackTo())
setFitSystemWindows(fit = false)

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.home.redux
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.entities.IndeterminateProgressButton

View file

@ -5,11 +5,11 @@ import com.tangem.common.doOnResult
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.*
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.events.Shop
@ -20,13 +20,16 @@ import com.tangem.tap.common.extensions.eraseContext
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.features.home.BELARUS_COUNTRY_CODE
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.home.redux.HomeMiddleware.BUY_WALLET_URL
import com.tangem.tap.features.send.redux.states.ButtonState
import com.tangem.tap.features.signin.redux.SignInAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
@ -74,11 +77,11 @@ private fun handleHomeAction(action: Action) {
private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
delay(timeMillis = 200)
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
)
ScanCardProcessor.scan(
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = analyticsEvent,
onProgressStateChange = { showProgress ->
if (showProgress) {

View file

@ -11,12 +11,12 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.tap.*
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.extensions.removeContext
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

View file

@ -9,6 +9,7 @@ import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
@ -19,7 +20,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.domain.userWalletList.isLockedSync
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE

View file

@ -17,15 +17,12 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.ShareElement
import com.tangem.core.ui.fragments.setStatusBarColor
import com.tangem.datasource.asset.AssetReader
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.TwinsCardWidget
@ -35,7 +32,6 @@ import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletM
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding

View file

@ -14,6 +14,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
import com.tangem.operations.backup.BackupService
@ -30,10 +31,9 @@ import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.wallet.models.toCurrencies
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
object OnboardingWalletMiddleware {
@ -44,8 +44,8 @@ private val onboardingWalletMiddleware: Middleware<AppState> = { dispatch, state
{ next ->
{ action ->
when (action) {
is OnboardingWallet2Action -> handleWallet2Action(action, state)
else -> handleWalletAction(action, state, dispatch)
is OnboardingWallet2Action -> handleWallet2Action(action)
else -> handleWalletAction(action)
}
next(action)
}
@ -53,7 +53,7 @@ private val onboardingWalletMiddleware: Middleware<AppState> = { dispatch, state
}
@Suppress("LongMethod", "ComplexMethod")
private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch: DispatchFunction) {
private fun handleWalletAction(action: Action) {
if (action !is OnboardingWalletAction) return
val globalState = store.state.globalState
@ -179,7 +179,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
}
@Suppress("LongMethod", "ComplexMethod")
private fun handleWallet2Action(action: OnboardingWallet2Action, state: () -> AppState?) {
private fun handleWallet2Action(action: OnboardingWallet2Action) {
val globalState = store.state.globalState
val onboardingManager = globalState.onboardingState.onboardingManager
@ -330,7 +330,9 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
is BackupAction.AddBackupCard -> {
backupService.addBackupCard { result ->
backupService.skipCompatibilityChecks = false
tangemSdk.config.filter.cardIdFilter = null
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk
.config.filter.cardIdFilter = null
when (result) {
is CompletionResult.Success -> {
store.dispatchOnMain(BackupAction.AddBackupCard.Success)

View file

@ -9,6 +9,7 @@ import com.tangem.common.flatMap
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -18,10 +19,10 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.isLockable
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch
import org.rekotlin.Middleware
import timber.log.Timber
@ -110,9 +111,10 @@ internal class SaveWalletMiddleware {
// Enable saving access codes only if this is the first time user save the wallet
if (isFirstSavedWallet) {
preferencesStorage.shouldSaveAccessCodes = true
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = userWallet.hasAccessCode,
)
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository)
.setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = userWallet.hasAccessCode,
)
}
val savedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {

View file

@ -4,6 +4,7 @@ import com.tangem.Message
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
import com.tangem.tap.common.redux.StateDialog
@ -114,7 +115,7 @@ sealed class FeeAction : SendScreenAction {
object RequestFee : FeeAction()
sealed class FeeCalculation : FeeAction() {
data class SetFeeResult(val fee: List<Amount>) : FeeCalculation()
data class SetFeeResult(val fee: TransactionFee) : FeeCalculation()
object ClearResult : FeeCalculation()
}

View file

@ -67,7 +67,7 @@ class AmountMiddleware {
}
val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend(inputCrypto))
val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee)
val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee?.amount)
val amountFieldErrors = filterErrorsForAmountField(transactionErrors)
if (amountFieldErrors.isEmpty()) {
dispatch(AmountAction.SetAmountError(null))

View file

@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.redux.AppState
@ -53,15 +54,18 @@ class RequestFeeMiddleware {
val result = feeResult.data
// val result = FeeMock.getFee(walletManager.wallet.blockchain)
dispatch(FeeAction.FeeCalculation.SetFeeResult(result))
if (result.size == 1) {
val fee = result[0].value ?: BigDecimal.ZERO
if (fee.isZero()) {
dispatch(FeeAction.ChangeLayoutVisibility(main = false))
} else {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = false))
when (result) {
is TransactionFee.Single -> {
val fee = result.normal.amount.value ?: BigDecimal.ZERO
if (fee.isZero()) {
dispatch(FeeAction.ChangeLayoutVisibility(main = false))
} else {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = false))
}
}
is TransactionFee.Choosable -> {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = true))
}
} else {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = true))
}
}
is Result.Failure -> {

View file

@ -7,13 +7,14 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder.XrpTransactionExtras
import com.tangem.core.navigation.NavigationAction
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.models.scan.CardDTO
@ -38,6 +39,7 @@ import com.tangem.tap.features.send.redux.FeeAction.RequestFee
import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@ -97,11 +99,11 @@ private fun verifyAndSendTransaction(
val card = appState.globalState.scanResponse?.card ?: return
val destinationAddress = sendState.addressState.destinationWalletAddress ?: return
val typedAmount = sendState.amountState.amountToExtract ?: return
val feeAmount = sendState.feeState.currentFee ?: return
val fee = sendState.feeState.currentFee ?: return
val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend())
val transactionErrors = walletManager.validateTransaction(amountToSend, feeAmount)
val transactionErrors = walletManager.validateTransaction(amountToSend, fee.amount)
when {
transactionErrors.contains(TransactionError.TezosSendAll) -> {
val reduceAmount = walletManager.wallet.blockchain.minimalAmount()
@ -116,7 +118,7 @@ private fun verifyAndSendTransaction(
action = action,
walletManager = walletManager,
amountToSend = amountToSend,
feeAmount = feeAmount,
fee = fee,
feeType = sendState.feeState.selectedFeeType,
destinationAddress = destinationAddress,
transactionExtras = sendState.transactionExtrasState,
@ -135,7 +137,7 @@ private fun verifyAndSendTransaction(
action = action,
walletManager = walletManager,
amountToSend = amountToSend,
feeAmount = feeAmount,
fee = fee,
feeType = sendState.feeState.selectedFeeType,
destinationAddress = destinationAddress,
transactionExtras = sendState.transactionExtrasState,
@ -153,7 +155,7 @@ private fun sendTransaction(
action: SendActionUi.SendAmountToRecipient,
walletManager: WalletManager,
amountToSend: Amount,
feeAmount: Amount,
fee: Fee,
feeType: FeeType,
destinationAddress: String,
transactionExtras: TransactionExtrasState,
@ -163,7 +165,7 @@ private fun sendTransaction(
dispatch: (Action) -> Unit,
) {
dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS))
var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress)
var txData = walletManager.createTransaction(amountToSend, fee, destinationAddress)
transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) }
transactionExtras.binanceMemo?.memo?.let { txData = txData.copy(extras = BinanceTransactionExtras(it.toString())) }
@ -181,7 +183,7 @@ private fun sendTransaction(
updateFeedbackManagerInfo(
walletManager = walletManager,
amountToSend = amountToSend,
feeAmount = feeAmount,
feeAmount = fee.amount,
destinationAddress = destinationAddress,
)
dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error = error))
@ -200,6 +202,7 @@ private fun sendTransaction(
return@launch
}
val tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk
val linkedTerminalState = tangemSdk.config.linkedTerminal
if (card.isStart2Coin) {
tangemSdk.config.linkedTerminal = false
@ -276,7 +279,7 @@ private fun sendTransaction(
updateFeedbackManagerInfo(
walletManager = walletManager,
amountToSend = amountToSend,
feeAmount = feeAmount,
feeAmount = fee.amount,
destinationAddress = destinationAddress,
)
val error = sendResult.error as? BlockchainSdkError ?: return@withMainContext

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.send.redux.reducers
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.tap.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.FeeActionUi
import com.tangem.tap.features.send.redux.SendScreenAction
@ -13,6 +14,7 @@ import com.tangem.tap.features.wallet.redux.ProgressState
[REDACTED_AUTHOR]
*/
class FeeReducer : SendInternalReducer {
override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) {
is FeeActionUi -> handleUiAction(action, sendState, sendState.feeState)
is FeeAction -> handleAction(action, sendState, sendState.feeState)
@ -25,7 +27,9 @@ class FeeReducer : SendInternalReducer {
state.copy(controlsLayoutIsVisible = !state.controlsLayoutIsVisible)
}
is FeeActionUi.ChangeSelectedFee -> {
val currentFee = createValueOfFeeAmount(action.feeType, state.feeList)
val currentFee = state.fees?.let {
createValueOfFeeAmount(action.feeType, it)
}
state.copy(
selectedFeeType = action.feeType,
currentFee = currentFee,
@ -50,34 +54,36 @@ class FeeReducer : SendInternalReducer {
)
}
is FeeAction.FeeCalculation.SetFeeResult -> {
val fees = action.fee
if (fees.size == 1) {
val feeType = FeeType.SINGLE
val currentFee = createValueOfFeeAmount(feeType, fees)
when (val fees = action.fee) {
is TransactionFee.Single -> {
val feeType = FeeType.SINGLE
val currentFee = createValueOfFeeAmount(feeType, fees)
state.copy(
selectedFeeType = feeType,
feeList = fees,
currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState),
)
} else {
val feeType = getCurrentFeeType(state)
val currentFee = createValueOfFeeAmount(feeType, fees)
state.copy(
selectedFeeType = feeType,
fees = fees,
currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState),
)
}
is TransactionFee.Choosable -> {
val feeType = getCurrentFeeType(state)
val currentFee = createValueOfFeeAmount(feeType, fees)
state.copy(
selectedFeeType = feeType,
feeList = fees,
currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState),
)
state.copy(
selectedFeeType = feeType,
fees = fees,
currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState),
)
}
}.copy(
progressState = ProgressState.Done,
)
}
FeeAction.FeeCalculation.ClearResult -> {
state.copy(
feeList = null,
fees = null,
currentFee = null,
progressState = ProgressState.Done,
)
@ -87,17 +93,18 @@ class FeeReducer : SendInternalReducer {
return updateLastState(sendState.copy(feeState = result), result)
}
private fun createValueOfFeeAmount(feeType: FeeType, list: List<Amount>?): Amount? {
if (list == null || list.isEmpty()) return null
return if (list.size == 1) {
list[0]
} else {
when (feeType) {
FeeType.SINGLE -> list[1]
FeeType.LOW -> list[0]
FeeType.NORMAL -> list[1]
FeeType.PRIORITY -> list[2]
private fun createValueOfFeeAmount(feeType: FeeType, transactionFee: TransactionFee): Fee {
return when (transactionFee) {
is TransactionFee.Single -> {
transactionFee.normal
}
is TransactionFee.Choosable -> {
when (feeType) {
FeeType.SINGLE -> transactionFee.normal
FeeType.LOW -> transactionFee.minimum
FeeType.NORMAL -> transactionFee.normal
FeeType.PRIORITY -> transactionFee.priority
}
}
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.send.redux.states
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.features.wallet.redux.ProgressState
import java.math.BigDecimal
@ -23,8 +24,8 @@ fun FeeType.convertToAnalyticsFeeType(): AnalyticsParam.FeeType {
data class FeeState(
val selectedFeeType: FeeType = FeeType.NORMAL,
val feeList: List<Amount>? = null,
val currentFee: Amount? = null,
val fees: TransactionFee? = null,
val currentFee: Fee? = null,
val feeIsIncluded: Boolean = false,
val feeIsApproximate: Boolean = false,
val mainLayoutIsVisible: Boolean = false,
@ -38,5 +39,5 @@ data class FeeState(
fun isReady(): Boolean = currentFee != null
fun getCurrentFeeValue(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO
fun getCurrentFeeValue(): BigDecimal = currentFee?.amount?.value ?: BigDecimal.ZERO
}

View file

@ -11,6 +11,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.fragment.app.Fragment
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.transition.TransitionInflater
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.tokens.impl.presentation.ui.TokensListScreen
import com.tangem.tap.features.tokens.impl.presentation.viewmodels.TokensListViewModel
@ -40,6 +41,10 @@ internal class TokensListFragment : Fragment() {
}
TangemTheme {
val statusBarColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(color = statusBarColor)
}
TokensListScreen(
modifier = Modifier.systemBarsPadding(),
stateHolder = viewModel.uiState,

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.redux
import android.content.Context
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.models.UserWallet

View file

@ -1,11 +1,11 @@
package com.tangem.tap.features.wallet.redux
import android.graphics.Bitmap
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.userwallets.Artwork
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.toggleWidget.WidgetState
@ -118,19 +118,4 @@ enum class ErrorType {
sealed class WalletMainButton(enabled: Boolean) : Button(enabled) {
class SendButton(enabled: Boolean) : WalletMainButton(enabled)
}
data class Artwork(
val artworkId: String,
val artwork: Bitmap? = null,
) {
companion object {
const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png"
const val SERGIO_CARD_URL = "https://app.tangem.com/cards/card_tg059.png"
const val MARTA_CARD_URL = "https://app.tangem.com/cards/card_tg083.png"
const val SERGIO_CARD_ID = "BC01"
const val MARTA_CARD_ID = "BC02"
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
}
}

View file

@ -15,10 +15,10 @@ import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager
@ -102,7 +102,8 @@ class MultiWalletMiddleware {
}
private fun scanAndUpdateCard(selectedUserWallet: UserWallet) = scope.launch(Dispatchers.Default) {
ScanCardProcessor.scan(selectedUserWallet.cardId, allowsRequestAccessCodeFromRepository = true)
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor)
.scan(cardId = selectedUserWallet.cardId, allowsRequestAccessCodeFromRepository = true)
.flatMap { scanResponse ->
userWalletsListManager.update(
userWalletId = selectedUserWallet.walletId,

View file

@ -72,7 +72,7 @@ class TradeCryptoMiddleware {
}
scope.launch {
exchangeManager.buyErc20TestnetTokens(
buyErc20TestnetTokens(
card = card,
walletManager = walletManager,
token = currency.token,

View file

@ -12,6 +12,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.userwallets.GetCardImageUseCase
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
@ -23,7 +24,6 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.userWalletList.GetCardImageUseCase
import com.tangem.tap.domain.userWalletList.lockIfLockable
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction

View file

@ -1,14 +1,19 @@
package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.address.AddressType
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.userwallets.Artwork
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.userWalletsListManager
import org.rekotlin.Action
@ -114,7 +119,7 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
if (selectedUserWallet == action.userWalletId) {
newState = newState.copy(
cardImage = Artwork(artworkId = action.url, artwork = newState.cardImage?.artwork),
cardImage = Artwork(artworkId = action.url),
)
}
}
@ -151,7 +156,7 @@ fun Wallet.createAddressesData(): List<WalletDataModel.AddressData> {
getShareUri(it.value),
getExploreUrl(it.value),
)
if (it.type == blockchain.defaultAddressType()) {
if (it.type == AddressType.Default) {
listOfAddressData.add(0, addressData)
} else {
listOfAddressData.add(addressData)

View file

@ -4,7 +4,7 @@ import androidx.annotation.IdRes
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.store
import com.tangem.wallet.R
@ -102,8 +102,8 @@ class BalanceWidget(
groupBaseCurrency.show()
tvCurrency.text = tokenWalletData?.currency?.currencySymbol
tvBaseCurrency.text = data.currency.currencyName
tvAmount.text = if (showAmount) tokenWalletData?.getFormattedAmount() else ""
tvBaseAmount.text = if (showAmount) data.getFormattedAmount() else ""
tvAmount.text = if (showAmount) tokenWalletData?.getFormattedCryptoAmount() else ""
tvBaseAmount.text = if (showAmount) data.getFormattedCryptoAmount() else ""
if (showAmount) {
tvFiatAmount.show()
tvFiatAmount.text = tokenWalletData?.getFormattedFiatAmount(store.state.globalState.appCurrency)
@ -113,7 +113,7 @@ class BalanceWidget(
private fun showBalanceWithoutToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) {
groupBaseCurrency.hide()
tvCurrency.text = data.currency.currencyName
tvAmount.text = if (showAmount) data.getFormattedAmount() else ""
tvAmount.text = if (showAmount) data.getFormattedCryptoAmount() else ""
if (showAmount) {
tvFiatAmount.show()
tvFiatAmount.text = data.getFormattedFiatAmount(store.state.globalState.appCurrency)

View file

@ -1,44 +1,38 @@
package com.tangem.tap.features.wallet.ui
import android.view.View
import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressType
import com.tangem.blockchain.blockchains.cardano.CardanoAddressType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType
import com.tangem.wallet.R
object MultipleAddressUiHelper {
fun typeToId(type: AddressType): Int {
return when (type) {
is BitcoinAddressType.Legacy -> R.id.chip_legacy
is BitcoinAddressType.Segwit -> R.id.chip_default
is CardanoAddressType.Byron -> R.id.chip_legacy
is CardanoAddressType.Shelley -> R.id.chip_default
else -> View.NO_ID
private val blockchainsSupportingSplit = listOf(
Blockchain.Bitcoin,
Blockchain.BitcoinTestnet,
Blockchain.Litecoin,
Blockchain.BitcoinCash,
Blockchain.CardanoShelley,
)
fun typeToId(type: AddressType, blockchain: Blockchain): Int {
return if (blockchain in blockchainsSupportingSplit) {
if (type == AddressType.Legacy) {
R.id.chip_legacy
} else {
R.id.chip_default
}
} else {
View.NO_ID
}
}
fun idToType(id: Int, blockchain: Blockchain?): AddressType? {
return when (id) {
R.id.chip_default -> {
when (blockchain) {
Blockchain.Bitcoin,
Blockchain.BitcoinTestnet,
Blockchain.Litecoin,
Blockchain.BitcoinCash,
-> BitcoinAddressType.Segwit
Blockchain.CardanoShelley -> CardanoAddressType.Shelley
else -> null
}
}
R.id.chip_legacy -> {
when (blockchain) {
Blockchain.Bitcoin,
Blockchain.BitcoinTestnet,
Blockchain.Litecoin,
Blockchain.BitcoinCash,
-> BitcoinAddressType.Legacy
Blockchain.CardanoShelley -> CardanoAddressType.Byron
fun idToType(id: Int, blockchain: Blockchain): AddressType? {
return when (blockchain) {
in blockchainsSupportingSplit -> {
when (id) {
R.id.chip_default -> AddressType.Default
R.id.chip_legacy -> AddressType.Legacy
else -> null
}
}

View file

@ -59,7 +59,7 @@ import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.test.TestWallet
import com.tangem.tap.features.wallet.ui.utils.assembleWarnings
import com.tangem.tap.features.wallet.ui.utils.getAvailableActions
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell
@ -390,7 +390,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
chipGroupAddressType.show()
chipGroupAddressType.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(selectedAddress.type)
val checkedId = MultipleAddressUiHelper.typeToId(selectedAddress.type, currency.blockchain)
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
chipGroupAddressType.setOnCheckedChangeListener { _, checkedId ->
@ -425,7 +425,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
lBalance.root.show()
lBalance.groupBalance.show()
lBalance.tvError.hide()
lBalance.tvAmount.text = walletData.getFormattedAmount()
lBalance.tvAmount.text = walletData.getFormattedCryptoAmount()
lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency)
lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading)
}
@ -437,7 +437,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
lBalance.root.show()
lBalance.groupBalance.show()
lBalance.tvError.hide()
lBalance.tvAmount.text = walletData.getFormattedAmount()
lBalance.tvAmount.text = walletData.getFormattedCryptoAmount()
lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency)
when (status) {
is WalletDataModel.VerifiedOnline,

View file

@ -15,7 +15,7 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatRate
import com.tangem.tap.store
@ -85,7 +85,7 @@ class WalletAdapter : ListAdapter<WalletDataModel, WalletAdapter.WalletsViewHold
lContent.tvCurrency.text = wallet.currency.currencyName
lContent.tvAmountFiat.text = wallet.getFormattedFiatAmount(fiatCurrency)
lContent.tvAmount.text = wallet.getFormattedAmount()
lContent.tvAmount.text = wallet.getFormattedCryptoAmount()
lContent.tvStatus.isVisible = statusMessage != null
lContent.tvStatus.text = statusMessage

View file

@ -8,7 +8,7 @@ import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFiatRateString
import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedCryptoCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
@ -38,8 +38,8 @@ internal fun WalletDataModel.hasPendingTransactions(): Boolean {
return status.pendingTransactions.isEmpty()
}
internal fun WalletDataModel.getFormattedAmount(): String {
return status.amount.toFormattedCurrencyString(
internal fun WalletDataModel.getFormattedCryptoAmount(): String {
return status.amount.toFormattedCryptoCurrencyString(
decimals = currency.decimals,
currency = currency.currencySymbol,
)

View file

@ -190,7 +190,10 @@ class SingleWalletView : WalletView() {
(binding.lAddress.root as? ViewGroup)?.beginDelayedTransition()
chipGroupAddressType.show()
chipGroupAddressType.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(primaryWallet.walletAddresses.selectedAddress.type)
val checkedId = MultipleAddressUiHelper.typeToId(
primaryWallet.walletAddresses.selectedAddress.type,
primaryWallet.currency.blockchain,
)
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
chipGroupAddressType.setOnCheckedChangeListener { group, checkedId ->

View file

@ -6,6 +6,8 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.*
@ -18,10 +20,8 @@ import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.domain.userWalletList.unlockIfLockable
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.firstOrNull
@ -126,14 +126,16 @@ internal class WalletSelectorMiddleware {
private fun addWallet() = scope.launch {
Analytics.send(MyWallets.Button.ScanNewCard())
val prevUseBiometricsForAccessCode = tangemSdkManager.useBiometricsForAccessCode()
val cardSdkConfigRepository = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository)
val prevUseBiometricsForAccessCode = cardSdkConfigRepository.isBiometricsRequestPolicy()
// Update access code policy for access code saving when a card was scanned
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
cardSdkConfigRepository.setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
)
ScanCardProcessor.scan(
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets),
onWalletNotCreated = {
// No need to rollback policy, continue with the policy set before the card scan
@ -147,14 +149,14 @@ internal class WalletSelectorMiddleware {
saveUserWalletAndPopBackToWalletScreen(scanResponse)
.doOnFailure { error ->
// Rollback policy if card saving was failed
tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
Timber.e(error, "Unable to save user wallet")
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
}
},
onFailure = { error ->
// Rollback policy if card scanning was failed
tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
Timber.e(error, "Unable to scan card")
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
},
@ -208,7 +210,7 @@ internal class WalletSelectorMiddleware {
private suspend fun unlockUserWalletWithScannedCard(userWallet: UserWallet): CompletionResult<Unit> {
Analytics.send(MyWallets.Button.WalletUnlockTapped())
tangemSdkManager.changeDisplayedCardIdNumbersCount(userWallet.scanResponse)
return ScanCardProcessor.scan()
return store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan()
.map { scanResponse ->
val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
if (scannedUserWalletId == userWallet.walletId) {

View file

@ -8,6 +8,7 @@ import com.tangem.common.flatMap
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
@ -15,11 +16,10 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.domain.userWalletList.unlockIfLockable
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
import com.tangem.tap.features.signin.redux.SignInAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch
import org.rekotlin.Middleware
import timber.log.Timber
@ -100,10 +100,10 @@ internal class WelcomeMiddleware {
}
private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) {
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
)
ScanCardProcessor.scan(
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.SignIn),
onSuccess = { scanResponse ->
scope.launch { onCardScanned(scanResponse) }

View file

@ -13,8 +13,8 @@ import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import java.math.BigDecimal
/**
@ -72,7 +72,7 @@ class CurrencyExchangeManager(
return when (action) {
Action.Buy -> buyService
Action.Sell -> sellService
} as ExchangeUrlBuilder
}
}
enum class Action { Buy, Sell }
@ -86,31 +86,26 @@ class CurrencyExchangeManager(
}
}
suspend fun CurrencyExchangeManager.buyErc20TestnetTokens(
card: CardDTO,
walletManager: EthereumWalletManager,
token: Token,
) {
suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletManager, token: Token) {
walletManager.safeUpdate()
val amountToSend = Amount(walletManager.wallet.blockchain)
val destinationAddress = token.contractAddress
val feeResult =
walletManager.getFee(
amountToSend,
destinationAddress,
) as? Result.Success ?: return
val fee = feeResult.data[0]
val feeResult = walletManager.getFee(
amountToSend,
destinationAddress,
) as? Result.Success ?: return
val fee = feeResult.data.minimum
val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
if (coinValue < fee.value) return
if (coinValue < fee.amount.value) return
val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress)
val signer = TangemSigner(
card = card,
tangemSdk = tangemSdk,
tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk,
initialMessage = Message(),
) { signResponse ->
store.dispatch(

View file

@ -1,6 +1,5 @@
package com.tangem.tap.proxy
import com.tangem.TangemSdk
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.NavigationStateHolder
import com.tangem.domain.models.scan.CardDTO
@ -41,7 +40,6 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationState
var userTokensRepository: UserTokensRepository? = null
var mainStore: Store<AppState>? = null
var tangemSdkManager: TangemSdkManager? = null
var tangemSdk: TangemSdk? = null
var walletStoresManager: WalletStoresManager? = null
var appFiatCurrency: FiatCurrency = FiatCurrency.Default

View file

@ -6,12 +6,15 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.blockchains.optimism.OptimismWalletManager
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.extensions.isNetworkError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.lib.crypto.TransactionManager
@ -22,7 +25,6 @@ import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.Basic.TransactionSent.MemoType
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.tangemSdk
import java.math.BigDecimal
import java.math.BigInteger
import java.math.MathContext
@ -32,6 +34,7 @@ import java.math.RoundingMode
class TransactionManagerImpl(
private val appStateHolder: AppStateHolder,
private val analytics: AnalyticsEventHandler,
private val cardSdkConfigRepository: CardSdkConfigRepository,
) : TransactionManager {
override suspend fun sendApproveTransaction(
@ -112,7 +115,7 @@ class TransactionManagerImpl(
): SendTxResult {
val txData = walletManager.createTransaction(
amount = amount,
fee = Amount(value = feeAmount, blockchain = blockchain),
fee = Fee.Common(Amount(value = feeAmount, blockchain = blockchain)),
destination = destinationAddress,
).copy(hash = dataToSign, extras = createExtras(walletManager, gasLimit, dataToSign))
@ -214,24 +217,37 @@ class TransactionManagerImpl(
return when (fee) {
is Result.Success -> {
// for not EVM blockchains set gasLimit ZERO for now
val firstFee = fee.data.firstOrNull() ?: error("no fee found")
val minFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = firstFee),
)
val normalFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(fee.data.getOrNull(index = 1) ?: firstFee),
)
val priorityFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(fee.data.getOrNull(index = 2) ?: firstFee),
)
ProxyFees(
minFee = minFee,
normalFee = normalFee,
priorityFee = priorityFee,
)
when (fee.data) {
is TransactionFee.Single -> {
val fee = (fee.data as TransactionFee.Single).normal
val singleFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = fee.amount),
)
ProxyFees(
minFee = singleFee,
normalFee = singleFee,
priorityFee = singleFee,
)
}
is TransactionFee.Choosable -> {
val choosableFee = fee.data as TransactionFee.Choosable
ProxyFees(
minFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = choosableFee.minimum.amount),
),
normalFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = choosableFee.normal.amount),
),
priorityFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = choosableFee.priority.amount),
),
)
}
}
}
is Result.Failure -> {
error(fee.error.message ?: fee.error.customMessage)
@ -280,19 +296,21 @@ class TransactionManagerImpl(
}
return when (fee) {
is Result.Success -> {
val minFee = fee.data.firstOrNull() ?: error("no fee found")
val choosableFee = fee.data
val minProxyFee = ProxyFee(
gasLimit = walletManager.gasLimit ?: BigInteger.ZERO,
fee = convertToProxyAmount(minFee),
gasLimit = (choosableFee.minimum as Fee.Ethereum).gasLimit,
fee = convertToProxyAmount(amount = choosableFee.minimum.amount),
)
val normalProxyFee = ProxyFee(
gasLimit = walletManager.gasLimit ?: BigInteger.ZERO,
fee = convertToProxyAmount(fee.data.getOrNull(index = 1) ?: minFee),
gasLimit = (choosableFee.normal as Fee.Ethereum).gasLimit,
fee = convertToProxyAmount(amount = choosableFee.normal.amount),
)
val priorityProxyFee = ProxyFee(
gasLimit = walletManager.gasLimit ?: BigInteger.ZERO,
fee = convertToProxyAmount(fee.data.lastOrNull() ?: minFee),
gasLimit = (choosableFee.priority as Fee.Ethereum).gasLimit,
fee = convertToProxyAmount(amount = choosableFee.priority.amount),
)
ProxyFees(
minFee = minProxyFee,
normalFee = normalProxyFee,
@ -378,7 +396,7 @@ class TransactionManagerImpl(
val actualCard = requireNotNull(appStateHolder.getActualCard()) { "no card found" }
return TangemSigner(
card = actualCard,
tangemSdk = tangemSdk,
tangemSdk = cardSdkConfigRepository.sdk,
initialMessage = Message(),
) { signResponse ->
appStateHolder.mainStore?.dispatch(

View file

@ -10,6 +10,7 @@ import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toCoinId
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.lib.crypto.UserWalletManager
import com.tangem.lib.crypto.models.Currency
import com.tangem.lib.crypto.models.Currency.NativeToken
@ -17,7 +18,6 @@ import com.tangem.lib.crypto.models.Currency.NonNativeToken
import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.ProxyFiatCurrency
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.userWalletsListManager
import com.tangem.tap.walletCurrenciesManager

View file

@ -2,6 +2,7 @@ package com.tangem.tap.proxy.di
import androidx.compose.ui.text.intl.Locale
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider
@ -43,8 +44,13 @@ class ProxyModule {
fun provideTransactionManager(
appStateHolder: AppStateHolder,
analytics: AnalyticsEventHandler,
cardSdkConfigRepository: CardSdkConfigRepository,
): TransactionManager {
return TransactionManagerImpl(appStateHolder, analytics)
return TransactionManagerImpl(
appStateHolder = appStateHolder,
analytics = analytics,
cardSdkConfigRepository = cardSdkConfigRepository,
)
}
@Provides

View file

@ -1,6 +1,7 @@
package com.tangem.tap.proxy.redux
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
@ -15,5 +16,6 @@ sealed interface DaggerGraphAction : Action {
val walletRouter: WalletRouter,
val walletConnectInteractor: WalletConnectInteractor,
val tokenDetailsRouter: TokenDetailsRouter,
val cardSdkConfigRepository: CardSdkConfigRepository,
) : DaggerGraphAction
}

View file

@ -18,6 +18,7 @@ object DaggerGraphReducer {
walletRouter = action.walletRouter,
walletConnectInteractor = action.walletConnectInteractor,
tokenDetailsRouter = action.tokenDetailsRouter,
cardSdkConfigRepository = action.cardSdkConfigRepository,
)
}
}

View file

@ -2,7 +2,9 @@ package com.tangem.tap.proxy.redux
import com.tangem.datasource.asset.AssetReader
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
@ -27,6 +29,8 @@ data class DaggerGraphState(
val walletConnectInteractor: WalletConnectInteractor? = null,
val tokenDetailsFeatureToggles: TokenDetailsFeatureToggles? = null,
val tokenDetailsRouter: TokenDetailsRouter? = null,
val scanCardProcessor: ScanCardProcessor? = null,
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
) : StateType {
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {

Binary file not shown.

View file

@ -13,5 +13,7 @@ dependencies {
kapt(deps.hilt.kapt)
/** Core shouldn't depends on core, but in case with utils and logging its necessary */
implementation(project(":core:utils"))
implementation(projects.core.utils)
implementation(projects.core.analytics.models)
}

1
core/analytics/models/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,4 @@
plugins {
alias(deps.plugins.kotlin.jvm)
id("configuration")
}

View file

@ -1,4 +1,4 @@
package com.tangem.core.analytics
package com.tangem.core.analytics.models
/**
[REDACTED_AUTHOR]

View file

@ -1,18 +1,9 @@
package com.tangem.core.analytics
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.api.AnalyticsFilterHolder
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsHandlerHolder
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.api.ParamsInterceptorHolder
import com.tangem.core.analytics.api.*
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import java.util.concurrent.Executors
/**

View file

@ -1,6 +1,6 @@
package com.tangem.core.analytics.api
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.core.analytics.api
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.core.analytics.api
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]

View file

@ -32,7 +32,7 @@ dependencies {
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.okHttp)
implementation(deps.okHttp.logging)
implementation(deps.okHttp.prettyLogging)
implementation(deps.retrofit)
implementation(deps.retrofit.moshi)
implementation(deps.reactive.network)

View file

@ -1,8 +1,10 @@
package com.tangem.datasource.api.common
import android.util.Log
import com.ihsanbal.logging.Level
import com.ihsanbal.logging.LoggingInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
@ -22,7 +24,7 @@ fun createRetrofitInstance(
}
interceptors.forEach { okHttpBuilder.addInterceptor(it) }
if (logEnabled) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor())
if (logEnabled) okHttpBuilder.addInterceptor(createNetworkLoggingInterceptor())
return Retrofit.Builder()
.baseUrl(baseUrl)
@ -31,6 +33,9 @@ fun createRetrofitInstance(
.build()
}
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
fun createNetworkLoggingInterceptor(): Interceptor {
return LoggingInterceptor.Builder()
.setLevel(Level.BODY)
.log(Log.VERBOSE)
.build()
}

View file

@ -0,0 +1,20 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.cache.CacheKeysStore
import com.tangem.datasource.local.cache.RuntimeCacheKeysStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object CacheKeysStoreModule {
@Provides
@Singleton
fun provideCacheKeysStore(): CacheKeysStore {
return RuntimeCacheKeysStore()
}
}

View file

@ -63,9 +63,7 @@ class NetworkModule {
@PromotionOneInch
fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi {
val okClient = OkHttpClient.Builder()
.addHeaders(
AuthenticationHeader(authProvider),
)
.addHeaders(AuthenticationHeader(authProvider))
.allowLogging()
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)

View file

@ -0,0 +1,14 @@
package com.tangem.datasource.local.cache
import com.tangem.datasource.local.cache.model.CacheKey
interface CacheKeysStore {
fun get(id: String): CacheKey?
fun addOrReplace(key: CacheKey)
fun remove(id: String)
fun clear()
}

Some files were not shown because too many files have changed in this diff Show more