Updated on 2026-08-14

This commit is contained in:
Tangem 2022-04-06 19:03:55 +03:00
parent 608121ab50
commit 82fd854852
49 changed files with 980 additions and 117 deletions

View file

@ -3,12 +3,12 @@ package com.tangem.tap
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemSdk
import com.tangem.domain.common.FeatureCoroutineExceptionHandler
import com.tangem.operations.backup.BackupService
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tap.common.DialogManager
@ -27,12 +27,9 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.shop.redux.ShopAction
import com.tangem.wallet.R
import com.tangem.wallet.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.ref.WeakReference
import kotlin.coroutines.CoroutineContext
@ -42,23 +39,13 @@ lateinit var backupService: BackupService
var notificationsHandler: NotificationsHandler? = null
private val coroutineContext: CoroutineContext
get() = Job() + Dispatchers.IO + initCoroutineExceptionHandler()
get() = Job() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("scope")
val scope = CoroutineScope(coroutineContext)
private val mainCoroutineContext: CoroutineContext
get() = Job() + Dispatchers.Main
get() = Job() + Dispatchers.Main + FeatureCoroutineExceptionHandler.create("mainScope")
val mainScope = CoroutineScope(mainCoroutineContext)
private fun initCoroutineExceptionHandler(): CoroutineExceptionHandler {
return CoroutineExceptionHandler { _, throwable ->
val sw = StringWriter()
throwable.printStackTrace(PrintWriter(sw))
val exceptionAsString: String = sw.toString()
Log.e("Coroutine", exceptionAsString)
throw throwable
}
}
class MainActivity : AppCompatActivity(), SnackbarHandler {
private var snackbar: Snackbar? = null

View file

@ -7,6 +7,7 @@ import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
import com.tangem.Log
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.network.common.MoshiConverter
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
import com.tangem.tap.common.images.PicassoHelper
import com.tangem.tap.common.redux.AppState
@ -24,7 +25,6 @@ import com.tangem.tap.features.feedback.AdditionalEmailInfo
import com.tangem.tap.features.feedback.FeedbackManager
import com.tangem.tap.features.feedback.TangemLogCollector
import com.tangem.tap.network.NetworkConnectivity
import com.tangem.tap.network.createMoshi
import com.tangem.tap.persistence.PreferencesStorage
import com.tangem.wallet.BuildConfig
import org.rekotlin.Store
@ -72,7 +72,7 @@ class TapApplication : Application() {
}
private fun loadConfigs() {
val moshi = createMoshi()
val moshi = MoshiConverter.defaultMoshi()
val localLoader = FeaturesLocalLoader(this, moshi)
val remoteLoader = FeaturesRemoteLoader(moshi)
val configManager = ConfigManager(localLoader, remoteLoader)

View file

@ -0,0 +1,6 @@
package com.tangem.tap.common.extensions
/**
[REDACTED_AUTHOR]
*/
typealias ValueCallback<T> = (T) -> Unit

View file

@ -1,12 +0,0 @@
package com.tangem.tap.common.extensions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
[REDACTED_AUTHOR]
*/
suspend fun <T> withMainContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.Main, block)
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block)

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.extensions
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.NavigationAction

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.redux
import com.tangem.domain.store.DomainState
import com.tangem.domain.store.domainStore
import com.tangem.tap.common.redux.global.GlobalMiddleware
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.navigation.NavigationState
@ -49,6 +51,9 @@ data class AppState(
val shopState: ShopState = ShopState(),
) : StateType {
val featuresState: DomainState
get() = domainStore.state
companion object {
fun getMiddleware(): List<Middleware<AppState>> {
return listOf(

View file

@ -3,10 +3,10 @@ package com.tangem.tap.common.redux.global
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.ifNotNull
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.*
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager

View file

@ -3,11 +3,11 @@ package com.tangem.tap.domain
import com.tangem.blockchain.blockchains.solana.RentProvider
import com.tangem.blockchain.common.*
import com.tangem.common.services.Result
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.ThrottlerWithValues
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.currenciesRepository

View file

@ -9,17 +9,17 @@ import com.squareup.moshi.Types
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.card.FirmwareVersion
import com.tangem.network.common.MoshiConverter
import com.tangem.tap.common.extensions.appendIf
import com.tangem.tap.common.extensions.readJsonFileToString
import com.tangem.tap.domain.extensions.getCustomIconUrl
import com.tangem.tap.domain.extensions.setCustomIconUrl
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.network.createMoshi
import timber.log.Timber
class CurrenciesRepository(val context: Application) {
private val moshi = createMoshi()
private val moshi = MoshiConverter.defaultMoshi()
private val blockchainsAdapter: JsonAdapter<List<Blockchain>> = moshi.adapter(
Types.newParameterizedType(List::class.java, Blockchain::class.java)
)

View file

@ -10,11 +10,11 @@ import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.network.common.MoshiConverter
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.network.createMoshi
import com.tangem.tap.tangemSdkManager
class TwinCardsManager(
@ -107,7 +107,7 @@ class TwinCardsManager(
}
private fun getAdapter(): JsonAdapter<List<Issuer>> {
return createMoshi().adapter(
return MoshiConverter.defaultMoshi().adapter(
Types.newParameterizedType(List::class.java, Issuer::class.java)
)
}

View file

@ -5,14 +5,14 @@ import android.content.Context
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Types
import com.tangem.network.common.MoshiConverter
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
import com.tangem.tap.network.createMoshi
import com.trustwallet.walletconnect.models.WCPeerMeta
import com.trustwallet.walletconnect.models.session.WCSession
class WalletConnectRepository(val context: Application) {
private val moshi = createMoshi()
private val moshi = MoshiConverter.defaultMoshi()
private val walletConnectAdapter: JsonAdapter<List<SessionDao>> = moshi.adapter(
Types.newParameterizedType(List::class.java, SessionDao::class.java)
)

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.demo
import com.tangem.common.extensions.guard
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction

View file

@ -1,6 +1,8 @@
package com.tangem.tap.features.home.compose
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
@ -12,6 +14,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.tap.common.compose.SpacerS8
import com.tangem.wallet.R
@Composable
@ -45,7 +48,7 @@ fun HomeButtons(
maxLines = 1
)
}
Spacer(modifier = Modifier.size(8.dp))
SpacerS8()
Button(
modifier = Modifier
.weight(1f)

View file

@ -18,6 +18,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.tap.common.compose.SpacerS24
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.wallet.R
@ -108,7 +109,7 @@ fun StoriesScreen(
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Spacer(modifier = Modifier.size(24.dp))
SpacerS24()
StoriesProgressBar(
steps = steps,
currentStep = currentStep.value,

View file

@ -22,6 +22,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.compose.SpacerS16
import com.tangem.tap.common.compose.SpacerS24
import com.tangem.tap.common.extensions.compose.argb
import com.tangem.wallet.R
@ -53,11 +55,11 @@ fun StoriesGeneralContent(
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.size(16.dp))
SpacerS16()
SubtitleText(subtitleText, subtitleTextId)
Spacer(modifier = Modifier.size(25.dp))
SpacerS24()
if (imageSource != null) {
Image(

View file

@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.tap.common.compose.SpacerV4
@Composable
fun StoriesProgressBar(
@ -67,7 +68,7 @@ fun StoriesProgressBar(
) {}
}
if (index != steps) {
Spacer(modifier = Modifier.width(4.dp))
SpacerV4()
}
}
}

View file

@ -1,12 +1,12 @@
package com.tangem.tap.features.home.redux
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.AnalyticsParam
import com.tangem.tap.common.analytics.GetCardSourceParams
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.post
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
@ -50,7 +50,10 @@ private val homeMiddleware: Middleware<AppState> = { dispatch, state ->
postUi(700) { store.dispatch(HomeAction.ReadCard) }
}
}
is HomeAction.ReadCard -> handleReadCard()
is HomeAction.ReadCard -> {
handleReadCard()
// store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomTokens))
}
is HomeAction.GoToShop -> {
when (action.regionProvider.getRegion()?.toLowerCase()) {
"ru" -> store.dispatchOpenUrl(BUY_WALLET_URL)

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.onboarding.products.note.redux
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppDialog

View file

@ -1,8 +1,8 @@
package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.common.CompletionResult
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction

View file

@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppDialog

View file

@ -2,10 +2,10 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.backup.BackupService
import com.tangem.tap.*
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen

View file

@ -9,6 +9,7 @@ import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.common.services.Result
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.AnalyticsParam

View file

@ -11,6 +11,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.isZero
import com.tangem.common.services.Result
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.attestation.Attestation
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.analytics.Analytics

View file

@ -1,62 +0,0 @@
package com.tangem.tap.network
import com.squareup.moshi.FromJson
import com.squareup.moshi.Moshi
import com.squareup.moshi.ToJson
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.common.json.TangemSdkAdapter
import com.tangem.wallet.BuildConfig
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Converter
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import java.math.BigDecimal
import java.util.concurrent.TimeUnit
fun createRetrofitInstance(
baseUrl: String,
interceptors: List<Interceptor> = emptyList(),
): Retrofit {
val okHttpBuilder = OkHttpClient.Builder()
interceptors.forEach { okHttpBuilder.addInterceptor(it) }
addTimeOuts(okHttpBuilder)
if (BuildConfig.DEBUG) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor())
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(createMoshiConverterFactory())
.client(okHttpBuilder.build())
.build()
}
private fun addTimeOuts(okHttpBuilder: OkHttpClient.Builder) {
okHttpBuilder.callTimeout(1, TimeUnit.SECONDS)
okHttpBuilder.connectTimeout(20, TimeUnit.SECONDS)
okHttpBuilder.readTimeout(20, TimeUnit.SECONDS)
okHttpBuilder.writeTimeout(20, TimeUnit.SECONDS)
}
fun createMoshiConverterFactory(): Converter.Factory = MoshiConverterFactory.create(createMoshi())
fun createMoshi(): Moshi = Moshi.Builder()
.add(BigDecimalAdapter)
.add(KotlinJsonAdapterFactory())
.add(TangemSdkAdapter.DerivationPathAdapter())
.add(TangemSdkAdapter.DerivationNodeAdapter())
.build()
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
return logging
}
private object BigDecimalAdapter {
@FromJson
fun fromJson(string: String) = BigDecimal(string)
@ToJson
fun toJson(value: BigDecimal) = value.toString()
}

View file

@ -1,8 +1,7 @@
package com.tangem.tap.network.coinmarketcap
import com.tangem.tap.network.createRetrofitInstance
import com.tangem.network.common.createRetrofitInstance
import okhttp3.Interceptor
import okhttp3.Response
import retrofit2.http.GET
import retrofit2.http.Query
@ -25,18 +24,14 @@ interface CoinMarketCapApi {
fun create(apiKey: String): CoinMarketCapApi {
return createRetrofitInstance(
baseUrl,
listOf(createCoinMarketRequestInterceptor(apiKey)),
interceptors = listOf(createCoinMarketRequestInterceptor(apiKey)),
).create(CoinMarketCapApi::class.java)
}
}
}
private fun createCoinMarketRequestInterceptor(apiKey: String): Interceptor {
return object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("X-CMC_PRO_API_KEY", apiKey)
return chain.proceed(requestBuilder.build())
}
}
private fun createCoinMarketRequestInterceptor(apiKey: String): Interceptor = Interceptor { chain ->
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("X-CMC_PRO_API_KEY", apiKey)
chain.proceed(requestBuilder.build())
}

View file

@ -5,9 +5,9 @@ import android.util.Base64
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.network.common.createRetrofitInstance
import com.tangem.tap.common.extensions.urlEncode
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.network.createRetrofitInstance
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.network.exchangeServices.ExchangeService
import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder

View file

@ -4,9 +4,9 @@ import android.net.Uri
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.network.common.createRetrofitInstance
import com.tangem.tap.common.extensions.urlEncode
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.network.createRetrofitInstance
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.network.exchangeServices.ExchangeService
import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder
@ -23,8 +23,10 @@ class OnramperService(
) : ExchangeService, ExchangeUrlBuilder {
private val api: OnramperApi by lazy {
createRetrofitInstance(OnramperApi.BASE_URL, listOf(AddKeyToHeaderInterceptor(apiKey)))
.create(OnramperApi::class.java)
createRetrofitInstance(
baseUrl = OnramperApi.BASE_URL,
interceptors = listOf(AddKeyToHeaderInterceptor(apiKey))
).create(OnramperApi::class.java)
}
private var status: OnramperStatus? = null

View file

@ -3,7 +3,7 @@ package com.tangem.tap.network.payid
import com.squareup.moshi.JsonClass
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.tap.network.createRetrofitInstance
import com.tangem.network.common.createRetrofitInstance
import retrofit2.Retrofit
class PayIdService {

View file

@ -2,7 +2,7 @@ package com.tangem.tap.network.payid
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.tap.network.createRetrofitInstance
import com.tangem.network.common.createRetrofitInstance
/**
[REDACTED_AUTHOR]