Updated on 2026-08-14
This commit is contained in:
commit
3df9f7fe39
209 changed files with 5857 additions and 3426 deletions
|
|
@ -39,6 +39,8 @@ dependencies {
|
|||
|
||||
/** Features */
|
||||
implementation(project(":features:onboarding"))
|
||||
implementation(project(":features:learn2earn:api"))
|
||||
implementation(project(":features:learn2earn:impl"))
|
||||
implementation(project(":features:referral:presentation"))
|
||||
implementation(project(":features:referral:domain"))
|
||||
implementation(project(":features:referral:data"))
|
||||
|
|
@ -50,6 +52,8 @@ dependencies {
|
|||
implementation(project(":features:tester:impl"))
|
||||
implementation(project(":features:wallet:api"))
|
||||
implementation(project(":features:wallet:impl"))
|
||||
implementation(projects.features.tokendetails.api)
|
||||
implementation(projects.features.tokendetails.impl)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
android:largeHeap="@bool/largeHeap"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:extractNativeLibs="true"
|
||||
android:supportsRtl="true"
|
||||
tools:ignore="GoogleAppIndexingWarning"
|
||||
tools:replace="android:allowBackup, android:fullBackupContent">
|
||||
|
|
@ -138,6 +139,10 @@
|
|||
android:name="com.tangem.tap.features.sprinklr.ui.SprinklrActivity"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="com.tangem.feature.learn2earn.presentation.webView.Learn2earnWebViewActivity"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
{
|
||||
"isWalletPayIdEnabled": true,
|
||||
"isSendingToPayIdEnabled": true,
|
||||
"isTopUpEnabled": true,
|
||||
"isCreatingTwinCardsAllowed": true
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
{
|
||||
"isWalletPayIdEnabled": false,
|
||||
"isSendingToPayIdEnabled": true,
|
||||
"isTopUpEnabled": true,
|
||||
"isCreatingTwinCardsAllowed": true
|
||||
}
|
||||
|
|
@ -474,6 +474,17 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "telos",
|
||||
"symbol": "TLOS",
|
||||
"name": "Telos EVM",
|
||||
"networks":
|
||||
[
|
||||
{
|
||||
"networkId": "telos/test"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ravencoin",
|
||||
"symbol": "RVN",
|
||||
|
|
@ -495,6 +506,17 @@
|
|||
"networkId": "cosmos/test"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "aleph-zero",
|
||||
"symbol": "AZERO",
|
||||
"name": "Aleph Zero",
|
||||
"networks":
|
||||
[
|
||||
{
|
||||
"networkId": "aleph-zero/test"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import com.tangem.TangemSdk
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.operations.backup.BackupService
|
||||
import com.tangem.sdk.extensions.init
|
||||
|
|
@ -33,6 +34,11 @@ import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
|||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.features.intentHandler.IntentProcessor
|
||||
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.BuyCurrencyIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.features.shop.redux.ShopAction
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
|
|
@ -45,6 +51,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
|
@ -92,9 +99,15 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var walletRouter: WalletRouter
|
||||
|
||||
@Inject
|
||||
lateinit var tokenDetailsRouter: TokenDetailsRouter
|
||||
|
||||
@Inject
|
||||
lateinit var walletConnectInteractor: WalletConnectInteractor
|
||||
|
||||
// TODO: fixme: inject through DI
|
||||
private val intentProcessor: IntentProcessor = IntentProcessor()
|
||||
|
||||
private var snackbar: Snackbar? = null
|
||||
private val dialogManager = DialogManager()
|
||||
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::bind)
|
||||
|
|
@ -117,6 +130,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
lockUserWalletsTimer = LockUserWalletsTimer(owner = this)
|
||||
|
||||
initUserWalletsListManager()
|
||||
initIntentHandlers()
|
||||
|
||||
store.dispatch(
|
||||
ShopAction.CheckIfGooglePayAvailable(
|
||||
|
|
@ -129,10 +143,19 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
scanCardUseCase = scanCardUseCase,
|
||||
walletRouter = walletRouter,
|
||||
walletConnectInteractor = walletConnectInteractor,
|
||||
tokenDetailsRouter = tokenDetailsRouter,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun initIntentHandlers() {
|
||||
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
|
||||
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider))
|
||||
intentProcessor.addHandler(WalletConnectLinkIntentHandler())
|
||||
intentProcessor.addHandler(BuyCurrencyIntentHandler())
|
||||
intentProcessor.addHandler(SellCurrencyIntentHandler())
|
||||
}
|
||||
|
||||
private fun initUserWalletsListManager() {
|
||||
val manager = if (preferencesStorage.shouldSaveUserWallets) {
|
||||
UserWalletsListManager.provideBiometricImplementation(
|
||||
|
|
@ -164,12 +187,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
super.onResume()
|
||||
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
|
||||
|
||||
navigateToInitialScreenIfNeeded(intent)
|
||||
navigateToInitialScreenIfNeededOnResume(intent)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
intentHandler.handleIntent(intent, userWalletsListManager.hasUserWallets)
|
||||
scope.launch {
|
||||
intentProcessor.handleIntent(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
|
@ -185,6 +210,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
override fun onDestroy() {
|
||||
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
|
||||
intentProcessor.removeAll()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
|
@ -235,29 +261,39 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
lockUserWalletsTimer?.restart()
|
||||
}
|
||||
|
||||
private fun navigateToInitialScreenIfNeeded(intent: Intent?) {
|
||||
private fun navigateToInitialScreenIfNeededOnResume(intentWhichStartedActivity: Intent?) {
|
||||
val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
|
||||
val isNotScannedBefore = store.state.globalState.scanResponse == null
|
||||
val isOnboardingServiceNotActive = store.state.globalState.onboardingState.onboardingStarted
|
||||
val isShopNotOpened = store.state.shopState.total != null
|
||||
when {
|
||||
!backStackIsEmpty && isNotScannedBefore && isOnboardingServiceNotActive && isShopNotOpened -> {
|
||||
navigateToInitialScreen(intent)
|
||||
navigateToInitialScreenOnResume(intentWhichStartedActivity)
|
||||
}
|
||||
backStackIsEmpty -> {
|
||||
navigateToInitialScreen(intent)
|
||||
navigateToInitialScreenOnResume(intentWhichStartedActivity)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToInitialScreen(intent: Intent?) {
|
||||
private fun navigateToInitialScreenOnResume(intentWhichStartedActivity: Intent?) {
|
||||
if (store.state.globalState.userWalletsListManager?.hasUserWallets == true) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Welcome))
|
||||
store.dispatchOnMain(WelcomeAction.HandleIntentIfNeeded(intent))
|
||||
store.dispatchOnMain(WelcomeAction.SetInitialIntent(intentWhichStartedActivity))
|
||||
scope.launch {
|
||||
val handler = BackgroundScanIntentHandler(hasSavedUserWalletsProvider = { true })
|
||||
val isBackgroundScanNotHandled = handler.handleIntent(intentWhichStartedActivity)
|
||||
val hasNotIncompletedBackup = !backupService.hasIncompletedBackup
|
||||
if (isBackgroundScanNotHandled && hasNotIncompletedBackup) {
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Home))
|
||||
intentHandler.handleIntent(intent, hasSavedUserWallets = false)
|
||||
scope.launch {
|
||||
intentProcessor.handleIntent(intentWhichStartedActivity)
|
||||
}
|
||||
}
|
||||
store.dispatch(BackupAction.CheckForUnfinishedBackup)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,68 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.content.pm.*
|
||||
import coil.*
|
||||
import com.tangem.*
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.network.*
|
||||
import com.tangem.core.analytics.*
|
||||
import com.tangem.core.featuretoggle.manager.*
|
||||
import com.tangem.data.source.preferences.*
|
||||
import com.tangem.datasource.api.common.*
|
||||
import com.tangem.datasource.asset.*
|
||||
import com.tangem.datasource.config.*
|
||||
import com.tangem.datasource.config.models.*
|
||||
import com.tangem.datasource.connection.*
|
||||
import com.tangem.domain.*
|
||||
import com.tangem.domain.common.*
|
||||
import com.tangem.features.wallet.featuretoggles.*
|
||||
import com.tangem.tap.common.*
|
||||
import com.tangem.tap.common.analytics.*
|
||||
import com.tangem.tap.common.analytics.api.*
|
||||
import com.tangem.tap.common.analytics.handlers.amplitude.*
|
||||
import com.tangem.tap.common.analytics.handlers.appsFlyer.*
|
||||
import com.tangem.tap.common.analytics.handlers.firebase.*
|
||||
import com.tangem.tap.common.analytics.topup.*
|
||||
import com.tangem.tap.common.chat.*
|
||||
import com.tangem.tap.common.feedback.*
|
||||
import com.tangem.tap.common.images.*
|
||||
import com.tangem.tap.common.log.*
|
||||
import com.tangem.tap.common.redux.*
|
||||
import com.tangem.tap.common.redux.global.*
|
||||
import com.tangem.tap.common.shop.*
|
||||
import com.tangem.tap.domain.configurable.warningMessage.*
|
||||
import com.tangem.tap.domain.tokens.*
|
||||
import com.tangem.tap.domain.totalBalance.*
|
||||
import com.tangem.tap.domain.totalBalance.di.*
|
||||
import com.tangem.tap.domain.walletCurrencies.*
|
||||
import com.tangem.tap.domain.walletCurrencies.di.*
|
||||
import com.tangem.tap.domain.walletStores.*
|
||||
import com.tangem.tap.domain.walletStores.di.*
|
||||
import com.tangem.tap.domain.walletStores.repository.*
|
||||
import com.tangem.tap.domain.walletStores.repository.di.*
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import com.tangem.Log
|
||||
import com.tangem.LogFormat
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
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.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.common.LogConfig
|
||||
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import com.tangem.tap.common.analytics.AnalyticsFactory
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.topup.TopUpController
|
||||
import com.tangem.tap.common.chat.ChatManager
|
||||
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.images.createCoilImageLoader
|
||||
import com.tangem.tap.common.log.TangemLogCollector
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.appReducer
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.shop.TangemShopService
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.tokens.UserTokensRepository
|
||||
import com.tangem.tap.domain.totalBalance.TotalFiatBalanceCalculator
|
||||
import com.tangem.tap.domain.totalBalance.di.provideDefaultImplementation
|
||||
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
|
||||
import com.tangem.tap.domain.walletCurrencies.di.provideDefaultImplementation
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresManager
|
||||
import com.tangem.tap.domain.walletStores.di.provideDefaultImplementation
|
||||
import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository
|
||||
import com.tangem.tap.domain.walletStores.repository.WalletManagersRepository
|
||||
import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository
|
||||
import com.tangem.tap.domain.walletStores.repository.di.provideDefaultImplementation
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.*
|
||||
import com.tangem.tap.features.customtoken.api.featuretoggles.*
|
||||
import com.tangem.tap.proxy.*
|
||||
import com.tangem.tap.proxy.redux.*
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import dagger.hilt.android.*
|
||||
import kotlinx.coroutines.*
|
||||
import okhttp3.logging.*
|
||||
import org.rekotlin.*
|
||||
import timber.log.*
|
||||
import javax.inject.*
|
||||
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
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository as WalletConnect2Repository
|
||||
|
||||
lateinit var store: Store<AppState>
|
||||
|
|
@ -102,7 +111,6 @@ val walletCurrenciesManager by lazy {
|
|||
val totalFiatBalanceCalculator by lazy {
|
||||
TotalFiatBalanceCalculator.provideDefaultImplementation()
|
||||
}
|
||||
val intentHandler by lazy { IntentHandler() }
|
||||
|
||||
@HiltAndroidApp
|
||||
class TapApplication : Application(), ImageLoaderFactory {
|
||||
|
|
@ -137,6 +145,12 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
@Inject
|
||||
lateinit var walletConnectSessionsRepository: WalletConnectSessionsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var learn2earnInteractor: Learn2earnInteractor
|
||||
|
||||
@Inject
|
||||
lateinit var tokenDetailsFeatureToggles: TokenDetailsFeatureToggles
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
|
@ -153,6 +167,7 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
walletFeatureToggles = walletFeatureToggles,
|
||||
walletConnectRepository = walletConnect2Repository,
|
||||
walletConnectSessionsRepository = walletConnectSessionsRepository,
|
||||
tokenDetailsFeatureToggles = tokenDetailsFeatureToggles,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -190,8 +205,11 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
appStateHolder.userTokensRepository = userTokensRepository
|
||||
appStateHolder.walletStoresManager = walletStoresManager
|
||||
|
||||
scope.launch {
|
||||
// TODO: Try to performance and user experience.
|
||||
// [REDACTED_JIRA]
|
||||
runBlocking {
|
||||
featureTogglesManager.init()
|
||||
learn2earnInteractor.init()
|
||||
}
|
||||
|
||||
initTopUpController()
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Build
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.removePrefixOrNull
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class IntentHandler {
|
||||
|
||||
private val nfcActions = arrayOf(
|
||||
NfcAdapter.ACTION_NDEF_DISCOVERED,
|
||||
NfcAdapter.ACTION_TECH_DISCOVERED,
|
||||
NfcAdapter.ACTION_TAG_DISCOVERED,
|
||||
)
|
||||
|
||||
fun handleIntent(intent: Intent?, hasSavedUserWallets: Boolean) {
|
||||
handleBackgroundScan(intent, hasSavedUserWallets)
|
||||
handleWalletConnectLink(intent)
|
||||
handleBuyCurrencyCallback(intent)
|
||||
handleSellCurrencyCallback(intent)
|
||||
}
|
||||
|
||||
fun handleWalletConnectLink(intent: Intent?) {
|
||||
val wcUri = when (intent?.scheme) {
|
||||
WalletConnectManager.WC_SCHEME -> {
|
||||
intent.data?.toString()
|
||||
}
|
||||
TANGEM_SCHEME -> {
|
||||
intent.data?.toString()?.removePrefixOrNull(TANGEM_WC_PREFIX)
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
if (wcUri != null) {
|
||||
store.dispatch(WalletConnectAction.HandleDeepLink(wcUri))
|
||||
}
|
||||
}
|
||||
|
||||
fun handleBackgroundScan(intent: Intent?, hasSavedUserWallets: Boolean): Boolean {
|
||||
if (intent == null || intent.action !in nfcActions) return false
|
||||
|
||||
val tag: Tag? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
|
||||
}
|
||||
if (tag == null) return false
|
||||
|
||||
intent.action = null
|
||||
if (hasSavedUserWallets) {
|
||||
// TODO: Remove delay after [REDACTED_JIRA]
|
||||
scope.launch {
|
||||
delay(timeMillis = 200)
|
||||
store.dispatch(WelcomeAction.ProceedWithCard)
|
||||
}
|
||||
} else {
|
||||
store.dispatch(HomeAction.ReadCard())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun handleBuyCurrencyCallback(intent: Intent?) {
|
||||
val data = intent?.data ?: return
|
||||
|
||||
val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL)
|
||||
if (data.host == successUri.host && data.authority == successUri.authority) {
|
||||
val currency = store.state.walletState.selectedCurrency ?: return
|
||||
val currencyType = AnalyticsParam.CurrencyType.Currency(currency)
|
||||
Analytics.send(Token.Bought(currencyType))
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSellCurrencyCallback(intent: Intent?) {
|
||||
try {
|
||||
val transactionID =
|
||||
intent?.data?.getQueryParameter(TRANSACTION_ID_PARAM) ?: return
|
||||
val currency =
|
||||
intent.data?.getQueryParameter(CURRENCY_CODE_PARAM) ?: return
|
||||
val amount =
|
||||
intent.data?.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return
|
||||
val destinationAddress =
|
||||
intent.data?.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM)
|
||||
?: return
|
||||
|
||||
Timber.d("MoonPay Sell: $amount $currency to $destinationAddress")
|
||||
|
||||
store.dispatch(
|
||||
WalletAction.TradeCryptoAction.SendCrypto(
|
||||
currencyId = currency,
|
||||
amount = amount,
|
||||
destinationAddress = destinationAddress,
|
||||
transactionId = transactionID,
|
||||
),
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
Timber.d("Not MoonPay URL")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TRANSACTION_ID_PARAM = "transactionId"
|
||||
private const val CURRENCY_CODE_PARAM = "baseCurrencyCode"
|
||||
private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount"
|
||||
private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress"
|
||||
private const val TANGEM_SCHEME = "tangem"
|
||||
private const val TANGEM_WC_PREFIX = "tangem://wc?uri="
|
||||
}
|
||||
}
|
||||
|
|
@ -31,12 +31,14 @@ sealed class Basic(
|
|||
currency: AnalyticsParam.CardCurrency,
|
||||
batch: String,
|
||||
signInType: SignInType,
|
||||
walletsCount: String,
|
||||
) : Basic(
|
||||
event = "Signed in",
|
||||
params = mapOf(
|
||||
AnalyticsParam.CURRENCY to currency.value,
|
||||
AnalyticsParam.BATCH to batch,
|
||||
"Sign in type" to signInType.name,
|
||||
"Wallets Count" to walletsCount,
|
||||
),
|
||||
) {
|
||||
enum class SignInType {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ fun Blockchain.getGreyedOutIconRes(): Int {
|
|||
Blockchain.TerraV1 -> R.drawable.ic_terra_no_color
|
||||
Blockchain.TerraV2 -> R.drawable.ic_terra2_no_color
|
||||
Blockchain.Cronos -> R.drawable.ic_cronos_no_color
|
||||
Blockchain.Telos, Blockchain.TelosTestnet -> R.drawable.ic_telos_no_color
|
||||
Blockchain.AlephZero, Blockchain.AlephZeroTestnet -> R.drawable.ic_azero_no_color
|
||||
else -> R.drawable.ic_tangem_logo
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,18 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
AppScreen.WalletDetails -> WalletDetailsFragment()
|
||||
AppScreen.WalletDetails -> {
|
||||
val featureToggles = store.state.daggerGraphState.get(
|
||||
getDependency = DaggerGraphState::tokenDetailsFeatureToggles,
|
||||
)
|
||||
if (featureToggles.isRedesignedScreenEnabled) {
|
||||
store.state.daggerGraphState
|
||||
.get(getDependency = DaggerGraphState::tokenDetailsRouter)
|
||||
.getEntryFragment()
|
||||
} else {
|
||||
WalletDetailsFragment()
|
||||
}
|
||||
}
|
||||
AppScreen.WalletConnectSessions -> WalletConnectFragment()
|
||||
AppScreen.QrScan -> QrScanFragment()
|
||||
AppScreen.ReferralProgram -> ReferralFragment()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.tap.common.analytics.topup.TopUpController
|
|||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
|
|
@ -20,7 +19,6 @@ data class GlobalState(
|
|||
val onboardingState: OnboardingState = OnboardingState(),
|
||||
val cardVerifiedOnline: Boolean = false,
|
||||
val tapWalletManager: TapWalletManager = TapWalletManager(),
|
||||
val payIdManager: PayIdManager = PayIdManager(),
|
||||
val configManager: ConfigManager? = null,
|
||||
val warningManager: WarningMessagesManager? = null,
|
||||
val feedbackManager: FeedbackManager? = null,
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.network.payid.PayIdVerifyService
|
||||
import com.tangem.tap.network.payid.VerifyPayIdResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
class PayIdManager {
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
suspend fun verifyPayId(payId: String, blockchain: Blockchain): Result<VerifyPayIdResponse> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val splitPayId = payId.split("\$")
|
||||
val user = splitPayId[0]
|
||||
val baseUrl = "https://${splitPayId[1]}/"
|
||||
return@withContext PayIdVerifyService(baseUrl).verifyAddress(user, blockchain.getPayIdNetwork())
|
||||
}
|
||||
|
||||
private fun Blockchain.getPayIdNetwork(): String {
|
||||
return when (this) {
|
||||
Blockchain.XRP -> "XRPL"
|
||||
Blockchain.RSK -> "RSK"
|
||||
else -> this.currency
|
||||
}.lowercase(Locale.getDefault())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val payIdRegExp = (
|
||||
"^[a-z0-9!#@%&*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#@%&*+/=?^_`{|}~-]+)*\\\$(?:(?:[a-z0-9]" +
|
||||
"(?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z-]*[a-z0-9])?|(?:[0-9]{1,3}\\.){3}[0-9]{1,3})\$"
|
||||
).toRegex()
|
||||
|
||||
val payIdSupported: EnumSet<Blockchain> = EnumSet.of(
|
||||
Blockchain.XRP,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.Bitcoin,
|
||||
Blockchain.Litecoin,
|
||||
Blockchain.Stellar,
|
||||
Blockchain.Cardano,
|
||||
Blockchain.CardanoShelley,
|
||||
Blockchain.BitcoinCash,
|
||||
Blockchain.Binance,
|
||||
Blockchain.RSK,
|
||||
Blockchain.Tezos,
|
||||
)
|
||||
|
||||
fun isPayId(value: String?): Boolean = value?.contains(payIdRegExp) ?: false
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.isPayIdSupported(): Boolean {
|
||||
return PayIdManager.payIdSupported.contains(this)
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.core.analytics.Analytics
|
||||
|
|
@ -134,22 +137,13 @@ class TapWalletManager(
|
|||
|
||||
fun updateConfigManager(data: ScanResponse) {
|
||||
val configManager = store.state.globalState.configManager
|
||||
val blockchain = data.cardTypesResolver.getBlockchain()
|
||||
|
||||
if (data.cardTypesResolver.isStart2Coin()) {
|
||||
configManager?.turnOff(ConfigManager.IS_SENDING_TO_PAY_ID_ENABLED)
|
||||
configManager?.turnOff(ConfigManager.IS_TOP_UP_ENABLED)
|
||||
} else if (blockchain == Blockchain.Bitcoin ||
|
||||
data.walletData?.blockchain == Blockchain.Bitcoin.id
|
||||
) {
|
||||
configManager?.resetToDefault(ConfigManager.IS_SENDING_TO_PAY_ID_ENABLED)
|
||||
configManager?.resetToDefault(ConfigManager.IS_TOP_UP_ENABLED)
|
||||
} else {
|
||||
configManager?.resetToDefault(ConfigManager.IS_SENDING_TO_PAY_ID_ENABLED)
|
||||
configManager?.resetToDefault(ConfigManager.IS_TOP_UP_ENABLED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Wallet.getFirstToken(): Token? {
|
||||
return getTokens().toList().getOrNull(0)
|
||||
}
|
||||
fun Wallet.getFirstToken(): Token? = getTokens().toList().getOrNull(index = 0)
|
||||
|
|
@ -26,6 +26,11 @@ interface UserWalletsListManager {
|
|||
* */
|
||||
val hasUserWallets: Boolean
|
||||
|
||||
/**
|
||||
* Count of saved user wallets
|
||||
*/
|
||||
val walletsCount: Int
|
||||
|
||||
/**
|
||||
* Set [UserWallet] with provided [UserWalletId] as selected
|
||||
*
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ internal class BiometricUserWalletsListManager(
|
|||
override val hasUserWallets: Boolean
|
||||
get() = keysRepository.hasSavedEncryptionKeys()
|
||||
|
||||
override val walletsCount: Int
|
||||
get() = state.value.userWallets.size
|
||||
|
||||
override suspend fun unlock(): CompletionResult<UserWallet> {
|
||||
return unlockWithBiometryInternal()
|
||||
.mapFailure { error ->
|
||||
|
|
|
|||
|
|
@ -7,13 +7,7 @@ import com.tangem.tap.domain.model.UserWallet
|
|||
import com.tangem.tap.domain.userWalletList.UserWalletsListError
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.updateAndGet
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
||||
|
|
@ -36,6 +30,12 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
override val hasUserWallets: Boolean
|
||||
get() = state.value.userWallet != null
|
||||
|
||||
/**
|
||||
* only 1 wallet stored in runtime implementation
|
||||
*/
|
||||
override val walletsCount: Int
|
||||
get() = 1
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
|
||||
state.value.userWallet
|
||||
?.takeIf { it.walletId == userWalletId }
|
||||
|
|
|
|||
|
|
@ -12,36 +12,47 @@ import androidx.compose.ui.platform.ComposeView
|
|||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.learn2earn.presentation.Learn2earnViewModel
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.home.compose.StoriesScreen
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.home.redux.HomeState
|
||||
import com.tangem.tap.features.home.redux.Stories
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensAction
|
||||
import com.tangem.tap.store
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
@AndroidEntryPoint
|
||||
class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
||||
|
||||
private var homeState: MutableState<HomeState> = mutableStateOf(store.state.homeState)
|
||||
|
||||
private val learn2earnViewModel by activityViewModels<Learn2earnViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
store.dispatch(HomeAction.OnCreate)
|
||||
store.dispatch(HomeAction.Init)
|
||||
if (learn2earnViewModel.uiState.storyScreenState.isVisible) {
|
||||
store.dispatch(HomeAction.InsertStory(position = 0, Stories.OneInchPromo))
|
||||
// re init homeState after inserting learn2earn story
|
||||
homeState = mutableStateOf(store.state.homeState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return ComposeView(inflater.context).apply {
|
||||
setContent {
|
||||
BackHandler {
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
AppCompatTheme {
|
||||
TangemTheme {
|
||||
BackHandler {
|
||||
requireActivity().finish()
|
||||
}
|
||||
ScreenContent()
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +90,8 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
|||
@Composable
|
||||
private fun ScreenContent() {
|
||||
StoriesScreen(
|
||||
homeState,
|
||||
homeState = homeState,
|
||||
onLearn2earnClick = learn2earnViewModel.uiState.storyScreenState.onClick,
|
||||
onScanButtonClick = {
|
||||
Analytics.send(IntroductionProcess.ButtonScanCard())
|
||||
store.dispatch(HomeAction.ReadCard())
|
||||
|
|
|
|||
|
|
@ -5,29 +5,11 @@ package com.tangem.tap.features.home.compose
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
|
|
@ -43,38 +25,40 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.tangem.tap.features.home.compose.content.FirstStoriesContent
|
||||
import com.tangem.tap.features.home.compose.content.StoriesCurrencies
|
||||
import com.tangem.tap.features.home.compose.content.StoriesRevolutionaryWallet
|
||||
import com.tangem.tap.features.home.compose.content.StoriesUltraSecureBackup
|
||||
import com.tangem.tap.features.home.compose.content.StoriesWalletForEveryone
|
||||
import com.tangem.tap.features.home.compose.content.StoriesWeb3
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.learn2earn.presentation.ui.Learn2earnStoriesScreen
|
||||
import com.tangem.tap.features.home.compose.content.*
|
||||
import com.tangem.tap.features.home.compose.views.HomeButtons
|
||||
import com.tangem.tap.features.home.compose.views.StoriesProgressBar
|
||||
import com.tangem.tap.features.home.redux.HomeState
|
||||
import com.tangem.tap.features.home.redux.Stories
|
||||
import com.tangem.wallet.R
|
||||
import kotlin.math.max
|
||||
|
||||
private const val STEPS = 6
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
@Composable
|
||||
fun StoriesScreen(
|
||||
homeState: MutableState<HomeState>,
|
||||
onLearn2earnClick: () -> Unit,
|
||||
onScanButtonClick: () -> Unit,
|
||||
onShopButtonClick: () -> Unit,
|
||||
onSearchTokensClick: () -> Unit,
|
||||
) {
|
||||
val currentStep = remember { mutableStateOf(1) }
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val state = homeState.value
|
||||
|
||||
val isDarkBackground = currentStep.value !in 3..5
|
||||
var currentStory by remember { mutableStateOf(state.firstStory) }
|
||||
val currentStep = { state.stepOf(currentStory) }
|
||||
|
||||
val goToPreviousScreen = {
|
||||
currentStep.value = max(1, currentStep.value - 1)
|
||||
currentStory = state.stories[max(0, currentStep() - 1)]
|
||||
}
|
||||
val goToNextScreen = {
|
||||
currentStep.value = if (currentStep.value < STEPS) currentStep.value + 1 else 1
|
||||
currentStory = if (currentStep() < state.stories.lastIndex) {
|
||||
state.stories[currentStep() + 1]
|
||||
} else {
|
||||
state.firstStory
|
||||
}
|
||||
}
|
||||
|
||||
val isPressed = remember { mutableStateOf(false) }
|
||||
|
|
@ -82,10 +66,10 @@ fun StoriesScreen(
|
|||
|
||||
val hideContent = remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(key1 = isDarkBackground) {
|
||||
LaunchedEffect(key1 = currentStory.isDarkBackground) {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = !isDarkBackground,
|
||||
darkIcons = !currentStory.isDarkBackground,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +118,7 @@ fun StoriesScreen(
|
|||
},
|
||||
)
|
||||
}
|
||||
if (!isDarkBackground) {
|
||||
if (!currentStory.isDarkBackground) {
|
||||
Image(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
painter = painterResource(id = R.drawable.ic_overlay),
|
||||
|
|
@ -154,9 +138,9 @@ fun StoriesScreen(
|
|||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
StoriesProgressBar(
|
||||
steps = STEPS,
|
||||
currentStep = currentStep.value,
|
||||
stepDuration = currentStep.duration(),
|
||||
steps = state.stories.lastIndex,
|
||||
currentStep = currentStep(),
|
||||
stepDuration = currentStory.duration,
|
||||
paused = isPaused,
|
||||
onStepFinish = goToNextScreen,
|
||||
)
|
||||
|
|
@ -169,15 +153,16 @@ fun StoriesScreen(
|
|||
.height(17.dp)
|
||||
.alpha(if (hideContent.value) 0f else 1f)
|
||||
.align(Alignment.Start),
|
||||
colorFilter = if (isDarkBackground) null else ColorFilter.tint(Color.Black),
|
||||
colorFilter = if (currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black),
|
||||
)
|
||||
when (currentStep.value) {
|
||||
1 -> FirstStoriesContent(isPaused, currentStep.duration()) { hideContent.value = it }
|
||||
2 -> StoriesRevolutionaryWallet(currentStep.duration())
|
||||
3 -> StoriesUltraSecureBackup(isPaused, currentStep.duration())
|
||||
4 -> StoriesCurrencies(isPaused, currentStep.duration())
|
||||
5 -> StoriesWeb3(isPaused, currentStep.duration())
|
||||
6 -> StoriesWalletForEveryone(currentStep.duration())
|
||||
when (currentStory) {
|
||||
Stories.OneInchPromo -> Learn2earnStoriesScreen(onLearn2earnClick)
|
||||
Stories.TangemIntro -> FirstStoriesContent(isPaused, currentStory.duration) { hideContent.value = it }
|
||||
Stories.RevolutionaryWallet -> StoriesRevolutionaryWallet(currentStory.duration)
|
||||
Stories.UltraSecureBackup -> StoriesUltraSecureBackup(isPaused, currentStory.duration)
|
||||
Stories.Currencies -> StoriesCurrencies(isPaused, currentStory.duration)
|
||||
Stories.Web3 -> StoriesWeb3(isPaused, currentStory.duration)
|
||||
Stories.WalletForEveryone -> StoriesWalletForEveryone(currentStory.duration)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
|
|
@ -186,7 +171,7 @@ fun StoriesScreen(
|
|||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
if (currentStep.value == 4) {
|
||||
if (currentStory == Stories.Currencies) {
|
||||
Button(
|
||||
onClick = onSearchTokensClick,
|
||||
modifier = Modifier
|
||||
|
|
@ -210,29 +195,31 @@ fun StoriesScreen(
|
|||
)
|
||||
}
|
||||
}
|
||||
HomeButtons(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 37.dp)
|
||||
.fillMaxWidth(),
|
||||
isDarkBackground = isDarkBackground,
|
||||
btnScanStateInProgress = homeState.value.btnScanStateInProgress,
|
||||
onScanButtonClick = onScanButtonClick,
|
||||
onShopButtonClick = onShopButtonClick,
|
||||
)
|
||||
|
||||
if (currentStory != Stories.OneInchPromo) {
|
||||
HomeButtons(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.size16,
|
||||
end = TangemTheme.dimens.size16,
|
||||
bottom = TangemTheme.dimens.size36,
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
isDarkBackground = currentStory.isDarkBackground,
|
||||
btnScanStateInProgress = homeState.value.btnScanStateInProgress,
|
||||
onScanButtonClick = onScanButtonClick,
|
||||
onShopButtonClick = onShopButtonClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun MutableState<Int>.duration(): Int = when (this.value) {
|
||||
1 -> 8000
|
||||
else -> 6000
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun StoriesScreenPreview() {
|
||||
StoriesScreen(
|
||||
onLearn2earnClick = {},
|
||||
onScanButtonClick = {},
|
||||
onShopButtonClick = {},
|
||||
onSearchTokensClick = {},
|
||||
|
|
|
|||
|
|
@ -4,12 +4,7 @@ import androidx.compose.animation.core.Animatable
|
|||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -52,7 +47,7 @@ fun StoriesProgressBar(
|
|||
// .height()
|
||||
.padding(start = 9.dp, end = 9.dp, top = 16.dp),
|
||||
) {
|
||||
for (index in 1..steps) {
|
||||
for (index in 0..steps) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(2.dp)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ sealed class HomeAction : Action {
|
|||
object OnCreate : HomeAction()
|
||||
object Init : HomeAction()
|
||||
|
||||
data class InsertStory(val position: Int, val story: Stories) : HomeAction()
|
||||
|
||||
data class ReadCard(
|
||||
val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction),
|
||||
) : HomeAction()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@ private fun internalReduce(action: Action, state: AppState): HomeState {
|
|||
|
||||
var state = state.homeState
|
||||
when (action) {
|
||||
is HomeAction.InsertStory -> {
|
||||
state = state.copy(
|
||||
stories = state.stories.toMutableList().apply {
|
||||
add(action.position, action.story)
|
||||
},
|
||||
)
|
||||
}
|
||||
is HomeAction.ScanInProgress -> {
|
||||
state = state.copy(scanInProgress = action.scanInProgress)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,38 @@ import org.rekotlin.StateType
|
|||
data class HomeState(
|
||||
val scanInProgress: Boolean = false,
|
||||
val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED),
|
||||
val stories: List<Stories> = initDefaultStories(),
|
||||
) : StateType {
|
||||
|
||||
val firstStory: Stories
|
||||
get() = stories[0]
|
||||
|
||||
val btnScanStateInProgress: Boolean
|
||||
get() = btnScanState.progressState == ProgressState.Loading
|
||||
|
||||
fun stepOf(story: Stories): Int = stories.indexOf(story)
|
||||
|
||||
companion object {
|
||||
fun initDefaultStories(): List<Stories> = listOf(
|
||||
Stories.TangemIntro,
|
||||
Stories.RevolutionaryWallet,
|
||||
Stories.UltraSecureBackup,
|
||||
Stories.Currencies,
|
||||
Stories.Web3,
|
||||
Stories.WalletForEveryone,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Stories(
|
||||
val isDarkBackground: Boolean,
|
||||
val duration: Int,
|
||||
) {
|
||||
object OneInchPromo : Stories(true, duration = 8000)
|
||||
object TangemIntro : Stories(true, duration = 8000)
|
||||
object RevolutionaryWallet : Stories(true, duration = 6000)
|
||||
object UltraSecureBackup : Stories(false, duration = 6000)
|
||||
object Currencies : Stories(false, duration = 6000)
|
||||
object Web3 : Stories(false, duration = 6000)
|
||||
object WalletForEveryone : Stories(true, duration = 6000)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tap.features.intentHandler
|
||||
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface IntentHandler {
|
||||
suspend fun handleIntent(intent: Intent?): Boolean
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tap.features.intentHandler
|
||||
|
||||
import android.content.Intent
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: fixme: close it with the combined interfaces IntentHandler and IntentHandlerHolder
|
||||
class IntentProcessor {
|
||||
|
||||
private val intentHandlers = CopyOnWriteArrayList<IntentHandler>()
|
||||
|
||||
fun addHandler(handler: IntentHandler) {
|
||||
intentHandlers.add(handler)
|
||||
}
|
||||
|
||||
fun removeIntentHandler(handler: IntentHandler) {
|
||||
intentHandlers.remove(handler)
|
||||
}
|
||||
|
||||
fun removeAll() {
|
||||
intentHandlers.clear()
|
||||
}
|
||||
|
||||
suspend fun handleIntent(intent: Intent?) {
|
||||
intentHandlers.forEach {
|
||||
it.handleIntent(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Build
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BackgroundScanIntentHandler(
|
||||
private val hasSavedUserWalletsProvider: () -> Boolean,
|
||||
) : IntentHandler {
|
||||
|
||||
private val nfcActions = arrayOf(
|
||||
NfcAdapter.ACTION_NDEF_DISCOVERED,
|
||||
NfcAdapter.ACTION_TECH_DISCOVERED,
|
||||
NfcAdapter.ACTION_TAG_DISCOVERED,
|
||||
)
|
||||
|
||||
override suspend fun handleIntent(intent: Intent?): Boolean {
|
||||
if (intent == null || intent.action !in nfcActions) return false
|
||||
|
||||
val tag: Tag? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
|
||||
}
|
||||
if (tag == null) return false
|
||||
|
||||
intent.action = null
|
||||
if (hasSavedUserWalletsProvider.invoke()) {
|
||||
// TODO: Remove delay after [REDACTED_JIRA]
|
||||
scope.launch {
|
||||
delay(timeMillis = 200)
|
||||
store.dispatch(WelcomeAction.ProceedWithCard)
|
||||
}
|
||||
} else {
|
||||
store.dispatch(HomeAction.ReadCard())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder
|
||||
import com.tangem.tap.store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BuyCurrencyIntentHandler : IntentHandler {
|
||||
|
||||
override suspend fun handleIntent(intent: Intent?): Boolean {
|
||||
val data = intent?.data ?: return false
|
||||
val currency = store.state.walletState.selectedCurrency ?: return false
|
||||
|
||||
val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL)
|
||||
return if (data.host == successUri.host && data.authority == successUri.authority) {
|
||||
val currencyType = AnalyticsParam.CurrencyType.Currency(currency)
|
||||
Analytics.send(Token.Bought(currencyType))
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SellCurrencyIntentHandler : IntentHandler {
|
||||
|
||||
override suspend fun handleIntent(intent: Intent?): Boolean {
|
||||
return try {
|
||||
val intentData = intent?.data ?: return false
|
||||
val transactionID = intentData.getQueryParameter(TRANSACTION_ID_PARAM) ?: return false
|
||||
val currency = intentData.getQueryParameter(CURRENCY_CODE_PARAM) ?: return false
|
||||
val amount = intentData.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return false
|
||||
val destinationAddress = intentData.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM) ?: return false
|
||||
|
||||
Timber.d("MoonPay Sell: $amount $currency to $destinationAddress")
|
||||
store.dispatch(
|
||||
WalletAction.TradeCryptoAction.SendCrypto(
|
||||
currencyId = currency,
|
||||
amount = amount,
|
||||
destinationAddress = destinationAddress,
|
||||
transactionId = transactionID,
|
||||
),
|
||||
)
|
||||
true
|
||||
} catch (exception: Exception) {
|
||||
Timber.d("Not MoonPay URL")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val TRANSACTION_ID_PARAM = "transactionId"
|
||||
private const val CURRENCY_CODE_PARAM = "baseCurrencyCode"
|
||||
private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount"
|
||||
private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.tap.common.extensions.removePrefixOrNull
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
import com.tangem.tap.store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class WalletConnectLinkIntentHandler : IntentHandler {
|
||||
|
||||
override suspend fun handleIntent(intent: Intent?): Boolean {
|
||||
val intentData = intent?.data ?: return false
|
||||
val scheme = intent.scheme ?: return false
|
||||
|
||||
val wcUri = when (scheme) {
|
||||
WalletConnectManager.WC_SCHEME -> intentData.toString()
|
||||
TANGEM_SCHEME -> intentData.toString().removePrefixOrNull(TANGEM_WC_PREFIX)
|
||||
else -> null
|
||||
}
|
||||
|
||||
return if (wcUri == null) {
|
||||
false
|
||||
} else {
|
||||
store.dispatch(WalletConnectAction.HandleDeepLink(wcUri))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val TANGEM_SCHEME = "tangem"
|
||||
private const val TANGEM_WC_PREFIX = "tangem://wc?uri="
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.ToastNotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
|
|
@ -34,15 +33,14 @@ data class PrepareSendScreen(
|
|||
val tokenRate: BigDecimal? = null,
|
||||
) : SendScreenAction
|
||||
|
||||
// Address or PayId
|
||||
sealed class AddressPayIdActionUi : SendScreenActionUi {
|
||||
data class HandleUserInput(val data: String) : AddressPayIdActionUi()
|
||||
data class PasteAddressPayId(val data: String, val sourceType: AddressEntered.SourceType) : AddressPayIdActionUi()
|
||||
data class CheckClipboard(val data: String?) : AddressPayIdActionUi()
|
||||
data class CheckAddressPayId(val sourceType: AddressEntered.SourceType?) : AddressPayIdActionUi()
|
||||
data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUi()
|
||||
data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUi()
|
||||
data class ChangePayIdState(val sendingToPayIdEnabled: Boolean) : AddressPayIdActionUi()
|
||||
// Address
|
||||
sealed class AddressActionUi : SendScreenActionUi {
|
||||
data class HandleUserInput(val data: String) : AddressActionUi()
|
||||
data class PasteAddress(val data: String, val sourceType: AddressEntered.SourceType) : AddressActionUi()
|
||||
data class CheckClipboard(val data: String?) : AddressActionUi()
|
||||
data class CheckAddress(val sourceType: AddressEntered.SourceType?) : AddressActionUi()
|
||||
data class SetTruncateHandler(val handler: (String) -> String) : AddressActionUi()
|
||||
data class TruncateOrRestore(val truncate: Boolean) : AddressActionUi()
|
||||
}
|
||||
|
||||
sealed class TransactionExtrasAction : SendScreenActionUi {
|
||||
|
|
@ -76,27 +74,15 @@ sealed class TransactionExtrasAction : SendScreenActionUi {
|
|||
}
|
||||
}
|
||||
|
||||
sealed class AddressPayIdVerifyAction : SendScreenAction {
|
||||
sealed class AddressVerifyAction : SendScreenAction {
|
||||
enum class Error {
|
||||
PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN,
|
||||
PAY_ID_NOT_REGISTERED,
|
||||
PAY_ID_REQUEST_FAILED,
|
||||
ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN,
|
||||
ADDRESS_SAME_AS_WALLET,
|
||||
}
|
||||
|
||||
data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressPayIdVerifyAction()
|
||||
data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressVerifyAction()
|
||||
|
||||
sealed class PayIdVerification : AddressPayIdVerifyAction() {
|
||||
data class SetPayIdError(val error: Error?) : PayIdVerification()
|
||||
data class SetPayIdWalletAddress(
|
||||
val payId: String,
|
||||
val payIdWalletAddress: String,
|
||||
val isUserInput: Boolean,
|
||||
) : PayIdVerification()
|
||||
}
|
||||
|
||||
sealed class AddressVerification : AddressPayIdVerifyAction() {
|
||||
sealed class AddressVerification : AddressVerifyAction() {
|
||||
data class SetAddressError(val error: Error?) : AddressVerification()
|
||||
data class SetWalletAddress(val address: String, val isUserInput: Boolean) : AddressVerification()
|
||||
}
|
||||
|
|
@ -155,8 +141,6 @@ sealed class SendAction : SendScreenAction {
|
|||
override val messageResource: Int = R.string.send_transaction_success
|
||||
}
|
||||
|
||||
data class SendError(override val error: TapError) : SendAction(), ErrorAction
|
||||
|
||||
sealed class Dialog : SendAction(), StateDialog {
|
||||
data class TezosWarningDialog(
|
||||
val reduceCallback: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -2,49 +2,39 @@ package com.tangem.tap.features.send.redux.middlewares
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.isPayIdSupported
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetAddressError
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetWalletAddress
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdError
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdWalletAddress
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetAddressError
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetWalletAddress
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction.Error
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class AddressPayIdMiddleware {
|
||||
internal class AddressMiddleware {
|
||||
|
||||
fun handle(action: AddressPayIdActionUi, appState: AppState?, dispatch: (Action) -> Unit) {
|
||||
fun handle(action: AddressActionUi, appState: AppState?, dispatch: (Action) -> Unit) {
|
||||
when (action) {
|
||||
is AddressPayIdActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch)
|
||||
is AddressPayIdActionUi.PasteAddressPayId -> pasteAddressPayId(action.data, action.sourceType, dispatch)
|
||||
is AddressPayIdActionUi.CheckClipboard -> verifyClipboard(action.data, appState, dispatch)
|
||||
is AddressPayIdActionUi.CheckAddressPayId -> verifyAddressPayId(action.sourceType, appState, dispatch)
|
||||
is AddressActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch)
|
||||
is AddressActionUi.PasteAddress -> pasteAddress(action.data, action.sourceType, dispatch)
|
||||
is AddressActionUi.CheckClipboard -> verifyClipboard(action.data, appState, dispatch)
|
||||
is AddressActionUi.CheckAddress -> verifyAddress(action.sourceType, appState, dispatch)
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUserInput(input: String, appState: AppState?, dispatch: DispatchFunction) {
|
||||
val sendState = appState?.sendState ?: return
|
||||
if (input == sendState.addressPayIdState.viewFieldValue.value) return
|
||||
if (input == sendState.addressState.viewFieldValue.value) return
|
||||
|
||||
setAddressAndCheck(data = input, sourceType = null, isUserInput = true, dispatch = dispatch)
|
||||
}
|
||||
|
||||
private fun pasteAddressPayId(data: String, sourceType: AddressEntered.SourceType, dispatch: (Action) -> Unit) {
|
||||
private fun pasteAddress(data: String, sourceType: AddressEntered.SourceType, dispatch: (Action) -> Unit) {
|
||||
setAddressAndCheck(data = data, sourceType = sourceType, isUserInput = false, dispatch = dispatch)
|
||||
}
|
||||
|
||||
|
|
@ -54,74 +44,27 @@ internal class AddressPayIdMiddleware {
|
|||
isUserInput: Boolean,
|
||||
dispatch: (Action) -> Unit,
|
||||
) {
|
||||
val potentialPayId = data.lowercase()
|
||||
if (isPayIdEnabled() && PayIdManager.isPayId(potentialPayId)) {
|
||||
dispatch(SetPayIdWalletAddress(potentialPayId, "", isUserInput))
|
||||
} else {
|
||||
dispatch(SetWalletAddress(data, isUserInput))
|
||||
}
|
||||
dispatch(AddressPayIdActionUi.CheckAddressPayId(sourceType))
|
||||
dispatch(SetWalletAddress(data, isUserInput))
|
||||
dispatch(AddressActionUi.CheckAddress(sourceType))
|
||||
}
|
||||
|
||||
private fun verifyAddressPayId(
|
||||
private fun verifyAddress(
|
||||
sourceType: AddressEntered.SourceType?,
|
||||
appState: AppState?,
|
||||
dispatch: (Action) -> Unit,
|
||||
) {
|
||||
val sendState = appState?.sendState ?: return
|
||||
val wallet = sendState.walletManager?.wallet ?: return
|
||||
val addressPayId = sendState.addressPayIdState.normalFieldValue ?: return
|
||||
val isUserInput = sendState.addressPayIdState.viewFieldValue.isFromUserInput
|
||||
val address = sendState.addressState.normalFieldValue ?: return
|
||||
val isUserInput = sendState.addressState.viewFieldValue.isFromUserInput
|
||||
|
||||
if (isPayIdEnabled() && PayIdManager.isPayId(addressPayId)) {
|
||||
verifyPayId(addressPayId, wallet, isUserInput, dispatch)
|
||||
} else {
|
||||
verifyAddress(
|
||||
address = addressPayId,
|
||||
wallet = wallet,
|
||||
isUserInput = isUserInput,
|
||||
dispatch = dispatch,
|
||||
sourceType = sourceType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyPayId(payId: String, wallet: Wallet, isUserInput: Boolean, dispatch: DispatchFunction) {
|
||||
val blockchain = wallet.blockchain
|
||||
if (!blockchain.isPayIdSupported()) {
|
||||
dispatch(SetPayIdError(Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN))
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val result = PayIdManager().verifyPayId(payId, blockchain)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val addressDetails = result.data.getAddressDetails()
|
||||
if (addressDetails == null) {
|
||||
dispatch(SetPayIdError(Error.PAY_ID_NOT_REGISTERED))
|
||||
return@withContext
|
||||
}
|
||||
|
||||
val address = addressDetails.address
|
||||
val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, address)
|
||||
if (failReason == null) {
|
||||
dispatch(SetPayIdWalletAddress(payId, address, isUserInput))
|
||||
dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, addressDetails.tag))
|
||||
dispatch(FeeAction.RequestFee)
|
||||
} else {
|
||||
dispatch(SetAddressError(failReason))
|
||||
dispatch(TransactionExtrasAction.Release)
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {
|
||||
dispatch(SetPayIdError(Error.PAY_ID_REQUEST_FAILED))
|
||||
dispatch(TransactionExtrasAction.Release)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
verifyAddress(
|
||||
address = address,
|
||||
wallet = wallet,
|
||||
isUserInput = isUserInput,
|
||||
dispatch = dispatch,
|
||||
sourceType = sourceType,
|
||||
)
|
||||
}
|
||||
|
||||
private fun verifyAddress(
|
||||
|
|
@ -219,41 +162,33 @@ internal class AddressPayIdMiddleware {
|
|||
}
|
||||
|
||||
private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) {
|
||||
val addressPayId = input ?: return
|
||||
val address = input ?: return
|
||||
val wallet = appState?.sendState?.walletManager?.wallet ?: return
|
||||
|
||||
val internalDispatcher: (Action) -> Unit = {
|
||||
when (it) {
|
||||
is SetWalletAddress, is SetPayIdWalletAddress -> {
|
||||
dispatch(AddressPayIdVerifyAction.ChangePasteBtnEnableState(true))
|
||||
is SetWalletAddress -> {
|
||||
dispatch(AddressVerifyAction.ChangePasteBtnEnableState(true))
|
||||
}
|
||||
is SetAddressError, is SetPayIdError -> {
|
||||
dispatch(AddressPayIdVerifyAction.ChangePasteBtnEnableState(false))
|
||||
is SetAddressError -> {
|
||||
dispatch(AddressVerifyAction.ChangePasteBtnEnableState(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PayIdManager.isPayId(addressPayId) && isPayIdEnabled()) {
|
||||
verifyPayId(addressPayId, wallet, false, internalDispatcher)
|
||||
} else {
|
||||
verifyAddress(
|
||||
address = addressPayId,
|
||||
wallet = wallet,
|
||||
sourceType = null,
|
||||
isUserInput = false,
|
||||
dispatch = internalDispatcher,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isPayIdEnabled(): Boolean {
|
||||
return store.state.globalState.configManager?.config?.isSendingToPayIdEnabled ?: false
|
||||
verifyAddress(
|
||||
address = address,
|
||||
wallet = wallet,
|
||||
sourceType = null,
|
||||
isUserInput = false,
|
||||
dispatch = internalDispatcher,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.splitToMap(firstDelimiter: String, secondDelimiter: String): Map<String, String> {
|
||||
return this.split(firstDelimiter)
|
||||
return this
|
||||
.split(firstDelimiter)
|
||||
.map { it.split(secondDelimiter) }
|
||||
.map { it.first() to it.last().toString() }
|
||||
.toMap()
|
||||
.associate { it.first() to it.last() }
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ class RequestFeeMiddleware {
|
|||
}
|
||||
val typedAmount = sendState.amountState.amountToExtract ?: return
|
||||
|
||||
val destinationAddress = sendState.addressPayIdState.destinationWalletAddress!!
|
||||
val destinationAddress = sendState.addressState.destinationWalletAddress!!
|
||||
val destinationAmount = Amount(typedAmount, sendState.amountState.amountToSendCrypto)
|
||||
val txSender = if (scanResponse.isDemoCard()) {
|
||||
DemoTransactionSender(walletManager)
|
||||
|
|
|
|||
|
|
@ -55,18 +55,17 @@ class SendMiddleware {
|
|||
{ nextDispatch ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch)
|
||||
is AddressActionUi -> AddressMiddleware().handle(action, appState(), dispatch)
|
||||
is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch)
|
||||
is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch)
|
||||
is SendActionUi.SendAmountToRecipient ->
|
||||
verifyAndSendTransaction(action, appState(), dispatch)
|
||||
is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch)
|
||||
is SendAction.Warnings.Update -> updateWarnings(dispatch)
|
||||
is SendActionUi.CheckIfTransactionDataWasProvided -> {
|
||||
val transactionData = appState()?.sendState?.externalTransactionData
|
||||
if (transactionData != null) {
|
||||
store.dispatchOnMain(
|
||||
AddressPayIdVerifyAction.AddressVerification.SetWalletAddress(
|
||||
AddressVerifyAction.AddressVerification.SetWalletAddress(
|
||||
address = transactionData.destinationAddress,
|
||||
isUserInput = false,
|
||||
),
|
||||
|
|
@ -96,7 +95,7 @@ private fun verifyAndSendTransaction(
|
|||
val sendState = appState?.sendState ?: return
|
||||
val walletManager = sendState.walletManager ?: return
|
||||
val card = appState.globalState.scanResponse?.card ?: return
|
||||
val destinationAddress = sendState.addressPayIdState.destinationWalletAddress ?: return
|
||||
val destinationAddress = sendState.addressState.destinationWalletAddress ?: return
|
||||
val typedAmount = sendState.amountState.amountToExtract ?: return
|
||||
val feeAmount = sendState.feeState.currentFee ?: return
|
||||
|
||||
|
|
@ -391,12 +390,6 @@ fun createValidateTransactionError(
|
|||
return TapError.ValidateTransactionErrors(tapErrors) { it.joinToString("\r\n") }
|
||||
}
|
||||
|
||||
private fun setIfSendingToPayIdEnabled(appState: AppState?, dispatch: (Action) -> Unit) {
|
||||
val isSendingToPayIdEnabled =
|
||||
appState?.globalState?.configManager?.config?.isSendingToPayIdEnabled ?: false
|
||||
dispatch(AddressPayIdActionUi.ChangePayIdState(isSendingToPayIdEnabled))
|
||||
}
|
||||
|
||||
private fun updateWarnings(dispatch: (Action) -> Unit) {
|
||||
val warningsManager = store.state.globalState.warningManager ?: return
|
||||
val blockchain = store.state.sendState.walletManager?.wallet?.blockchain ?: return
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.tap.features.send.redux.reducers
|
||||
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification
|
||||
import com.tangem.tap.features.send.redux.SendScreenAction
|
||||
import com.tangem.tap.features.send.redux.states.AddressPayIdState
|
||||
import com.tangem.tap.features.send.redux.states.InputViewValue
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class AddressPayIdReducer : SendInternalReducer {
|
||||
override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) {
|
||||
is AddressPayIdActionUi -> handleUiAction(action, sendState, sendState.addressPayIdState)
|
||||
is AddressPayIdVerifyAction -> handleAction(action, sendState, sendState.addressPayIdState)
|
||||
else -> sendState
|
||||
}
|
||||
|
||||
private fun handleUiAction(
|
||||
action: AddressPayIdActionUi,
|
||||
sendState: SendState,
|
||||
state: AddressPayIdState,
|
||||
): SendState {
|
||||
val result = when (action) {
|
||||
is AddressPayIdActionUi.HandleUserInput -> state
|
||||
is AddressPayIdActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler)
|
||||
is AddressPayIdActionUi.TruncateOrRestore -> {
|
||||
val value = if (action.truncate) state.truncatedFieldValue ?: "" else state.normalFieldValue ?: ""
|
||||
state.copy(viewFieldValue = state.viewFieldValue.copy(value = value))
|
||||
}
|
||||
is AddressPayIdActionUi.PasteAddressPayId -> return sendState
|
||||
is AddressPayIdActionUi.CheckClipboard -> return sendState
|
||||
is AddressPayIdActionUi.CheckAddressPayId -> return sendState
|
||||
is AddressPayIdActionUi.ChangePayIdState -> state.copy(sendingToPayIdEnabled = action.sendingToPayIdEnabled)
|
||||
}
|
||||
return updateLastState(sendState.copy(addressPayIdState = result), result)
|
||||
}
|
||||
|
||||
private fun handleAction(
|
||||
action: AddressPayIdVerifyAction,
|
||||
sendState: SendState,
|
||||
state: AddressPayIdState,
|
||||
): SendState {
|
||||
val result = when (action) {
|
||||
is PayIdVerification.SetPayIdWalletAddress -> {
|
||||
state.copy(
|
||||
viewFieldValue = InputViewValue(action.payId, action.isUserInput),
|
||||
normalFieldValue = action.payId,
|
||||
truncatedFieldValue = state.truncate(action.payId),
|
||||
destinationWalletAddress = action.payIdWalletAddress,
|
||||
error = null,
|
||||
)
|
||||
}
|
||||
is AddressVerification.SetWalletAddress -> {
|
||||
state.copy(
|
||||
viewFieldValue = InputViewValue(action.address, action.isUserInput),
|
||||
normalFieldValue = action.address,
|
||||
truncatedFieldValue = state.truncate(action.address),
|
||||
destinationWalletAddress = action.address,
|
||||
error = null,
|
||||
)
|
||||
}
|
||||
is AddressPayIdVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled)
|
||||
is AddressVerification.SetAddressError -> state.copy(error = action.error, destinationWalletAddress = null)
|
||||
is PayIdVerification.SetPayIdError -> state.copy(error = action.error, destinationWalletAddress = null)
|
||||
}
|
||||
return updateLastState(sendState.copy(addressPayIdState = result), result)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.tap.features.send.redux.reducers
|
||||
|
||||
import com.tangem.tap.features.send.redux.AddressActionUi
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification
|
||||
import com.tangem.tap.features.send.redux.SendScreenAction
|
||||
import com.tangem.tap.features.send.redux.states.AddressState
|
||||
import com.tangem.tap.features.send.redux.states.InputViewValue
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class AddressReducer : SendInternalReducer {
|
||||
override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) {
|
||||
is AddressActionUi -> handleUiAction(action, sendState, sendState.addressState)
|
||||
is AddressVerifyAction -> handleAction(action, sendState, sendState.addressState)
|
||||
else -> sendState
|
||||
}
|
||||
|
||||
private fun handleUiAction(action: AddressActionUi, sendState: SendState, state: AddressState): SendState {
|
||||
val result = when (action) {
|
||||
is AddressActionUi.HandleUserInput -> state
|
||||
is AddressActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler)
|
||||
is AddressActionUi.TruncateOrRestore -> {
|
||||
val value = if (action.truncate) state.truncatedFieldValue ?: "" else state.normalFieldValue ?: ""
|
||||
state.copy(viewFieldValue = state.viewFieldValue.copy(value = value))
|
||||
}
|
||||
is AddressActionUi.PasteAddress -> return sendState
|
||||
is AddressActionUi.CheckClipboard -> return sendState
|
||||
is AddressActionUi.CheckAddress -> return sendState
|
||||
}
|
||||
return updateLastState(sendState.copy(addressState = result), result)
|
||||
}
|
||||
|
||||
private fun handleAction(action: AddressVerifyAction, sendState: SendState, state: AddressState): SendState {
|
||||
val result = when (action) {
|
||||
is AddressVerification.SetWalletAddress -> {
|
||||
state.copy(
|
||||
viewFieldValue = InputViewValue(action.address, action.isUserInput),
|
||||
normalFieldValue = action.address,
|
||||
truncatedFieldValue = state.truncate(action.address),
|
||||
destinationWalletAddress = action.address,
|
||||
error = null,
|
||||
)
|
||||
}
|
||||
is AddressVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled)
|
||||
is AddressVerification.SetAddressError -> state.copy(error = action.error, destinationWalletAddress = null)
|
||||
}
|
||||
return updateLastState(sendState.copy(addressState = result), result)
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ object SendScreenReducer {
|
|||
|
||||
val reducer: SendInternalReducer = when (action) {
|
||||
is PrepareSendScreen -> PrepareSendScreenStatesReducer()
|
||||
is AddressPayIdActionUi, is AddressPayIdVerifyAction -> AddressPayIdReducer()
|
||||
is AddressActionUi, is AddressVerifyAction -> AddressReducer()
|
||||
is TransactionExtrasAction -> TransactionExtrasReducer()
|
||||
is AmountActionUi, is AmountAction -> AmountReducer()
|
||||
is FeeActionUi, is FeeAction -> FeeReducer()
|
||||
|
|
@ -71,7 +71,7 @@ private class SendReducer : SendInternalReducer {
|
|||
amountState = state.amountState.copy(
|
||||
inputIsEnabled = false,
|
||||
),
|
||||
addressPayIdState = state.addressPayIdState.copy(
|
||||
addressState = state.addressState.copy(
|
||||
inputIsEnabled = false,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,17 +2,16 @@ package com.tangem.tap.features.send.redux.states
|
|||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarMemo
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction
|
||||
import java.math.BigInteger
|
||||
|
||||
data class AddressPayIdState(
|
||||
data class AddressState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val normalFieldValue: String? = null,
|
||||
val truncatedFieldValue: String? = null,
|
||||
val destinationWalletAddress: String? = null,
|
||||
val error: AddressPayIdVerifyAction.Error? = null,
|
||||
val error: AddressVerifyAction.Error? = null,
|
||||
val truncateHandler: ((String) -> String)? = null,
|
||||
val sendingToPayIdEnabled: Boolean = false,
|
||||
val pasteIsEnabled: Boolean = false,
|
||||
val inputIsEnabled: Boolean = true,
|
||||
) : SendScreenState {
|
||||
|
|
@ -22,8 +21,6 @@ data class AddressPayIdState(
|
|||
fun truncate(value: String): String = truncateHandler?.invoke(value) ?: value
|
||||
|
||||
fun isReady(): Boolean = error == null && destinationWalletAddress?.isNotEmpty() ?: false
|
||||
|
||||
fun isPayIdState(): Boolean = destinationWalletAddress != null && destinationWalletAddress != normalFieldValue
|
||||
}
|
||||
|
||||
data class TransactionExtrasState(
|
||||
|
|
@ -98,11 +95,7 @@ data class BinanceMemoState(
|
|||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val memo: BigInteger? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
companion object {
|
||||
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// tag must contains only digits
|
||||
data class XrpDestinationTagState(
|
||||
|
|
@ -34,7 +34,7 @@ data class SendState(
|
|||
val coinConverter: CurrencyConverter? = null,
|
||||
val tokenConverter: CurrencyConverter? = null,
|
||||
val lastChangedStates: LinkedHashSet<StateId> = linkedSetOf(),
|
||||
val addressPayIdState: AddressPayIdState = AddressPayIdState(),
|
||||
val addressState: AddressState = AddressState(),
|
||||
val transactionExtrasState: TransactionExtrasState = TransactionExtrasState(),
|
||||
val amountState: AmountState = AmountState(),
|
||||
val feeState: FeeState = FeeState(),
|
||||
|
|
@ -52,7 +52,7 @@ data class SendState(
|
|||
MainCurrencyType.CRYPTO -> amountState.amountToExtract?.decimals ?: 0
|
||||
}
|
||||
|
||||
fun convertFiatToCoin(value: BigDecimal): BigDecimal {
|
||||
private fun convertFiatToCoin(value: BigDecimal): BigDecimal {
|
||||
return if (!this.coinIsConvertible()) value else coinConverter!!.toCrypto(value)
|
||||
}
|
||||
|
||||
|
|
@ -60,14 +60,14 @@ data class SendState(
|
|||
return if (!this.tokenIsConvertible()) value else tokenConverter!!.toCrypto(value)
|
||||
}
|
||||
|
||||
fun convertCoinToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal {
|
||||
private fun convertCoinToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal {
|
||||
if (!this.coinIsConvertible()) return value
|
||||
|
||||
val converter = coinConverter!!
|
||||
return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value)
|
||||
}
|
||||
|
||||
fun convertTokenToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal {
|
||||
private fun convertTokenToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal {
|
||||
if (!this.tokenIsConvertible()) return value
|
||||
|
||||
val converter = tokenConverter!!
|
||||
|
|
@ -107,13 +107,13 @@ data class SendState(
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun addressPayIdIsReady(): Boolean = store.state.sendState.addressPayIdState.isReady()
|
||||
private fun addressIsReady(): Boolean = store.state.sendState.addressState.isReady()
|
||||
|
||||
fun amountIsReady(): Boolean = store.state.sendState.amountState.isReady()
|
||||
private fun amountIsReady(): Boolean = store.state.sendState.amountState.isReady()
|
||||
|
||||
fun isReadyToRequestFee(): Boolean = addressPayIdIsReady() && amountIsReady()
|
||||
fun isReadyToRequestFee(): Boolean = addressIsReady() && amountIsReady()
|
||||
|
||||
fun isReadyToSend(): Boolean = addressPayIdIsReady() && amountIsReady() &&
|
||||
fun isReadyToSend(): Boolean = addressIsReady() && amountIsReady() &&
|
||||
store.state.sendState.feeState.isReady()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import com.tangem.tap.common.toggleWidget.ViewStateWidget
|
|||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.*
|
||||
import com.tangem.tap.features.send.redux.AddressActionUi.*
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.*
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi.*
|
||||
import com.tangem.tap.features.send.redux.states.FeeType
|
||||
|
|
@ -80,7 +80,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
etAmountToSend = view.findViewById(R.id.etAmountToSend)
|
||||
|
||||
initSendButtonStates()
|
||||
setupAddressOrPayIdLayout()
|
||||
setupAddressLayout()
|
||||
setupTransactionExtrasLayout()
|
||||
setupAmountLayout()
|
||||
setupFeeLayout()
|
||||
|
|
@ -99,14 +99,14 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
sendBtn = IndeterminateProgressButtonWidget(btnSend, progress)
|
||||
}
|
||||
|
||||
private fun setupAddressOrPayIdLayout() = with(binding.lSendAddressPayid) {
|
||||
store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, "...") })
|
||||
private fun setupAddressLayout() = with(binding.lSendAddress) {
|
||||
store.dispatch(SetTruncateHandler { etAddress.truncateMiddleWith(it, "...") })
|
||||
store.dispatch(CheckClipboard(requireContext().getFromClipboard()?.toString()))
|
||||
|
||||
etAddressOrPayId.apply {
|
||||
etAddress.apply {
|
||||
setOnSystemPasteButtonClickListener {
|
||||
store.dispatch(
|
||||
PasteAddressPayId(
|
||||
PasteAddress(
|
||||
data = requireContext().getFromClipboard()?.toString() ?: "",
|
||||
sourceType = Token.Send.AddressEntered.SourceType.PastePopup,
|
||||
),
|
||||
|
|
@ -119,9 +119,9 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
|
||||
inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
.filter { store.state.sendState.addressPayIdState.viewFieldValue.value != it }
|
||||
.filter { store.state.sendState.addressState.viewFieldValue.value != it }
|
||||
.onEach {
|
||||
store.dispatch(AddressPayIdActionUi.HandleUserInput(it))
|
||||
store.dispatch(AddressActionUi.HandleUserInput(it))
|
||||
}
|
||||
.launchIn(mainScope)
|
||||
}
|
||||
|
|
@ -129,12 +129,12 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
imvPaste.setOnClickListener {
|
||||
Analytics.send(Token.Send.ButtonPaste())
|
||||
store.dispatch(
|
||||
PasteAddressPayId(
|
||||
PasteAddress(
|
||||
data = requireContext().getFromClipboard()?.toString() ?: "",
|
||||
sourceType = Token.Send.AddressEntered.SourceType.PasteButton,
|
||||
),
|
||||
)
|
||||
store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused))
|
||||
store.dispatch(TruncateOrRestore(!etAddress.isFocused))
|
||||
}
|
||||
imvQrCode.setOnClickListener {
|
||||
Analytics.send(Token.Send.ButtonQRCode())
|
||||
|
|
@ -145,7 +145,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupTransactionExtrasLayout() = with(binding.lSendAddressPayid) {
|
||||
private fun setupTransactionExtrasLayout() = with(binding.lSendAddress) {
|
||||
// TODO: [REDACTED_TASK_KEY]
|
||||
etXlmMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -202,15 +202,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
// Delayed launch is needed in order for the UI to be drawn and to process the sent events.
|
||||
// If do not use the delay, then etAmount error field is not displayed when
|
||||
// inserting an incorrect amount by shareUri
|
||||
binding.lSendAddressPayid.imvQrCode.postDelayed(
|
||||
binding.lSendAddress.imvQrCode.postDelayed(
|
||||
{
|
||||
store.dispatch(
|
||||
PasteAddressPayId(
|
||||
PasteAddress(
|
||||
data = scannedCode,
|
||||
sourceType = Token.Send.AddressEntered.SourceType.QRCode,
|
||||
),
|
||||
)
|
||||
store.dispatch(TruncateOrRestore(!binding.lSendAddressPayid.etAddressOrPayId.isFocused))
|
||||
store.dispatch(TruncateOrRestore(!binding.lSendAddress.etAddress.isFocused))
|
||||
},
|
||||
200,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,30 +7,15 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.core.text.bold
|
||||
import com.tangem.common.extensions.remove
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.enableError
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.update
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.getMessageString
|
||||
import com.tangem.tap.common.text.DecimalDigitsInputFilter
|
||||
import com.tangem.tap.domain.MultiMessageError
|
||||
import com.tangem.tap.domain.assembleErrors
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
|
||||
import com.tangem.tap.features.send.redux.AddressVerifyAction.Error
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.send.redux.states.AddressPayIdState
|
||||
import com.tangem.tap.features.send.redux.states.AmountState
|
||||
import com.tangem.tap.features.send.redux.states.FeeState
|
||||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptLayoutType
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptState
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.send.redux.states.StateId
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtraError
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.send.redux.states.*
|
||||
import com.tangem.tap.features.send.ui.FeeUiHelper
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.send.ui.dialogs.KaspaWarningDialog
|
||||
|
|
@ -61,7 +46,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
lastChangedStates.forEach {
|
||||
when (it) {
|
||||
StateId.SEND_SCREEN -> handleSendScreen(fg, state)
|
||||
StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState)
|
||||
StateId.ADDRESS_PAY_ID -> handleAddressState(fg, state.addressState)
|
||||
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
|
||||
StateId.AMOUNT -> handleAmountState(fg, state.amountState)
|
||||
StateId.FEE -> handleFeeState(fg, state.feeState)
|
||||
|
|
@ -72,7 +57,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun handleTransactionExtrasState(fg: SendFragment, infoState: TransactionExtrasState) =
|
||||
with(fg.binding.lSendAddressPayid) {
|
||||
with(fg.binding.lSendAddress) {
|
||||
fun showView(view: View, info: Any?) {
|
||||
view.show(info != null)
|
||||
}
|
||||
|
|
@ -192,13 +177,10 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
)
|
||||
}
|
||||
|
||||
private fun handleAddressPayIdState(fg: SendFragment, state: AddressPayIdState) =
|
||||
with(fg.binding.lSendAddressPayid) {
|
||||
private fun handleAddressState(fg: SendFragment, state: AddressState) {
|
||||
with(fg.binding.lSendAddress) {
|
||||
fun parseError(context: Context, error: Error?): String? {
|
||||
val resId = when (error) {
|
||||
Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_error_payid_unsupported_by_blockchain
|
||||
Error.PAY_ID_NOT_REGISTERED -> R.string.send_error_payid_not_registered
|
||||
Error.PAY_ID_REQUEST_FAILED -> R.string.send_error_payid_request_failed
|
||||
Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_validation_invalid_address
|
||||
Error.ADDRESS_SAME_AS_WALLET -> R.string.send_error_address_same_as_wallet
|
||||
else -> null
|
||||
|
|
@ -208,8 +190,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
|
||||
imvPaste.isEnabled = state.pasteIsEnabled
|
||||
|
||||
val et = etAddressOrPayId
|
||||
val til = tilAddressOrPayId
|
||||
val et = etAddress
|
||||
val til = tilAddress
|
||||
val parsedError = parseError(til.context, state.error)
|
||||
|
||||
til.isEnabled = state.inputIsEnabled
|
||||
|
|
@ -218,19 +200,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
flPaste.show(state.inputIsEnabled)
|
||||
flQrCode.show(state.inputIsEnabled)
|
||||
|
||||
val hintResId = if (state.sendingToPayIdEnabled) {
|
||||
R.string.send_destination_hint_address_payid
|
||||
} else {
|
||||
R.string.send_destination_hint_address
|
||||
}
|
||||
til.hint = til.getString(hintResId)
|
||||
til.hint = til.getString(R.string.send_destination_hint_address)
|
||||
til.error = parsedError
|
||||
til.isErrorEnabled = parsedError != null
|
||||
til.helperText = state.destinationWalletAddress
|
||||
til.isHelperTextEnabled = state.isPayIdState() && parsedError == null
|
||||
til.isHelperTextEnabled = parsedError == null
|
||||
|
||||
if (!state.viewFieldValue.isFromUserInput) et.update(state.viewFieldValue.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAmountState(fg: SendFragment, state: AmountState) = with(fg.binding.lSendAmount) {
|
||||
if (state.error != null) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ import android.view.View
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
|
|
@ -22,12 +24,16 @@ import coil.size.Scale
|
|||
import com.badoo.mvicore.modelWatcher
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.ui.fragments.setStatusBarColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.OneTouchClickListener
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.feature.learn2earn.presentation.Learn2earnViewModel
|
||||
import com.tangem.feature.learn2earn.presentation.ui.Learn2earnMainPageScreen
|
||||
import com.tangem.feature.swap.api.SwapFeatureToggleManager
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.common.analytics.events.Portfolio
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -73,6 +79,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
|
|||
|
||||
private var walletView: WalletView = MultiWalletView()
|
||||
|
||||
private val learn2earnViewModel by activityViewModels<Learn2earnViewModel>()
|
||||
private val viewModel by viewModels<WalletViewModel>()
|
||||
|
||||
private val totalBalanceWatcher = modelWatcher {
|
||||
|
|
@ -102,6 +109,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
|
|||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
learn2earnViewModel.onMainScreenCreated()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
|
@ -199,6 +207,27 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
|
|||
binding.toolbar.setNavigationIcon(
|
||||
if (state.canSaveUserWallets) R.drawable.ic_wallet_24 else R.drawable.ic_tap_card_24,
|
||||
)
|
||||
|
||||
showLearn2earnView()
|
||||
}
|
||||
|
||||
private fun showLearn2earnView() {
|
||||
val isShowing = learn2earnViewModel.uiState.mainScreenState.isVisible
|
||||
if (!isShowing) return
|
||||
|
||||
binding.composeLearnToEarnContainer.show(true) { binding.llWarnings.beginDelayedTransition() }
|
||||
binding.composeLearnToEarnContainer.apply {
|
||||
setViewCompositionStrategy(
|
||||
strategy = ViewCompositionStrategy.DisposeOnLifecycleDestroyed(
|
||||
lifecycle = this@WalletFragment.lifecycle,
|
||||
),
|
||||
)
|
||||
setContent {
|
||||
TangemTheme {
|
||||
Learn2earnMainPageScreen(learn2earnViewModel.uiState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPullToRefreshLayout(state: WalletState) {
|
||||
|
|
@ -239,6 +268,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
|
|||
private fun refreshWalletData() {
|
||||
Analytics.send(Portfolio.Refreshed())
|
||||
store.dispatch(WalletAction.LoadData.Refresh)
|
||||
learn2earnViewModel.onMainScreenRefreshed()
|
||||
}
|
||||
|
||||
private fun showWarningsIfPresent(warnings: List<WarningMessage>) {
|
||||
|
|
|
|||
|
|
@ -17,16 +17,8 @@ import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -71,6 +63,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
currency = currency,
|
||||
batch = scanResponse.card.batchId,
|
||||
signInType = signInType,
|
||||
walletsCount = store.state.globalState.userWalletsListManager?.walletsCount.toString(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ internal fun WalletDataModel.getAvailableActions(
|
|||
|
||||
internal fun WalletDataModel.shouldShowMultipleAddress(): Boolean {
|
||||
val listOfAddresses = walletAddresses?.list.orEmpty()
|
||||
return listOfAddresses.size > 1 && currency.blockchain != Blockchain.BitcoinCash
|
||||
return listOfAddresses.size > 1
|
||||
}
|
||||
|
||||
internal fun WalletDataModel.assembleWarnings(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ internal sealed interface WelcomeAction : Action {
|
|||
data class Error(val error: TangemError) : WelcomeAction
|
||||
}
|
||||
|
||||
data class HandleIntentIfNeeded(val intent: Intent?) : WelcomeAction
|
||||
data class SetInitialIntent(val intent: Intent?) : WelcomeAction
|
||||
|
||||
object CloseError : WelcomeAction
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.welcome.redux
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnResult
|
||||
|
|
@ -19,6 +18,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
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 kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -39,24 +39,10 @@ internal class WelcomeMiddleware {
|
|||
|
||||
private fun handleAction(action: WelcomeAction, state: WelcomeState) {
|
||||
when (action) {
|
||||
is WelcomeAction.ProceedWithBiometrics -> {
|
||||
proceedWithBiometrics(state)
|
||||
}
|
||||
is WelcomeAction.ProceedWithCard -> {
|
||||
proceedWithCard(state)
|
||||
}
|
||||
is WelcomeAction.HandleIntentIfNeeded -> {
|
||||
handleInitialIntent(action.intent)
|
||||
}
|
||||
is WelcomeAction.ClearUserWallets -> {
|
||||
disableUserWalletsSaving()
|
||||
}
|
||||
is WelcomeAction.ProceedWithBiometrics.Error,
|
||||
is WelcomeAction.ProceedWithCard.Error,
|
||||
is WelcomeAction.ProceedWithBiometrics.Success,
|
||||
is WelcomeAction.ProceedWithCard.Success,
|
||||
is WelcomeAction.CloseError,
|
||||
-> Unit
|
||||
is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics(state)
|
||||
is WelcomeAction.ProceedWithCard -> proceedWithCard(state)
|
||||
is WelcomeAction.ClearUserWallets -> disableUserWalletsSaving()
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +71,9 @@ internal class WelcomeMiddleware {
|
|||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Success)
|
||||
store.onUserWalletSelected(userWallet = selectedUserWallet)
|
||||
|
||||
intentHandler.handleWalletConnectLink(state.intent)
|
||||
state.intent?.let {
|
||||
WalletConnectLinkIntentHandler().handleIntent(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,20 +92,13 @@ internal class WelcomeMiddleware {
|
|||
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
|
||||
store.onUserWalletSelected(userWallet = userWallet)
|
||||
|
||||
intentHandler.handleWalletConnectLink(state.intent)
|
||||
state.intent?.let {
|
||||
WalletConnectLinkIntentHandler().handleIntent(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInitialIntent(intent: Intent?) {
|
||||
val isBackgroundScanNotHandled = !intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true)
|
||||
val hasNotIncompletedBackup = !backupService.hasIncompletedBackup
|
||||
|
||||
if (isBackgroundScanNotHandled && hasNotIncompletedBackup) {
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) {
|
||||
tangemSdkManager.setAccessCodeRequestPolicy(
|
||||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ internal object WelcomeReducer {
|
|||
|
||||
private fun internalReduce(action: WelcomeAction, state: WelcomeState): WelcomeState {
|
||||
return when (action) {
|
||||
is WelcomeAction.HandleIntentIfNeeded -> state.copy(intent = action.intent)
|
||||
is WelcomeAction.SetInitialIntent -> state.copy(intent = action.intent)
|
||||
is WelcomeAction.ProceedWithBiometrics -> state.copy(isUnlockWithBiometricsInProgress = true)
|
||||
is WelcomeAction.ProceedWithCard -> state.copy(isUnlockWithCardInProgress = true)
|
||||
is WelcomeAction.ProceedWithBiometrics.Error -> state.copy(
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
package com.tangem.tap.network.payid
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface PayIdVerifyApi {
|
||||
@GET("{user}")
|
||||
suspend fun verifyAddress(
|
||||
@Path("user") user: String,
|
||||
@Header("Accept") acceptNetworkHeader: String,
|
||||
@Header("PayID-Version") payIdVersion: String = "1.0",
|
||||
): VerifyPayIdResponse
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class VerifyPayIdResponse(
|
||||
val addresses: List<PayIdAddress> = mutableListOf(),
|
||||
val payId: String? = null,
|
||||
) {
|
||||
fun getAddressDetails(): PayIdAddressDetails? = if (addresses.isNotEmpty()) addresses[0].addressDetails else null
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PayIdAddress(
|
||||
var paymentNetwork: String,
|
||||
var environment: String,
|
||||
var addressDetailsType: String,
|
||||
var addressDetails: PayIdAddressDetails,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PayIdAddressDetails(
|
||||
var address: String,
|
||||
var tag: String? = null,
|
||||
)
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.tap.network.payid
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.datasource.api.common.createRetrofitInstance
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PayIdVerifyService(
|
||||
private val baseUrl: String,
|
||||
) {
|
||||
|
||||
private val api = createRetrofitInstance(
|
||||
baseUrl = baseUrl,
|
||||
logEnabled = false,
|
||||
).create(PayIdVerifyApi::class.java)
|
||||
|
||||
suspend fun verifyAddress(user: String, network: String): Result<VerifyPayIdResponse> {
|
||||
return performRequest { api.verifyAddress(user, createNetworkHeader(network)) }
|
||||
}
|
||||
|
||||
private fun createNetworkHeader(network: String): String = "application/$network-mainnet+json"
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.proxy.di
|
||||
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider
|
||||
import com.tangem.lib.crypto.DerivationManager
|
||||
import com.tangem.lib.crypto.TransactionManager
|
||||
import com.tangem.lib.crypto.UserWalletManager
|
||||
|
|
@ -48,4 +50,20 @@ class ProxyModule {
|
|||
appStateHolder = appStateHolder,
|
||||
)
|
||||
}
|
||||
|
||||
// regions FeatureConsumers
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLear2earnDependencies(appStateHolder: AppStateHolder): Learn2earnDependencyProvider {
|
||||
return object : Learn2earnDependencyProvider {
|
||||
override fun getUserCountryCodeProvider(): () -> String = {
|
||||
appStateHolder.mainStore?.state?.globalState?.userCountryCode ?: Locale.current.language
|
||||
}
|
||||
|
||||
override fun getWebViewAuthCredentialsProvider(): () -> String? = {
|
||||
appStateHolder.mainStore?.state?.globalState?.configManager?.config?.tangemComAuthorization
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion FeatureConsumers
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux
|
|||
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -13,5 +14,6 @@ sealed interface DaggerGraphAction : Action {
|
|||
val scanCardUseCase: ScanCardUseCase,
|
||||
val walletRouter: WalletRouter,
|
||||
val walletConnectInteractor: WalletConnectInteractor,
|
||||
val tokenDetailsRouter: TokenDetailsRouter,
|
||||
) : DaggerGraphAction
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ object DaggerGraphReducer {
|
|||
scanCardUseCase = action.scanCardUseCase,
|
||||
walletRouter = action.walletRouter,
|
||||
walletConnectInteractor = action.walletConnectInteractor,
|
||||
tokenDetailsRouter = action.tokenDetailsRouter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import com.tangem.datasource.asset.AssetReader
|
|||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
|
|
@ -23,6 +25,8 @@ data class DaggerGraphState(
|
|||
val walletConnectRepository: WalletConnectRepository? = null,
|
||||
val walletConnectSessionsRepository: WalletConnectSessionsRepository? = null,
|
||||
val walletConnectInteractor: WalletConnectInteractor? = null,
|
||||
val tokenDetailsFeatureToggles: TokenDetailsFeatureToggles? = null,
|
||||
val tokenDetailsRouter: TokenDetailsRouter? = null,
|
||||
) : StateType {
|
||||
|
||||
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {
|
||||
|
|
|
|||
12
app/src/main/res/drawable/ic_azero_no_color.xml
Normal file
12
app/src/main/res/drawable/ic_azero_no_color.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#EBEBEB"/>
|
||||
<path
|
||||
android:pathData="M14.927,11.319H16.829C16.874,11.319 16.918,11.302 16.95,11.271C16.982,11.24 17,11.198 17,11.154V9.834C17,9.79 16.982,9.748 16.95,9.717C16.918,9.686 16.874,9.669 16.829,9.669H14.164L12.064,5.131C12.046,5.092 12.017,5.059 11.98,5.035C11.943,5.012 11.9,5 11.856,5H10.144C10.1,5 10.057,5.012 10.02,5.035C9.983,5.059 9.954,5.092 9.936,5.131L7.836,9.669H5.171C5.126,9.669 5.082,9.686 5.05,9.717C5.018,9.748 5,9.79 5,9.834V11.154C5,11.198 5.018,11.24 5.05,11.271C5.082,11.302 5.126,11.319 5.171,11.319H7.073L5.015,15.768C5.003,15.793 4.998,15.821 5.001,15.848C5.003,15.875 5.012,15.902 5.028,15.925C5.043,15.948 5.065,15.967 5.09,15.98C5.115,15.993 5.143,16 5.172,16H6.702C6.746,16 6.79,15.988 6.827,15.965C6.864,15.941 6.893,15.908 6.911,15.87L11,7.03L15.089,15.87C15.107,15.908 15.136,15.941 15.173,15.965C15.21,15.988 15.254,16 15.298,16H16.828C16.857,16 16.885,15.993 16.91,15.98C16.935,15.967 16.957,15.948 16.972,15.925C16.988,15.902 16.997,15.875 16.999,15.848C17.001,15.821 16.997,15.793 16.985,15.768L14.927,11.319Z"
|
||||
android:fillColor="#B0B0B0"/>
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/ic_telos_no_color.xml
Normal file
15
app/src/main/res/drawable/ic_telos_no_color.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#EBEBEB"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M14.862,5.607C14.909,5.486 14.825,5.354 14.695,5.346L12.507,5.2C12.44,5.196 12.375,5.227 12.336,5.283L11.756,6.118C11.72,6.17 11.66,6.201 11.597,6.201L7.745,6.178C7.556,6.177 7.373,6.245 7.231,6.37L6.163,7.308C5.925,7.517 5.839,7.849 5.945,8.147L6.454,9.575C6.457,9.583 6.455,9.591 6.451,9.597C6.165,9.977 5.998,10.434 5.973,10.909L5.964,11.071C5.91,12.08 6.029,13.091 6.316,14.061C6.552,14.859 6.823,15.775 6.989,16.336C7.076,16.632 7.379,16.807 7.679,16.735C8.325,16.58 9.435,16.313 10.309,16.102C11.078,15.917 11.809,15.602 12.472,15.17L12.886,14.901C13.285,14.641 13.597,14.269 13.784,13.831C13.787,13.824 13.793,13.818 13.801,13.817L15.292,13.544C15.603,13.487 15.848,13.246 15.91,12.936L16.188,11.541C16.225,11.356 16.193,11.163 16.097,11L14.288,7.909C14.17,7.706 14.15,7.461 14.234,7.242L14.862,5.607Z"
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#B0B0B0"/>
|
||||
</vector>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="16dp"
|
||||
android:text="@string/wallet_button_buy"
|
||||
android:text="@string/common_buy"
|
||||
android:textColor="@color/darkGray3"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="16dp"
|
||||
android:text="@string/wallet_button_sell"
|
||||
android:text="@string/common_sell"
|
||||
android:textColor="@color/darkGray3"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
|
||||
app:title="@string/send_title" />
|
||||
app:title="@string/common_send" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
|
@ -39,8 +39,8 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/l_send_address_payid"
|
||||
layout="@layout/layout_send_address_payid"
|
||||
android:id="@+id/l_send_address"
|
||||
layout="@layout/layout_send_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
style="@style/TapPrimaryIconButton"
|
||||
android:layout_width="match_parent"
|
||||
android:fontFamily="@font/saira_semi_condensed_regular"
|
||||
android:text="@string/send_title"
|
||||
android:text="@string/common_send"
|
||||
app:icon="@drawable/ic_arrow_right" />
|
||||
|
||||
<ProgressBar
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@
|
|||
android:id="@+id/cl_wallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="92dp">
|
||||
|
||||
<ImageView
|
||||
|
|
@ -113,6 +113,12 @@
|
|||
android:paddingTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages">
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/compose_learn_to_earn_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_wallet_rescan_warning"
|
||||
layout="@layout/layout_wallet_rescan_warning"
|
||||
|
|
@ -219,8 +225,8 @@
|
|||
android:id="@+id/btn_add_token"
|
||||
style="@style/TapPrimaryButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/main_manage_tokens"
|
||||
android:visibility="gone"
|
||||
|
|
|
|||
|
|
@ -150,63 +150,6 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/tv_explore"
|
||||
tools:text="Send only Ethereum (ETH) from Ethereum network to this address. Using other tokens and networks may result in loss of funds." />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_payid_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@color/lightGray5"
|
||||
android:layout_marginTop="25dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/btn_copy" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_payid_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:src="@drawable/ic_payid"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_payid_divider"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_create_payid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="9dp"
|
||||
android:padding="16dp"
|
||||
android:text="@string/wallet_address_button_create_payid"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:drawableEndCompat="@drawable/ic_angle_bracket_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_payid_divider" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payid_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:textAlignment="textEnd"
|
||||
android:textColor="@color/darkGray1"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_payid_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_payid_divider"
|
||||
tools:text="romafdffdfdfn$payid.tangem.com" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/group_payid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="v_payid_divider, iv_payid_icon, tv_payid_address, tv_create_payid" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
|
|
|||
|
|
@ -18,10 +18,9 @@
|
|||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilAddressOrPayId"
|
||||
android:id="@+id/tilAddress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/send_destination_hint_address_payid"
|
||||
app:boxBackgroundColor="@color/backgroundLightGray"
|
||||
app:errorIconDrawable="@null"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -29,7 +28,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.tangem.tap.features.send.ui.EditTextCustomPaste
|
||||
android:id="@+id/etAddressOrPayId"
|
||||
android:id="@+id/etAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/backgroundLightGray"
|
||||
|
|
@ -71,7 +70,7 @@
|
|||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/shape_ellipse"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/tilAddressOrPayId">
|
||||
app:layout_constraintTop_toTopOf="@+id/tilAddress">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imvQrCode"
|
||||
|
|
@ -104,41 +103,6 @@
|
|||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<!-- <com.google.android.material.chip.ChipGroup-->
|
||||
<!-- android:id="@+id/groupMemo"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="8dp"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
||||
<!-- app:selectionRequired="true"-->
|
||||
<!-- app:singleLine="true"-->
|
||||
<!-- app:singleSelection="true">-->
|
||||
|
||||
<!-- <com.google.android.material.chip.Chip-->
|
||||
<!-- android:id="@+id/chipMemoText"-->
|
||||
<!-- style="@style/TapChip"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:text="Text" />-->
|
||||
|
||||
<!-- <com.google.android.material.chip.Chip-->
|
||||
<!-- android:id="@+id/chipMemoId"-->
|
||||
<!-- style="@style/TapChip"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:text="ID" />-->
|
||||
|
||||
<!-- <com.google.android.material.chip.Chip-->
|
||||
<!-- android:id="@+id/chipMemoHash"-->
|
||||
<!-- style="@style/TapChip"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:text="Hash" />-->
|
||||
|
||||
<!-- </com.google.android.material.chip.ChipGroup>-->
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilXlmMemo"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
android:id="@+id/amountContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tilAddressOrPayId">
|
||||
app:layout_constraintTop_toBottomOf="@+id/tilAddress">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/flAmountToSend"
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@
|
|||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_buy"
|
||||
style="@style/TapPrimaryIconButton"
|
||||
android:text="@string/wallet_button_buy"
|
||||
android:text="@string/common_buy"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_arrow_up" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_sell"
|
||||
style="@style/TapPrimaryIconButton"
|
||||
android:text="@string/wallet_button_sell"
|
||||
android:text="@string/common_sell"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_arrow_down" />
|
||||
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
style="@style/TapPrimaryIconButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/wallet_button_send"
|
||||
android:text="@string/common_send"
|
||||
app:icon="@drawable/ic_send" />
|
||||
|
||||
</merge>
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
package com.tangem;
|
||||
|
||||
public class Test2 {
|
||||
}
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
package com.tangem.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.SharedPreferences
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.text.Editable
|
||||
import android.text.Html
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.core.os.bundleOf
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.tangem_card.data.TangemCard
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID
|
||||
import com.tangem.tangem_sdk.data.loadFromBundle
|
||||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.fragment.pin.PinRequestFragment
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.CoinEngine
|
||||
import com.tangem.wallet.CoinEngineFactory
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.TangemContext
|
||||
import kotlinx.android.synthetic.tangemAccess.fragment_confirm_transaction.*
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class ConfirmTransactionFragment : BaseFragment(), NavigationResultListener, NfcAdapter.ReaderCallback {
|
||||
|
||||
override val layoutId = R.layout.fragment_confirm_transaction
|
||||
|
||||
private lateinit var sp: SharedPreferences
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var amount: CoinEngine.Amount
|
||||
|
||||
private var isIncludeFee: Boolean = true
|
||||
private var requestPIN2Count = 0
|
||||
private var nodeCheck = true
|
||||
private var dtVerified: Date? = null
|
||||
|
||||
private var blockchainCallbacks: CoinEngine.BlockchainRequestsCallbacks? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
sp = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
ctx = TangemContext.loadFromBundle(requireContext(), arguments)
|
||||
|
||||
val callback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
navigateUp()
|
||||
}
|
||||
}
|
||||
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(engine!!.balanceHTML, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(engine!!.balanceHTML)
|
||||
tvBalance.text = html
|
||||
|
||||
isIncludeFee = arguments?.getBoolean(Constant.EXTRA_FEE_INCLUDED, true) ?: true
|
||||
|
||||
if (isIncludeFee)
|
||||
tvIncFee.setText(R.string.confirm_transaction_including_fee)
|
||||
else
|
||||
tvIncFee.setText(R.string.confirm_transaction_not_including_fee)
|
||||
|
||||
amount = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_AMOUNT) ?: "0",
|
||||
arguments?.getString(Constant.EXTRA_AMOUNT_CURRENCY) ?: "")
|
||||
|
||||
if (engine.allowSelectFeeInclusion())
|
||||
tvIncFee.visibility = View.VISIBLE
|
||||
else
|
||||
tvIncFee.visibility = View.INVISIBLE
|
||||
|
||||
if (ctx.card.blockchainID == Blockchain.Token.id) {
|
||||
// for Blockchain.Token limit decimals
|
||||
etAmount.setText(amount.toValueString(ctx.card.tokensDecimal))
|
||||
} else {
|
||||
// for others
|
||||
etAmount.setText(amount.toValueString())
|
||||
}
|
||||
|
||||
tvCurrency.text = engine.balanceCurrency
|
||||
tvCurrency2.text = engine.feeCurrency
|
||||
tvCardID.text = ctx.card.cidDescription
|
||||
etWallet.setText(arguments?.getString(Constant.EXTRA_TARGET_ADDRESS))
|
||||
|
||||
btnSend.visibility = View.INVISIBLE
|
||||
|
||||
if (!engine.allowSelectFeeLevel()) {
|
||||
rgFee.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
etFee.isEnabled = sp.getBoolean(getString(R.string.pref_manual_editing_fee), false)
|
||||
|
||||
// set listeners
|
||||
rgFee.setOnCheckedChangeListener { _, checkedId -> doSetFee(checkedId) }
|
||||
etFee.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
try {
|
||||
val eqFee = engine.evaluateFeeEquivalent(etFee!!.text.toString())
|
||||
tvFeeEquivalent.text = eqFee
|
||||
|
||||
if (!ctx.coinData!!.amountEquivalentDescriptionAvailable) {
|
||||
tvFeeEquivalent.error = getString(R.string.confirm_transaction_error_service_unavailable)
|
||||
tvCurrency2.visibility = View.GONE
|
||||
tvFeeEquivalent.visibility = View.GONE
|
||||
} else
|
||||
tvFeeEquivalent.error = null
|
||||
|
||||
if (sp.getBoolean(getString(R.string.pref_manual_editing_fee), false))
|
||||
(activity as MainActivity).toastHelper
|
||||
.showSingleToast(context, getString(R.string.confirm_transaction_warning_risk_delaying))
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
tvFeeEquivalent.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
|
||||
}
|
||||
})
|
||||
btnSend.setOnClickListener {
|
||||
if (UtilHelper.isOnline(requireContext())) {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.add(Calendar.MINUTE, -1)
|
||||
|
||||
if (dtVerified == null || dtVerified!!.before(calendar.time)) {
|
||||
finishWithError(Activity.RESULT_CANCELED, getString(R.string.confirm_transaction_error_data_is_outdated))
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val engineCoin = CoinEngineFactory.create(ctx)
|
||||
|
||||
if (engineCoin!!.isNeedCheckNode && !nodeCheck) {
|
||||
Toast.makeText(context, getString(R.string.confirm_transaction_error_cannot_reach_node), Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val txFee = engineCoin.convertToAmount(etFee.text.toString(), tvCurrency2.text.toString())
|
||||
val txAmount = engineCoin.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
|
||||
|
||||
if (!engineCoin.hasBalanceInfo()) {
|
||||
finishWithError(Activity.RESULT_CANCELED, getString(R.string.confirm_transaction_error_cannot_check_balance))
|
||||
return@setOnClickListener
|
||||
|
||||
} else if (!engineCoin.isBalanceNotZero) {
|
||||
finishWithError(Activity.RESULT_CANCELED, getString(R.string.general_wallet_empty))
|
||||
return@setOnClickListener
|
||||
|
||||
} else if (!engineCoin.isExtractPossible) {
|
||||
finishWithError(Activity.RESULT_CANCELED, getString(R.string.confirm_transaction_error_incoming_transaction_unconfirmed))
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (!engineCoin.checkNewTransactionAmountAndFee(txAmount, txFee, isIncludeFee)) {
|
||||
finishWithError(Activity.RESULT_CANCELED, getString(R.string.prepare_transaction_error_not_enough_funds))
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
requestPIN2Count = 0
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MODE, PinRequestFragment.Mode.RequestPIN2.toString())
|
||||
ctx.saveToBundle(data)
|
||||
data.putBoolean(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
|
||||
navigateForResult(Constant.REQUEST_CODE_REQUEST_PIN2_, R.id.action_confirmTransactionFragment_to_pinRequestFragment, data)
|
||||
} else
|
||||
Toast.makeText(context, getString(R.string.general_error_no_connection), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
if (!navigatedBack) requestFee()
|
||||
}
|
||||
|
||||
private fun requestFee() {
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
coinEngine!!.requestFee(
|
||||
object : CoinEngine.BlockchainRequestsCallbacks {
|
||||
override fun onComplete(success: Boolean) {
|
||||
if (success) {
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
dtVerified = Date()
|
||||
doSetFee(rgFee?.checkedRadioButtonId ?: R.id.rbNormalFee)
|
||||
} else {
|
||||
finishWithError(Activity.RESULT_CANCELED, ctx.error)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProgress() {
|
||||
}
|
||||
|
||||
override fun allowAdvance(): Boolean {
|
||||
return UtilHelper.isOnline(requireContext())
|
||||
}
|
||||
},
|
||||
etWallet.text.toString(),
|
||||
amount)
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
Log.d("LIFECYCLE", "NavigationResult assessed ${this::class.java.simpleName}")
|
||||
if (requestCode == Constant.REQUEST_CODE_SIGN_TRANSACTION) {
|
||||
if (data != null) {
|
||||
if (data.containsKey(EXTRA_TANGEM_CARD_UID) && data.containsKey(EXTRA_TANGEM_CARD)) {
|
||||
val updatedCard = TangemCard(data.getString(EXTRA_TANGEM_CARD_UID))
|
||||
updatedCard.loadFromBundle(data.getBundle(EXTRA_TANGEM_CARD))
|
||||
ctx.card = updatedCard
|
||||
}
|
||||
}
|
||||
if (resultCode == Constant.RESULT_INVALID_PIN_ && requestPIN2Count < 2) {
|
||||
requestPIN2Count++
|
||||
val bundle = Bundle()
|
||||
bundle.putString(Constant.EXTRA_MODE, PinRequestFragment.Mode.RequestPIN2.toString())
|
||||
ctx.saveToBundle(bundle)
|
||||
bundle.putBoolean(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
|
||||
navigateForResult(Constant.REQUEST_CODE_REQUEST_PIN2_,
|
||||
R.id.action_confirmTransactionFragment_to_pinRequestFragment,
|
||||
bundle)
|
||||
return
|
||||
}
|
||||
navigateBackWithResult(resultCode, data)
|
||||
} else if (requestCode == Constant.REQUEST_CODE_REQUEST_PIN2_) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val bundle = Bundle()
|
||||
ctx.saveToBundle(bundle)
|
||||
bundle.putString(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
|
||||
bundle.putString(Constant.EXTRA_AMOUNT, etAmount.text.toString())
|
||||
bundle.putString(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
|
||||
bundle.putString(Constant.EXTRA_FEE, etFee.text.toString())
|
||||
bundle.putString(Constant.EXTRA_FEE_CURRENCY, tvCurrency2.text.toString())
|
||||
bundle.putBoolean(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
|
||||
navigateForResult(Constant.REQUEST_CODE_SIGN_TRANSACTION,
|
||||
R.id.action_confirmTransactionFragment_to_signTransactionFragment,
|
||||
bundle)
|
||||
} else
|
||||
Toast.makeText(context, R.string.confirm_transaction_error_pin_2_is_required, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
(activity as MainActivity).nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doSetFee(checkedRadioButtonId: Int) {
|
||||
var txtFee = ""
|
||||
when (checkedRadioButtonId) {
|
||||
R.id.rbMinimalFee ->
|
||||
if (ctx.coinData.minFee != null) {
|
||||
txtFee = ctx.coinData.minFee!!.toValueString()
|
||||
btnSend?.visibility = View.VISIBLE
|
||||
} else
|
||||
btnSend?.visibility = View.INVISIBLE
|
||||
|
||||
R.id.rbNormalFee ->
|
||||
if (ctx.coinData.normalFee != null) {
|
||||
txtFee = ctx.coinData.normalFee!!.toValueString()
|
||||
btnSend?.visibility = View.VISIBLE
|
||||
} else
|
||||
btnSend?.visibility = View.INVISIBLE
|
||||
|
||||
R.id.rbMaximumFee ->
|
||||
if (ctx.coinData.maxFee != null) {
|
||||
txtFee = ctx.coinData.maxFee!!.toValueString()
|
||||
btnSend?.visibility = View.VISIBLE
|
||||
} else
|
||||
btnSend?.visibility = View.INVISIBLE
|
||||
}
|
||||
etFee?.setText(txtFee.replace(',', '.'))
|
||||
}
|
||||
|
||||
private fun finishWithError(errorCode: Int, message: String) {
|
||||
navigateBackWithResult(
|
||||
errorCode,
|
||||
bundleOf(Constant.EXTRA_MESSAGE to message),
|
||||
R.id.loadedWalletFragment)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
package com.tangem.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.isPayIdSupported
|
||||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.fragment.qr.CameraPermissionManager
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.util.extensions.isStart2CoinCard
|
||||
import com.tangem.wallet.CoinEngineFactory
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.TangemContext
|
||||
import kotlinx.android.synthetic.tangemAccess.fragment_prepare_transaction.*
|
||||
import java.io.IOException
|
||||
|
||||
class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, NfcAdapter.ReaderCallback {
|
||||
companion object {
|
||||
val TAG: String = PrepareTransactionFragment::class.java.simpleName
|
||||
}
|
||||
|
||||
override val layoutId = R.layout.fragment_prepare_transaction
|
||||
|
||||
private val ctx: TangemContext by lazy { TangemContext.loadFromBundle(context, arguments) }
|
||||
private val cameraPermissionManager: CameraPermissionManager by lazy { CameraPermissionManager(this) }
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
tvCardID.text = ctx.card?.cidDescription
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(engine!!.balanceHTML, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(engine!!.balanceHTML)
|
||||
tvBalance.text = html
|
||||
|
||||
if (ctx.blockchain.isPayIdSupported() && !ctx.card.isStart2CoinCard()) {
|
||||
etWallet.hint = getString(R.string.prepare_transaction_hint_address_or_pay_id)
|
||||
}
|
||||
|
||||
if (!engine.allowSelectFeeInclusion()) {
|
||||
rgIncFee.visibility = View.INVISIBLE
|
||||
} else {
|
||||
rgIncFee.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (ctx.card!!.remainingSignatures < 2) {
|
||||
etAmount.isEnabled = false
|
||||
}
|
||||
|
||||
if (ctx.card.remainingSignatures == 1) {
|
||||
androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.prepare_transaction_warning_last_signature)
|
||||
.setMessage(R.string.prepare_transaction_warning_send_full_amount)
|
||||
.setPositiveButton(R.string.general_ok) { _, _ -> }
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
tvCurrency.text = engine.balance.currency
|
||||
etAmount.setText(engine.balance.toValueString())
|
||||
|
||||
// limit number of symbols after comma
|
||||
etAmount.filters = engine.amountInputFilters
|
||||
|
||||
// set listeners
|
||||
etAmount.setOnEditorActionListener { lv, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
val imm = lv.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(lv.windowToken, 0)
|
||||
lv.clearFocus()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
btnVerify.setOnClickListener {
|
||||
if (!UtilHelper.isOnline(requireContext())) {
|
||||
Toast.makeText(context, R.string.general_error_no_connection, Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val engine1 = CoinEngineFactory.create(ctx)
|
||||
val strAmount: String = etAmount.text.toString().replace(",", ".")
|
||||
val amount = engine1!!.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
|
||||
|
||||
try {
|
||||
if (!engine.checkNewTransactionAmount(amount))
|
||||
etAmount.error = getString(R.string.prepare_transaction_error_not_enough_funds)
|
||||
else
|
||||
etAmount.error = null
|
||||
} catch (e: Exception) {
|
||||
etAmount.error = getString(R.string.prepare_transaction_error_unknown_amount_format)
|
||||
}
|
||||
|
||||
// check wallet address
|
||||
if (!engine1.validateAddress(etWallet.text.toString())) {
|
||||
etWallet.error = getString(R.string.prepare_transaction_error_incorrect_destination)
|
||||
return@setOnClickListener
|
||||
} else
|
||||
etWallet.error = null
|
||||
|
||||
if (etWallet.text.toString() == ctx.coinData!!.wallet) {
|
||||
etWallet.error = getString(R.string.prepare_transaction_error_same_address)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (!etAmount.error.isNullOrEmpty() || !etWallet.error.isNullOrEmpty()) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val data = Bundle()
|
||||
ctx.saveToBundle(data)
|
||||
data.putString(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
|
||||
data.putBoolean(Constant.EXTRA_FEE_INCLUDED, (rgIncFee!!.checkedRadioButtonId == R.id.rbFeeIn))
|
||||
data.putString(Constant.EXTRA_AMOUNT, strAmount)
|
||||
data.putString(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
|
||||
navigateForResult(
|
||||
Constant.REQUEST_CODE_SEND_TRANSACTION__,
|
||||
R.id.action_prepareTransactionFragment_to_confirmTransactionFragment,
|
||||
data)
|
||||
}
|
||||
|
||||
ivCamera.setOnClickListener {
|
||||
if (cameraPermissionManager.isPermissionGranted()) {
|
||||
navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareTransactionFragment_to_qrScanFragment)
|
||||
} else {
|
||||
cameraPermissionManager.requirePermission()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
cameraPermissionManager.handleRequestPermissionResult(requestCode, grantResults) {
|
||||
navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareTransactionFragment_to_qrScanFragment)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
if (requestCode == Constant.REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.containsKey("QRCode")) {
|
||||
val code = data.getString("QRCode")
|
||||
val schemeSplit = code!!.split(":")
|
||||
when (schemeSplit.size) {
|
||||
2 -> {
|
||||
if (schemeSplit[0] == ctx.blockchain.uriScheme) {
|
||||
etWallet?.setText(schemeSplit[1])
|
||||
} else {
|
||||
etWallet?.setText(code)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
etWallet?.setText(code)
|
||||
}
|
||||
}
|
||||
} else if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION__) {
|
||||
navigateBackWithResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
(activity as MainActivity).nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,312 +0,0 @@
|
|||
package com.tangem.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.media.MediaPlayer
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.tangem_card.reader.CardProtocol
|
||||
import com.tangem.tangem_card.tasks.SignTask
|
||||
import com.tangem.tangem_card.util.Util
|
||||
import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.tangem_sdk.android.reader.NfcReader
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID
|
||||
import com.tangem.tangem_sdk.data.asBundle
|
||||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.util.Analytics
|
||||
import com.tangem.util.AnalyticsEvent
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.wallet.CoinEngine
|
||||
import com.tangem.wallet.CoinEngineFactory
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.TangemContext
|
||||
import kotlinx.android.synthetic.main.layout_progress_horizontal.*
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
import kotlinx.android.synthetic.tangemAccess.fragment_sign_transaction.*
|
||||
|
||||
|
||||
class SignTransactionFragment : BaseFragment(), NavigationResultListener,
|
||||
NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
|
||||
companion object {
|
||||
val TAG: String = SignTransactionFragment::class.java.simpleName
|
||||
}
|
||||
|
||||
override val layoutId = R.layout.fragment_sign_transaction
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var mpFinishSignSound: MediaPlayer
|
||||
|
||||
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
|
||||
|
||||
private var signTransactionTask: SignTask? = null
|
||||
|
||||
private lateinit var amount: CoinEngine.Amount
|
||||
private lateinit var fee: CoinEngine.Amount
|
||||
private var isIncludeFee = true
|
||||
private var outAddressStr: String? = null
|
||||
private var lastReadSuccess = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(context, arguments)
|
||||
|
||||
val callback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED)
|
||||
}
|
||||
}
|
||||
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound)
|
||||
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
||||
amount = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_AMOUNT), arguments?.getString(Constant.EXTRA_AMOUNT_CURRENCY))
|
||||
fee = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_FEE), arguments?.getString(Constant.EXTRA_FEE_CURRENCY))
|
||||
isIncludeFee = arguments?.getBoolean(Constant.EXTRA_FEE_INCLUDED, true) ?: true
|
||||
outAddressStr = arguments?.getString(Constant.EXTRA_TARGET_ADDRESS)
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
|
||||
FirebaseAnalytics.getInstance(requireActivity())
|
||||
.logEvent(AnalyticsEvent.READY_TO_SIGN.event, Analytics.setCardData(ctx))
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
signTransactionTask?.cancel(true)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
signTransactionTask?.cancel(true)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) {
|
||||
navigateBackWithResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
|
||||
if (sUID == ctx.card.uid) {
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000
|
||||
else
|
||||
isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000
|
||||
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
coinEngine?.setOnNeedSendTransaction { tx ->
|
||||
if (tx != null) {
|
||||
val data = Bundle()
|
||||
ctx.saveToBundle(data)
|
||||
data.putByteArray(Constant.EXTRA_TX, tx)
|
||||
navigateForResult(
|
||||
Constant.REQUEST_CODE_SEND_TRANSACTION_,
|
||||
R.id.action_signTransactionFragment_to_sendTransactionFragment,
|
||||
data)
|
||||
}
|
||||
}
|
||||
val transactionToSign = coinEngine?.constructTransaction(amount, fee, isIncludeFee, outAddressStr)
|
||||
|
||||
signTransactionTask = SignTask(ctx.card, NfcReader((activity as MainActivity).nfcManager, isoDep),
|
||||
App.localStorage, App.pinStorage, this, transactionToSign)
|
||||
signTransactionTask?.start()
|
||||
} else
|
||||
(activity as MainActivity).nfcManager.ignoreTag(isoDep.tag)
|
||||
|
||||
} catch (e: CardProtocol.TangemException_WrongAmount) {
|
||||
try {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, ctx.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, ctx.card.asBundle)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} catch(e: IllegalArgumentException) {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, e.message)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data, R.id.loadedWalletFragment)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar?.progress = 5
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar?.post { progressBar?.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
signTransactionTask = null
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.error == null) {
|
||||
|
||||
FirebaseAnalytics.getInstance(requireActivity())
|
||||
.logEvent(AnalyticsEvent.SIGNED.event, Analytics.setCardData(ctx))
|
||||
|
||||
rlProgressBar?.post { rlProgressBar?.visibility = View.GONE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
|
||||
}
|
||||
|
||||
mpFinishSignSound.start()
|
||||
} else {
|
||||
lastReadSuccess = false
|
||||
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
|
||||
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
|
||||
progressBar?.post {
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
|
||||
}
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_cannot_sign))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
|
||||
navigateBackWithResult(Constant.RESULT_INVALID_PIN_, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
} else {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) {
|
||||
try {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
progressBar?.post {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
|
||||
NoExtendedLengthSupportDialog.message = getText(R.string.dialog_the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.dialog_the_nfc_adapter_length_apdu_advice).toString()
|
||||
NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG)
|
||||
}
|
||||
} else {
|
||||
(activity as? MainActivity)?.toastHelper?.showSingleToast(
|
||||
context, getString(R.string.general_notification_scan_again)
|
||||
)
|
||||
}
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
signTransactionTask = null
|
||||
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// private val waitSecurityDelayDialogNew = WaitSecurityDelayDialogNew()
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
LOG.i(TAG, "onReadBeforeRequest timeout $timeout")
|
||||
activity?.let { WaitSecurityDelayDialog.onReadBeforeRequest(it, timeout) }
|
||||
|
||||
// if (!waitSecurityDelayDialogNew.isAdded)
|
||||
// waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG)
|
||||
|
||||
|
||||
// val readBeforeRequest = ReadBeforeRequest()
|
||||
// readBeforeRequest.timeout = timeout
|
||||
// EventBus.getDefault().post(readBeforeRequest)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
LOG.i(TAG, "onReadAfterRequest")
|
||||
activity?.let { WaitSecurityDelayDialog.onReadAfterRequest(it) }
|
||||
|
||||
// val readAfterRequest = ReadAfterRequest()
|
||||
// EventBus.getDefault().post(readAfterRequest)
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
LOG.i(TAG, "onReadWait msec $msec")
|
||||
activity?.let { WaitSecurityDelayDialog.onReadWait(it, msec) }
|
||||
|
||||
// val readWait = ReadWait()
|
||||
// readWait.msec = msec
|
||||
// EventBus.getDefault().post(readWait)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,343 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/cl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.tangem.ui.ConfirmTransactionFragment"
|
||||
tools:ignore="Autofill">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="@string/general_send_transaction"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llTransaction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/btn_light"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_from_card"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardID"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="bold"
|
||||
tools:text="BB00 0000 1210 0233" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_balance"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBalance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="bold"
|
||||
tools:text="4.51735 BTC" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_send_to_wallet"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etWallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:fontFamily="@font/maax"
|
||||
android:hint="@string/confirm_transaction_hint_target_address"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="2"
|
||||
android:padding="12dp"
|
||||
android:singleLine="false"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_1_small"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_amount"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvIncFee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/textView3"
|
||||
app:layout_constraintLeft_toRightOf="@+id/textView3"
|
||||
tools:text="(including fee)" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etAmount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:digits="0123456789.,"
|
||||
android:fontFamily="@font/maax"
|
||||
android:hint="@string/prepare_transaction_hint_enter_amount"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="numberDecimal"
|
||||
android:padding="12dp"
|
||||
android:textSize="@dimen/text_size_1_large"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tvCurrency"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_min="150dp"
|
||||
android:enabled="false"
|
||||
android:textColor="@color/black"
|
||||
tools:text="1.34343434343443434343434"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCurrency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_btc"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_1_large"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/etAmount"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llFee"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@+id/llTransaction">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/confirm_transaction_fee"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<info.hoang8f.android.segmented.SegmentedGroup
|
||||
android:id="@+id/rgFee"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:checkedButton="@+id/rbNormalFee"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:sc_border_width="2dp"
|
||||
app:sc_corner_radius="10dp"
|
||||
app:sc_tint_color="@color/colorPrimary">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbMinimalFee"
|
||||
style="@style/RadioButton"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/confirm_transaction_btn_fee_minimal"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbNormalFee"
|
||||
style="@style/RadioButton"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/confirm_transaction_btn_fee_normal"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbMaximumFee"
|
||||
style="@style/RadioButton"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/confirm_transaction_btn_fee_priority"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
</info.hoang8f.android.segmented.SegmentedGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etFee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:hint="@string/confirm_transaction_hint_fee_amount"
|
||||
android:inputType="numberDecimal"
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorLink="@android:color/holo_blue_dark"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_min="100dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCurrency2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_btc"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@+id/etFee"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFeeEquivalent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:textSize="@dimen/text_size_1_small"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@+id/tvCurrency2"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSend"
|
||||
style="@style/AppTheme.RoundedCornerMaterialButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/confirm_transaction_btn_send"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="@android:style/Widget.Holo.Light.ProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:elevation="1dp"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/colorPrimary"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/llFee" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.tangem.ui.PrepareTransactionFragment"
|
||||
tools:ignore="contentDescription">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:text="@string/general_send_transaction"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llFrom"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/btn_light"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="24dp"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_from_card"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardID"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="bold"
|
||||
tools:text="BB00 0000 1210 0233" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_balance"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBalance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="bold"
|
||||
tools:text="4.51735 Btc" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llTo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/card_state_loaded_with_zero"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="32dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/llFrom">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_send_to_wallet"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivCamera"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/qr_scan"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etWallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:hint="@string/prepare_transaction_hint_enter_address"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="text|textMultiLine|textNoSuggestions"
|
||||
android:padding="12dp"
|
||||
android:singleLine="false"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textColorLink="@android:color/holo_blue_dark"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@+id/ivCamera"
|
||||
tools:layout_editor_absoluteY="2dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_amount"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etAmount"
|
||||
tools:text="1.34343434343443434343434"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:digits="0123456789.,"
|
||||
android:fontFamily="@font/maax"
|
||||
android:hint="@string/prepare_transaction_hint_enter_amount"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="numberDecimal"
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="@dimen/text_size_1_large"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tvCurrency"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_min="150dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCurrency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/general_btc"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="@dimen/text_size_1_large"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/etAmount"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<info.hoang8f.android.segmented.SegmentedGroup
|
||||
android:id="@+id/rgIncFee"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:checkedButton="@+id/rbFeeIn"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:sc_border_width="2dp"
|
||||
app:sc_corner_radius="10dp"
|
||||
app:sc_tint_color="@color/colorPrimary"
|
||||
tools:layout_editor_absoluteX="10dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbFeeIn"
|
||||
style="@style/RadioButton"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/confirm_transaction_btn_including_fee"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbFeeOut"
|
||||
style="@style/RadioButton"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/confirm_transaction_btn_not_including_fee"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
</info.hoang8f.android.segmented.SegmentedGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnVerify"
|
||||
style="@style/AppTheme.RoundedCornerMaterialButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="@string/prepare_transaction_btn_verify"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_touch_card"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="250dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.75">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/now_touch_the_card_with_id"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardID"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
tools:text="CB02 0000 0002 5000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="@string/to_sign_the_transaction"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/layout_progress_horizontal" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">Tangem</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.datasource.api.promotion
|
||||
|
||||
import com.tangem.datasource.api.promotion.models.*
|
||||
import retrofit2.http.*
|
||||
|
||||
/**
|
||||
*
|
||||
* Promotion API
|
||||
* @see <a href = "https://www.notion.so/tangem/Promotion-Program-API-0907159c3fdb4975aac761be632f44da">Documentation<a/>
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface PromotionApi {
|
||||
|
||||
@Headers("Cache-Control: max-age=3600")
|
||||
@GET("promotion")
|
||||
suspend fun getPromotionInfo(@Query("programName") name: String): PromotionInfoResponse
|
||||
|
||||
@POST("promotion/code/validate")
|
||||
suspend fun validateCode(@Body request: CodeValidateRequestBody): CodeValidateResponse
|
||||
|
||||
@POST("promotion/code/award")
|
||||
suspend fun requestAwardByCode(@Body request: CodeAwardRequestBody): CodeAwardResponse
|
||||
|
||||
@POST("promotion/validate")
|
||||
suspend fun validate(@Body request: ValidateRequestBody): ValidateResponse
|
||||
|
||||
@POST("promotion/award")
|
||||
suspend fun requestAward(@Body request: AwardRequestBody): AwardResponse
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class AbstractPromotionResponse {
|
||||
|
||||
abstract val error: Error?
|
||||
|
||||
fun isError(): Boolean = error != null
|
||||
|
||||
data class Error(
|
||||
@Json(name = "code") val code: Int,
|
||||
@Json(name = "message") val message: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class AwardRequestBody(
|
||||
@Json(name = "walletId") val walletId: String,
|
||||
@Json(name = "address") val address: String,
|
||||
@Json(name = "programName") val programName: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class AwardResponse(
|
||||
@Json(name = "status") val status: Boolean?,
|
||||
@Json(name = "error") override val error: Error? = null,
|
||||
) : AbstractPromotionResponse()
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class CodeAwardRequestBody(
|
||||
@Json(name = "walletId") val walletId: String,
|
||||
@Json(name = "address") val address: String,
|
||||
@Json(name = "code") val code: String?,
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class CodeAwardResponse(
|
||||
@Json(name = "status") val status: Boolean?,
|
||||
@Json(name = "error") override val error: Error? = null,
|
||||
) : AbstractPromotionResponse()
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class CodeValidateRequestBody(
|
||||
@Json(name = "walletId") val walletId: String,
|
||||
@Json(name = "code") val code: String?,
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class CodeValidateResponse(
|
||||
@Json(name = "valid") val valid: Boolean?,
|
||||
@Json(name = "error") override val error: Error? = null,
|
||||
) : AbstractPromotionResponse()
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class PromotionInfoResponse(
|
||||
@Json(name = "newCard") val newCard: Data?,
|
||||
@Json(name = "oldCard") val oldCard: Data?,
|
||||
@Json(name = "awardPaymentToken") val awardPaymentToken: TokenData?,
|
||||
@Json(name = "error") override val error: Error? = null,
|
||||
) : AbstractPromotionResponse() {
|
||||
|
||||
data class Data(
|
||||
@Json(name = "status") val status: Status,
|
||||
@Json(name = "award") val award: Double,
|
||||
)
|
||||
|
||||
enum class Status(val value: String) {
|
||||
@Json(name = "pending")
|
||||
PENDING("pending"),
|
||||
|
||||
@Json(name = "active")
|
||||
ACTIVE("active"),
|
||||
|
||||
@Json(name = "finished")
|
||||
FINISHED("finished"),
|
||||
}
|
||||
|
||||
data class TokenData(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "symbol") val symbol: String,
|
||||
@Json(name = "active") val active: Boolean,
|
||||
@Json(name = "networkId") val networkId: String,
|
||||
@Json(name = "contractAddress") val contractAddress: String,
|
||||
@Json(name = "decimalCount") val decimalCount: Int,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class ValidateRequestBody(
|
||||
@Json(name = "walletId") val walletId: String,
|
||||
@Json(name = "programName") val programName: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.api.promotion.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class ValidateResponse(
|
||||
@Json(name = "valid") val valid: Boolean?,
|
||||
@Json(name = "error") override val error: Error? = null,
|
||||
) : AbstractPromotionResponse()
|
||||
|
|
@ -17,7 +17,6 @@ interface ConfigManager {
|
|||
fun resetToDefault(name: String)
|
||||
|
||||
companion object {
|
||||
const val IS_SENDING_TO_PAY_ID_ENABLED = "isSendingToPayIdEnabled"
|
||||
const val IS_CREATING_TWIN_CARDS_ALLOWED = "isCreatingTwinCardsAllowed"
|
||||
const val IS_TOP_UP_ENABLED = "isTopUpEnabled"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
package com.tangem.datasource.config
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.BlockchairCredentials
|
||||
import com.tangem.blockchain.common.GetBlockCredentials
|
||||
import com.tangem.blockchain.common.NowNodeCredentials
|
||||
import com.tangem.blockchain.common.QuickNodeCredentials
|
||||
import com.tangem.blockchain.common.TonCenterCredentials
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.datasource.config.ConfigManager.Companion.IS_CREATING_TWIN_CARDS_ALLOWED
|
||||
import com.tangem.datasource.config.ConfigManager.Companion.IS_SENDING_TO_PAY_ID_ENABLED
|
||||
import com.tangem.datasource.config.ConfigManager.Companion.IS_TOP_UP_ENABLED
|
||||
import com.tangem.datasource.config.models.Config
|
||||
import com.tangem.datasource.config.models.ConfigModel
|
||||
|
|
@ -34,7 +28,6 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
|
|||
|
||||
override fun turnOff(name: String) {
|
||||
when (name) {
|
||||
IS_SENDING_TO_PAY_ID_ENABLED -> config = config.copy(isSendingToPayIdEnabled = false)
|
||||
IS_TOP_UP_ENABLED -> config = config.copy(isTopUpEnabled = false)
|
||||
IS_CREATING_TWIN_CARDS_ALLOWED -> config = config.copy(isCreatingTwinCardsAllowed = false)
|
||||
}
|
||||
|
|
@ -42,13 +35,13 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
|
|||
|
||||
override fun resetToDefault(name: String) {
|
||||
when (name) {
|
||||
IS_SENDING_TO_PAY_ID_ENABLED ->
|
||||
config =
|
||||
config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
|
||||
IS_TOP_UP_ENABLED -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
|
||||
IS_CREATING_TWIN_CARDS_ALLOWED ->
|
||||
config =
|
||||
config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
|
||||
IS_TOP_UP_ENABLED -> {
|
||||
config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
|
||||
}
|
||||
IS_CREATING_TWIN_CARDS_ALLOWED -> {
|
||||
config = config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,12 +50,11 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
|
|||
|
||||
config = config.copy(
|
||||
isTopUpEnabled = model.isTopUpEnabled,
|
||||
isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
|
||||
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
|
||||
)
|
||||
|
||||
defaultConfig = defaultConfig.copy(
|
||||
isTopUpEnabled = model.isTopUpEnabled,
|
||||
isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
|
||||
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
|
||||
)
|
||||
}
|
||||
|
|
@ -111,6 +103,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
|
|||
zendesk = configValues.zendesk,
|
||||
swapReferrerAccount = configValues.swapReferrerAccount,
|
||||
walletConnectProjectId = configValues.walletConnectProjectId,
|
||||
tangemComAuthorization = configValues.tangemComAuthorization,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ data class Config(
|
|||
val appsFlyerDevKey: String = "",
|
||||
val amplitudeApiKey: String = "",
|
||||
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
|
||||
val isSendingToPayIdEnabled: Boolean = true,
|
||||
val isTopUpEnabled: Boolean = false,
|
||||
@Deprecated("Not relevant since version 3.23")
|
||||
val isCreatingTwinCardsAllowed: Boolean = false,
|
||||
|
|
@ -19,4 +18,5 @@ data class Config(
|
|||
val zendesk: ZendeskConfig? = null,
|
||||
val swapReferrerAccount: SwapReferrerAccount? = null,
|
||||
val walletConnectProjectId: String = "",
|
||||
val tangemComAuthorization: String? = null,
|
||||
)
|
||||
|
|
@ -8,7 +8,6 @@ import com.squareup.moshi.Json
|
|||
|
||||
class FeatureModel(
|
||||
val isTopUpEnabled: Boolean,
|
||||
val isSendingToPayIdEnabled: Boolean,
|
||||
val isCreatingTwinCardsAllowed: Boolean,
|
||||
)
|
||||
|
||||
|
|
@ -38,6 +37,7 @@ class ConfigValueModel(
|
|||
val swapReferrerAccount: SwapReferrerAccount?,
|
||||
val kaspaSecondaryApiUrl: String,
|
||||
val walletConnectProjectId: String,
|
||||
val tangemComAuthorization: String?,
|
||||
)
|
||||
|
||||
data class AppsFlyer(
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ package com.tangem.datasource.di
|
|||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.api.paymentology.PaymentologyApi
|
||||
import com.tangem.datasource.api.promotion.PromotionApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.utils.RequestHeader.*
|
||||
import com.tangem.datasource.utils.addHeaders
|
||||
import com.tangem.datasource.utils.allowLogging
|
||||
import com.tangem.lib.auth.AuthProvider
|
||||
import com.tangem.lib.auth.BuildConfig
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -13,6 +16,7 @@ import dagger.hilt.components.SingletonComponent
|
|||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
|
@ -54,10 +58,37 @@ class NetworkModule {
|
|||
.create(PaymentologyApi::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@PromotionOneInch
|
||||
fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi {
|
||||
val okClient = OkHttpClient.Builder()
|
||||
.addHeaders(
|
||||
AuthenticationHeader(authProvider),
|
||||
)
|
||||
.allowLogging()
|
||||
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
return createBasePromotionRetrofit(okClient, moshi)
|
||||
}
|
||||
|
||||
private fun createBasePromotionRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): PromotionApi {
|
||||
return Retrofit.Builder()
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.baseUrl(if (BuildConfig.DEBUG) DEV_TANGEM_TECH_BASE_URL else PROD_TANGEM_TECH_BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
.create(PromotionApi::class.java)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PROD_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v1/"
|
||||
const val DEV_TANGEM_TECH_BASE_URL = "https://devapi.tangem-tech.com/v1/"
|
||||
|
||||
private const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/"
|
||||
const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/"
|
||||
const val API_ONE_INCH_TIMEOUT_MS = 5000L
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class PromotionOneInch
|
||||
|
|
@ -13,7 +13,7 @@ internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeade
|
|||
val request = chain.request().newBuilder().apply {
|
||||
requestHeaders
|
||||
.flatMap(RequestHeader::values)
|
||||
.forEach { addHeader(it.first, it.second) }
|
||||
.forEach { addHeader(it.first, it.second.invoke()) }
|
||||
}.build()
|
||||
|
||||
chain.proceed(request)
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import com.tangem.lib.auth.AuthProvider
|
|||
*
|
||||
* @param pairs header name and header value pairs
|
||||
*/
|
||||
sealed class RequestHeader(vararg pairs: Pair<String, String>) {
|
||||
sealed class RequestHeader(vararg pairs: Pair<String, () -> String>) {
|
||||
|
||||
/** Header list */
|
||||
val values: List<Pair<String, String>> = pairs.toList()
|
||||
val values: List<Pair<String, () -> String>> = pairs.toList()
|
||||
|
||||
object CacheControlHeader : RequestHeader("Cache-Control" to "max-age=600")
|
||||
object CacheControlHeader : RequestHeader("Cache-Control" to { "max-age=600" })
|
||||
|
||||
class AuthenticationHeader(authProvider: AuthProvider) : RequestHeader(
|
||||
"card_public_key" to authProvider.getCardPublicKey(),
|
||||
"card_id" to authProvider.getCardId(),
|
||||
"card_id" to { authProvider.getCardId() },
|
||||
"card_public_key" to { authProvider.getCardPublicKey() },
|
||||
)
|
||||
}
|
||||
|
|
@ -14,5 +14,13 @@
|
|||
{
|
||||
"name": "REDESIGNED_WALLET_SCREEN_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "1INCH_LEARN_2_EARN_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "REDESIGNED_TOKEN_DETAIL_SCREEN_ENABLED",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<string name="common_done">Erledigt</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_save_changes">Änderungen speichern</string>
|
||||
<string name="common_send">Absenden</string>
|
||||
<string name="common_success">Erfolg</string>
|
||||
<string name="details_manage_security_access_code">Zugangscode</string>
|
||||
<string name="details_manage_security_access_code_description">Sie müssen den richtigen Zugangscode eingeben, bevor Sie die Karte scannen.</string>
|
||||
|
|
@ -34,11 +35,7 @@
|
|||
<string name="main_processing_full_amount">Der Betrag enthält nicht einige Ihrer Mittel</string>
|
||||
<string name="send_amount_label">Betrag</string>
|
||||
<string name="send_destination_hint_address">Adresse</string>
|
||||
<string name="send_destination_hint_address_payid">Adresse oder PayString</string>
|
||||
<string name="send_error_address_same_as_wallet">Die Adresse stimmt mit der Adresse Ihrer Brieftasche überein</string>
|
||||
<string name="send_error_payid_not_registered">PayString ist nicht registriert</string>
|
||||
<string name="send_error_payid_request_failed">PayString-Anfrage ist fehlgeschlagen</string>
|
||||
<string name="send_error_payid_unsupported_by_blockchain">PayString wird von der Blockchain nicht unterstützt</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">inkl. Gebühr</string>
|
||||
|
|
@ -48,7 +45,6 @@
|
|||
<string name="send_fee_picker_priority">Priorität</string>
|
||||
<string name="send_max_amount_label">Höchstbetrag</string>
|
||||
<string name="send_network_fee_title">Netzgebühr</string>
|
||||
<string name="send_title">Absenden</string>
|
||||
<string name="send_total_label">Gesamt</string>
|
||||
<string name="send_total_subtitle_asset_format">%1$s und %2$s werden gesendet</string>
|
||||
<string name="send_total_subtitle_fiat_format">≈ %1$s (inkl. Gebühr: %2$s)</string>
|
||||
|
|
@ -57,11 +53,9 @@
|
|||
<string name="send_validation_invalid_address">Ungültige Adresse</string>
|
||||
<string name="shop_one_wallet">Tangem Wallet</string>
|
||||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="wallet_address_button_create_payid">PayString erstellen</string>
|
||||
<string name="wallet_balance_loading">Die Bilanz wird aufgeladen…</string>
|
||||
<string name="wallet_balance_tx_in_progress">Die Transaktion läuft…</string>
|
||||
<string name="wallet_balance_verified">Verifizierte Bilanz</string>
|
||||
<string name="wallet_button_send">Absenden</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_error_no_account">Das Konto ist nicht erstellt</string>
|
||||
<string name="wallet_error_unsupported_blockchain">Diese Karte wird nicht unterstützt</string>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<string name="common_done">Exécuté</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_save_changes">Sauvegarder les modifications</string>
|
||||
<string name="common_send">Envoyer</string>
|
||||
<string name="common_success">Avec succès</string>
|
||||
<string name="details_manage_security_access_code">Code d\'accès</string>
|
||||
<string name="details_manage_security_access_code_description">Vous devrez entrer le mot de passe correct avant de scanner la carte</string>
|
||||
|
|
@ -34,11 +35,7 @@
|
|||
<string name="main_processing_full_amount">Le montant n\'inclut pas certains de vos fonds</string>
|
||||
<string name="send_amount_label">Somme</string>
|
||||
<string name="send_destination_hint_address">Adresse</string>
|
||||
<string name="send_destination_hint_address_payid">Adresse ou PayString</string>
|
||||
<string name="send_error_address_same_as_wallet">L\'adresse est la même que celle de votre portefeuille</string>
|
||||
<string name="send_error_payid_not_registered">PayString non enregistré</string>
|
||||
<string name="send_error_payid_request_failed">La demande de PayString a échoué</string>
|
||||
<string name="send_error_payid_unsupported_by_blockchain">PayString non pris en charge par la blockchain</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">Inclure les commissions</string>
|
||||
|
|
@ -48,7 +45,6 @@
|
|||
<string name="send_fee_picker_priority">Priorité</string>
|
||||
<string name="send_max_amount_label">Somme maximale</string>
|
||||
<string name="send_network_fee_title">Commissions du réseau</string>
|
||||
<string name="send_title">Envoyer</string>
|
||||
<string name="send_total_label">Total</string>
|
||||
<string name="send_total_subtitle_asset_format">Sera envoyé %1$s et %2$s</string>
|
||||
<string name="send_total_subtitle_fiat_format">≈ %1$s (incl. les commissions : %2$s)</string>
|
||||
|
|
@ -57,11 +53,9 @@
|
|||
<string name="send_validation_invalid_address">Adresse incorrecte</string>
|
||||
<string name="shop_one_wallet">Tangem Wallet</string>
|
||||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="wallet_address_button_create_payid">Créer PayString</string>
|
||||
<string name="wallet_balance_loading">Solde est en cours de téléchargement…</string>
|
||||
<string name="wallet_balance_tx_in_progress">Transaction en cours…</string>
|
||||
<string name="wallet_balance_verified">Solde confirmé</string>
|
||||
<string name="wallet_button_send">Envoyer</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_error_no_account">Compte n\'est pas créé</string>
|
||||
<string name="wallet_error_unsupported_blockchain">Cette carte n\'est pas prise en charge</string>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<string name="common_done">Fatto</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_save_changes">Mantieni le modifiche</string>
|
||||
<string name="common_send">Invia</string>
|
||||
<string name="common_success">Con successo</string>
|
||||
<string name="details_manage_security_access_code">Codice di accesso</string>
|
||||
<string name="details_manage_security_access_code_description">Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto</string>
|
||||
|
|
@ -34,11 +35,7 @@
|
|||
<string name="main_processing_full_amount">L\'importo non include alcuni dei tuoi fondi</string>
|
||||
<string name="send_amount_label">Importo</string>
|
||||
<string name="send_destination_hint_address">Indirizzo</string>
|
||||
<string name="send_destination_hint_address_payid">Indirizzo o PayString</string>
|
||||
<string name="send_error_address_same_as_wallet">L\'indirizzo corrisponde all\'indirizzo del tuo portafoglio</string>
|
||||
<string name="send_error_payid_not_registered">PayString non registrato</string>
|
||||
<string name="send_error_payid_request_failed">Richiesta PayString fallita</string>
|
||||
<string name="send_error_payid_unsupported_by_blockchain">PayString non supportato dalla blockchain</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">Includi commissione</string>
|
||||
|
|
@ -48,7 +45,6 @@
|
|||
<string name="send_fee_picker_priority">Prioritario</string>
|
||||
<string name="send_max_amount_label">Importo totale</string>
|
||||
<string name="send_network_fee_title">Costi della rete</string>
|
||||
<string name="send_title">Invia</string>
|
||||
<string name="send_total_label">Totale</string>
|
||||
<string name="send_total_subtitle_asset_format">Sarà inviato %1$s e %2$s</string>
|
||||
<string name="send_total_subtitle_fiat_format">≈ %1$s (inc. commissione: %2$s)</string>
|
||||
|
|
@ -57,11 +53,9 @@
|
|||
<string name="send_validation_invalid_address">Indirizzo non valido</string>
|
||||
<string name="shop_one_wallet">Tangem Wallet</string>
|
||||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="wallet_address_button_create_payid">Crea PayString</string>
|
||||
<string name="wallet_balance_loading">Il saldo sta per essere caricato…</string>
|
||||
<string name="wallet_balance_tx_in_progress">Transazione in corso…</string>
|
||||
<string name="wallet_balance_verified">Saldo verificato</string>
|
||||
<string name="wallet_button_send">Invia</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_error_no_account">Conto non creato</string>
|
||||
<string name="wallet_error_unsupported_blockchain">Questa carta non è supportata</string>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<string name="alert_signed_hashes_message">Эта карта не является платежным средством. В настоящее время мы не можем сопоставить количество подписей на карте с информацией в блокчейне. Это нормально, но в редких случаях может означать, что предыдущий владелец удерживает подписанную транзакцию от публикации, что является поводом для беспокойства.\nНе принимайте эту карту в качестве физического платежа от кого-то, кому вы не доверяете.\nВо всех остальных отношениях эта карта совершенно безопасна.\nTangem — единственный аппаратный кошелек, предлагающий защиту методом подсчета подписей.</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">У вас возникли трудности со сканированием карты?</string>
|
||||
<string name="alert_unsupported_card">Эта карта не предназначена для работы с этим приложением</string>
|
||||
<string name="app_settings_enable_biometrics_description">Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem App</string>
|
||||
<string name="app_settings_enable_biometrics_description">Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem</string>
|
||||
<string name="app_settings_enable_biometrics_title">Включите биометрическую аутентификацию</string>
|
||||
<string name="app_settings_off_saved_access_code_alert_message">Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком.</string>
|
||||
<string name="app_settings_off_saved_wallet_alert_message">При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения.</string>
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
<string name="card_settings_access_code_recovery_title">Восстановление кода доступа</string>
|
||||
<string name="card_settings_change_access_code">Смена кода доступа</string>
|
||||
<string name="card_settings_change_access_code_footer">Код доступа будет изменен только на данной карте</string>
|
||||
<string name="card_settings_reset_card_to_factory">Сброс к заводским настройкам</string>
|
||||
<string name="card_settings_reset_card_to_factory">Заводские настройки</string>
|
||||
<string name="card_settings_security_mode">Тип безопасности</string>
|
||||
<string name="card_settings_title">Настройки карты</string>
|
||||
<string name="chat_bot_name">Tangem Bot</string>
|
||||
|
|
@ -52,27 +52,41 @@
|
|||
<string name="common_balance">Баланс: %s</string>
|
||||
<string name="common_biometric_authentication">биометрическую аутентификацию</string>
|
||||
<string name="common_biometrics">биометрией</string>
|
||||
<string name="common_buy">Купить</string>
|
||||
<string name="common_camera_denied_alert_message">Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности.</string>
|
||||
<string name="common_cancel">Отмена</string>
|
||||
<string name="common_close">Закрыть</string>
|
||||
<string name="common_copy">Копировать</string>
|
||||
<string name="common_copy_address">Скопировать адрес</string>
|
||||
<string name="common_create">Создать</string>
|
||||
<string name="common_delete">Удалить</string>
|
||||
<string name="common_disabled">Отключено</string>
|
||||
<string name="common_dislike">Не нравится</string>
|
||||
<string name="common_done">Готово</string>
|
||||
<string name="common_enable">Включить</string>
|
||||
<string name="common_enabled">Включено</string>
|
||||
<string name="common_exchange">Обменять</string>
|
||||
<string name="common_explore_transaction_history">Посмотреть историю транзакций</string>
|
||||
<string name="common_explorer">Обозреватель</string>
|
||||
<string name="common_like">Нравится</string>
|
||||
<string name="common_main_network">Основная сеть</string>
|
||||
<string name="common_no">Нет</string>
|
||||
<string name="common_ok">Ок</string>
|
||||
<string name="common_no_data">Нет данных</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">Основная карта</string>
|
||||
<string name="common_receive">Получить</string>
|
||||
<string name="common_reject">Отклонить</string>
|
||||
<string name="common_reload">Перезагрузить</string>
|
||||
<string name="common_save_changes">Сохранить изменения</string>
|
||||
<string name="common_search">Искать</string>
|
||||
<string name="common_seed_phrase">Секретная фраза</string>
|
||||
<string name="common_sell">Продать</string>
|
||||
<string name="common_send">Отправить</string>
|
||||
<string name="common_server_unavailable">Сервер недоступен, повторите попытку позднее</string>
|
||||
<string name="common_share">Поделиться</string>
|
||||
<string name="common_sign">Подписать</string>
|
||||
<string name="common_sign_and_send">Подписать и отправить</string>
|
||||
<string name="common_stake">Стейкинг</string>
|
||||
<string name="common_start">Начать</string>
|
||||
<string name="common_submit">Отправить</string>
|
||||
<string name="common_success">Успешно</string>
|
||||
|
|
@ -81,6 +95,7 @@
|
|||
<string name="common_transactions">Транзакции</string>
|
||||
<string name="common_transfer">Перевод</string>
|
||||
<string name="common_understand">Я понял</string>
|
||||
<string name="common_unreachable">Недоступно</string>
|
||||
<string name="common_yes">Да</string>
|
||||
<string name="contract_address_copied_message">Адрес контракта скопирован!</string>
|
||||
<string name="currency_subtitle_expanded">Доступные сети</string>
|
||||
|
|
@ -145,11 +160,22 @@
|
|||
<string name="initial_message_tap_header">Приложите карту</string>
|
||||
<string name="internal_error_wallet_manager_not_found">Внутренняя ошибка: не удается найти менеджер кошельков</string>
|
||||
<string name="key_invalidated_warning_description">Вы обновили данные биометрии, отсканируйте свою карту для входа</string>
|
||||
<string name="main_get_bonus_subtitle">Вы завершили обучение и можете получить свои токены 1inch</string>
|
||||
<string name="main_get_bonus_title">Получите бонус</string>
|
||||
<plurals name="main_learn_subtitle">
|
||||
<item quantity="one">Пройдите обучение и получите %d токен 1inch на свой кошелек</item>
|
||||
<item quantity="few">Пройдите обучение и получите %d токена 1inch на свой кошелек</item>
|
||||
<item quantity="many">Пройдите обучение и получите %d токена 1inch на свой кошелек</item>
|
||||
<item quantity="other">Пройдите обучение и получите %d токенов 1inch на свой кошелек</item>
|
||||
</plurals>
|
||||
<string name="main_learn_title">Бонус за обучение</string>
|
||||
<string name="main_manage_tokens">Управление токенами</string>
|
||||
<string name="main_no_backup_warning_subtitle">Чтобы защитить свои активы, мы советуем вам выполнить эту процедуру</string>
|
||||
<string name="main_no_backup_warning_title">Бэкап кошелька не был произведен</string>
|
||||
<string name="main_page_balance">Баланс</string>
|
||||
<string name="main_processing_full_amount">В сумме учтены не все монеты</string>
|
||||
<string name="main_promotion_credited">Токены 1inch будут зачислены на адрес вашего кошелька %s в течение 24 часов</string>
|
||||
<string name="main_promotion_no_purchase">По вашему промокоду не было покупки кошелька, а значит вы не можете получить бонус. Купите кошелек Tangem, отсканируйте его в приложении и получите бонус.</string>
|
||||
<string name="main_scan_card_warning_view_subtitle">Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту</string>
|
||||
<string name="main_scan_card_warning_view_title">Отсканируйте карту</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
|
||||
|
|
@ -173,11 +199,8 @@
|
|||
<string name="onboarding_button_claim">Запросить</string>
|
||||
<string name="onboarding_button_continue_wallet">Перейти к моему кошельку</string>
|
||||
<string name="onboarding_button_finalize_backup">Завершение бэкапа</string>
|
||||
<string name="onboarding_button_kyc_start">Верифицировать (Utorg)</string>
|
||||
<string name="onboarding_button_kyc_waiting">Обновить</string>
|
||||
<string name="onboarding_button_pin">Установить Код доступа</string>
|
||||
<string name="onboarding_button_receive_crypto">Получить криптовалюту</string>
|
||||
<string name="onboarding_button_register_wallet">Зарегистрироваться</string>
|
||||
<string name="onboarding_button_scan_origin_card">Сканировать основную карту</string>
|
||||
<string name="onboarding_button_skip_backup">Пропустить</string>
|
||||
<string name="onboarding_button_what_does_it_mean">Как это работает?</string>
|
||||
|
|
@ -192,19 +215,9 @@
|
|||
<string name="onboarding_exit_alert_message">В этом случае вам будет необходимо начать процесс заново.</string>
|
||||
<string name="onboarding_exit_alert_title">Вы хотите выйти из процесса активации?</string>
|
||||
<string name="onboarding_getting_started">Подготовка</string>
|
||||
<string name="onboarding_linking_error_card_with_wallets">Другой кошелек уже был создан на карте, которую вы пытаетесь добавить. Хотите сбросить его и использовать карту для бэкапа?</string>
|
||||
<string name="onboarding_navbar_kyc_progress">Подтвердите свою личность</string>
|
||||
<string name="onboarding_navbar_kyc_start">Верификация клиента</string>
|
||||
<string name="onboarding_navbar_pin">Код доступа</string>
|
||||
<string name="onboarding_navbar_register_wallet">Подключиться</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Резервная копия</string>
|
||||
<string name="onboarding_saltpay_button_backup_origin">Приложите SaltPay карту</string>
|
||||
<string name="onboarding_saltpay_subtitle_no_backup_cards">Для начала процесса бэкапа вам необходимо добавить Tangem карту</string>
|
||||
<string name="onboarding_saltpay_subtitle_one_backup_card">Завершите процесс бэкапа создав код доступа</string>
|
||||
<string name="onboarding_saltpay_title_backup_card">Приложите Tangem карту</string>
|
||||
<string name="onboarding_saltpay_title_no_backup_card">Бэкап карта не добавлена</string>
|
||||
<string name="onboarding_saltpay_title_one_backup_card">Бэкап карта создана</string>
|
||||
<string name="onboarding_saltpay_title_prepare_origin">Приготовьте SaltPay карту</string>
|
||||
<string name="onboarding_seed_button_read_more">Читать про секретную фразу</string>
|
||||
<string name="onboarding_seed_generate_message">Запишите эти 12 слов в порядке, указанном ниже, и сохраните их в надежном месте.</string>
|
||||
<string name="onboarding_seed_generate_title">Ваша секретная фраза</string>
|
||||
|
|
@ -220,13 +233,8 @@
|
|||
<string name="onboarding_seed_user_validation_title">Итак, проверим</string>
|
||||
<string name="onboarding_subtitle_claim">Для начала работы просто запросите начисление wxDAI на свой кошелек</string>
|
||||
<string name="onboarding_subtitle_claim_progress">Это займет несколько секунд</string>
|
||||
<string name="onboarding_subtitle_kyc_retry">Более подробная информация отправлена на ваш адрес электронной почты.</string>
|
||||
<string name="onboarding_subtitle_kyc_start">Для начала работы с картой вам необходимо завершить процесс подтверждения личности</string>
|
||||
<string name="onboarding_subtitle_kyc_waiting">Пожалуйста дождитесь завершения процесса подтверждения личности. Вы будете уведомлены через e-mail. Обычно это занимает не более часа. Вы можете закрыть приложение и вернуться позже.</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">Чтобы начать процесс резервного копирования, добавьте одну или две резервные карты.</string>
|
||||
<string name="onboarding_subtitle_one_backup_card">Вы можете добавить еще одну карту или завершить процесс резервного копирования</string>
|
||||
<string name="onboarding_subtitle_pin">Установите 4-х значный код.\nОн будет использован для платежей.</string>
|
||||
<string name="onboarding_subtitle_register_wallet">Подключите вашу карту к децентрализованной платежной системе</string>
|
||||
<string name="onboarding_subtitle_scan_backup_card_format">Подготовьте резервную карту с номером %s</string>
|
||||
<string name="onboarding_subtitle_scan_origin_card">Подготовьте основную карту</string>
|
||||
<string name="onboarding_subtitle_scan_primary_card_format">Подготовьте основную карту с номером %s</string>
|
||||
|
|
@ -237,13 +245,9 @@
|
|||
<string name="onboarding_title_backup_card_format">Резервная карта #%d</string>
|
||||
<string name="onboarding_title_claim">Запросить %s</string>
|
||||
<string name="onboarding_title_claim_progress">Запрашивается</string>
|
||||
<string name="onboarding_title_kyc_retry">Что-то пошло не так</string>
|
||||
<string name="onboarding_title_kyc_start">Подтвердите свою личность</string>
|
||||
<string name="onboarding_title_kyc_waiting">Подтверждение личности в процессе</string>
|
||||
<string name="onboarding_title_no_backup_cards">Нет резервных карт</string>
|
||||
<string name="onboarding_title_one_backup_card">Добавлена одна резервная карта</string>
|
||||
<string name="onboarding_title_pin">Код доступа</string>
|
||||
<string name="onboarding_title_register_wallet">Подключите свою карту</string>
|
||||
<string name="onboarding_title_scan_origin_card">Подготовьте свою карту</string>
|
||||
<string name="onboarding_title_two_backup_cards">Добавлены две резервные карты</string>
|
||||
<string name="onboarding_top_up_body">Пополните кошелек на любую сумму, чтобы начать пользоваться картой</string>
|
||||
|
|
@ -264,6 +268,9 @@
|
|||
<string name="organize_tokens_group">Группировка</string>
|
||||
<string name="organize_tokens_sort_by_balance">По балансу</string>
|
||||
<string name="organize_tokens_title">Сортировка токенов</string>
|
||||
<string name="organize_tokens_ungroup">Разгруппировать</string>
|
||||
<string name="receive_bottom_sheet_title">%1$s %2$s адрес в сети %3$s</string>
|
||||
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) в сети %3$s</string>
|
||||
<string name="referral_button_participate">Участвовать</string>
|
||||
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже.</string>
|
||||
|
|
@ -295,12 +302,6 @@
|
|||
<string name="reset_card_without_backup_to_factory_message">Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек.</string>
|
||||
<string name="russian_bank_card_warning_subtitle">У вас есть карта банка другой страны или платежной системы UnionPay?</string>
|
||||
<string name="russian_bank_card_warning_title">Карты банков РФ в данный момент не принимаются</string>
|
||||
<string name="saltpay_error_empty_backup_message">Приложите карту с логотипом Visa</string>
|
||||
<string name="saltpay_error_empty_backup_title">Внимание</string>
|
||||
<string name="saltpay_error_no_gas_message">Пожалуйста обратитесь в службу поддержки</string>
|
||||
<string name="saltpay_error_no_gas_title">Недостаточно средств для активации</string>
|
||||
<string name="saltpay_error_pin_weak_message">Данный Код доступа может быть легко взломан</string>
|
||||
<string name="saltpay_error_pin_weak_title">Ввод одинаковых цифр является не безопасным</string>
|
||||
<string name="save_user_wallet_agreement_access_description">Войдите в приложение и следите за своим балансом без сканирования карты</string>
|
||||
<string name="save_user_wallet_agreement_access_title">Доступ в приложение</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">Использовать биометрию</string>
|
||||
|
|
@ -317,11 +318,7 @@
|
|||
<string name="search_tokens_title">Поиск валют</string>
|
||||
<string name="send_amount_label">Сумма</string>
|
||||
<string name="send_destination_hint_address">Адрес</string>
|
||||
<string name="send_destination_hint_address_payid">Адрес или PayString</string>
|
||||
<string name="send_error_address_same_as_wallet">Адрес совпадает с адресом кошелька</string>
|
||||
<string name="send_error_payid_not_registered">PayString не зарегистрирован</string>
|
||||
<string name="send_error_payid_request_failed">Не удалось выполнить запрос PayString</string>
|
||||
<string name="send_error_payid_unsupported_by_blockchain">PayString не поддерживается блокчейном</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Недопустимый Tag. Он не будет добавлен в транзакцию.</string>
|
||||
<string name="send_extras_error_invalid_memo">Недопустимый Memo. Он не будет добавлен в транзакцию.</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
|
|
@ -333,7 +330,6 @@
|
|||
<string name="send_fee_picker_priority">Приоритетная</string>
|
||||
<string name="send_max_amount_label">Максимальная сумма</string>
|
||||
<string name="send_network_fee_title">Сетевая комиссия</string>
|
||||
<string name="send_title">Отправить</string>
|
||||
<string name="send_title_currency_format">Отправка %s</string>
|
||||
<string name="send_total_label">Всего</string>
|
||||
<string name="send_total_subtitle_asset_format">%1$s и %2$s будет отправлено</string>
|
||||
|
|
@ -350,6 +346,7 @@
|
|||
<string name="solana_rent_warning">Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату.</string>
|
||||
<string name="story_awe_description">Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте.</string>
|
||||
<string name="story_awe_title">Революционный аппаратный кошелек</string>
|
||||
<string name="story_backup_description">До **трех карт** с одним кошельком</string>
|
||||
<string name="story_backup_description_1">До</string>
|
||||
<string name="story_backup_description_2_bold">трех карт</string>
|
||||
<string name="story_backup_description_3">с одним кошельком</string>
|
||||
|
|
@ -358,6 +355,9 @@
|
|||
<string name="story_currencies_title">Тысячи криптовалют</string>
|
||||
<string name="story_finish_description">Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту к телефону.</string>
|
||||
<string name="story_finish_title">Кошелек для каждого</string>
|
||||
<string name="story_learn_description">Пройдите обучение и получите возможность купить кошелек Tangem со скидкой и токены 1inch в качестве бонуса</string>
|
||||
<string name="story_learn_learn">Пройти обучение</string>
|
||||
<string name="story_learn_title">Получите свой бонус</string>
|
||||
<string name="story_meet_borrow">Занимайте</string>
|
||||
<string name="story_meet_buy">Покупайте</string>
|
||||
<string name="story_meet_exchange">Обменивайте</string>
|
||||
|
|
@ -410,6 +410,7 @@
|
|||
<string name="token_details_hide_token">Скрыть токен</string>
|
||||
<string name="token_details_send_blocked_fee_format">%1$s — это токен в сети %2$s. Чтобы отправить транзакцию %3$s, необходимо пополнить баланс %4$s (%5$s) для оплаты комиссии сети.</string>
|
||||
<string name="token_details_send_blocked_tx_format">Пожалуйста, дождитесь завершения транзакции %s, чтобы иметь возможность отправить средства</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s токен в сети %%image%% %2$s</string>
|
||||
<string name="token_details_unable_hide_alert_message">Токен %1$s является основной валютой в сети %2$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Невозможно скрыть %s</string>
|
||||
<string name="token_item_no_rate">Нет цены</string>
|
||||
|
|
@ -424,6 +425,7 @@
|
|||
<string name="transaction_history_error_failed_to_load">Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию.</string>
|
||||
<string name="transaction_history_not_supported_description">История транзакций в настоящее время не поддерживается для этого блокчейна. Но не волнуйтесь, мы работаем над этим! А пока вы можете проверить ее в обозревателе.</string>
|
||||
<string name="transaction_history_transaction_from_address">от: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">на: %s</string>
|
||||
<string name="transaction_history_tx_in_progress">В процессе…</string>
|
||||
<string name="twin_error_same_card">Вы отсканировали ту же карту. Для создания twin-кошелька вам необходимо отсканировать карту с номером %d</string>
|
||||
<string name="twins_onboarding_description_format">Это карта, которую вы держите в руках. У парной карты номер %s.\n\nОбе карты можно использовать для вывода средств из этого кошелька.</string>
|
||||
|
|
@ -445,7 +447,6 @@
|
|||
<string name="user_wallet_list_single_header">Одновалютные</string>
|
||||
<string name="user_wallet_list_title">Мои кошельки</string>
|
||||
<string name="user_wallet_list_unlock_all">Разблокировать все с %s</string>
|
||||
<string name="wallet_address_button_create_payid">Создать PayString</string>
|
||||
<string name="wallet_address_button_explore">История транзакций</string>
|
||||
<string name="wallet_balance_blockchain_unreachable">Сеть недоступна</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуй позже.</string>
|
||||
|
|
@ -454,21 +455,26 @@
|
|||
<string name="wallet_balance_tx_in_progress">Транзакция подтверждается…</string>
|
||||
<string name="wallet_balance_verified">Подтвержденный баланс</string>
|
||||
<string name="wallet_button_actions">Действия</string>
|
||||
<string name="wallet_button_buy">Купить</string>
|
||||
<string name="wallet_button_sell">Продать</string>
|
||||
<string name="wallet_button_send">Отправить</string>
|
||||
<string name="wallet_choose_trade_action">Вы хотите купить или продать криптовалюту?</string>
|
||||
<string name="wallet_connect_alert_sign_message">Запрос на подпись сообщения.\n\n%s</string>
|
||||
<string name="wallet_connect_bnb_sign_message">Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s</string>
|
||||
<string name="wallet_connect_bnb_trade_order_message">Торговый ордер на %1$s\nЦена: %2$s\nСумма к получению: %3$s\nСумма к оплате: %4$s</string>
|
||||
<string name="wallet_connect_bnb_transaction_message">Детали транзакции:\nОт: %1$s\nК: %2$s\nСумма: %3$s</string>
|
||||
<string name="wallet_connect_bnb_transaction_signed">Транзакция с BNB успешно подписана и отправлена в Dapp.</string>
|
||||
<string name="wallet_connect_clipboard_alert">Буфер обмена содержит код WalletConnect. Использовать скопированное значение или отсканировать QR-код</string>
|
||||
<string name="wallet_connect_create_tx_message">Запрос на создание транзакции для %1$s\n%2$s\n\nСумма: %3$s\nКомиссия: %4$s\nВсего: %5$s\nБаланс: %6$s</string>
|
||||
<string name="wallet_connect_create_tx_not_enough_funds">Невозможно отправить транзакцию. Недостаточно средств.</string>
|
||||
<string name="wallet_connect_error_failed_to_connect">Не удалось установить сессию WalletConnect. Пожалуйста, повторите попытку позже.</string>
|
||||
<string name="wallet_connect_error_missing_blockchains">Не все токены добавлены в ваш список. Пожалуйста, добавьте их в начале, а потом попробуйте снова. Недостающие токены: \n</string>
|
||||
<string name="wallet_connect_error_sing_failed">Не удалось подписать сообщение.\nПожалуйста, попробуйте еще раз</string>
|
||||
<string name="wallet_connect_error_timeout">Не удалось установить сессию WalletConnect за отведённое время. Пожалуйста, повторите попытку позже.</string>
|
||||
<string name="wallet_connect_error_unsupported_blockchains">Запрос на подключение через WalletConnect содержит неподдерживаемые блокчеины. Неподдерживаемые блокчеины:\n</string>
|
||||
<string name="wallet_connect_error_unsupported_dapp">Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации.</string>
|
||||
<string name="wallet_connect_error_with_framework_message">Произошла непредвиденная ошибка. Сообщение ошибки: %s Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки.</string>
|
||||
<string name="wallet_connect_error_wrong_card_selected">Неверная карта выбрана в приложении Tangem</string>
|
||||
<string name="wallet_connect_failed_to_build_tx">Не удалось создать транзакцию из данных Dapp. Код: %s</string>
|
||||
<string name="wallet_connect_generic_error_with_code">Произошла непредвиденная ошибка. Код ошибки: %d Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки.</string>
|
||||
<string name="wallet_connect_message_signed">Сообщение было успешно подписано и отправлено в Dapp</string>
|
||||
<string name="wallet_connect_network_not_found_format">Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново.</string>
|
||||
<string name="wallet_connect_paste_from_clipboard">Вставить из буфера обмена</string>
|
||||
<string name="wallet_connect_request_session_start">Запрос на открытие сессии для\n%1$s\n\nСЕТЬ: %2$s\n\nURL: %3$s</string>
|
||||
|
|
@ -477,12 +483,18 @@
|
|||
<string name="wallet_connect_scanner_error_not_valid_card">Эту карту нельзя использовать с WalletConnect.</string>
|
||||
<string name="wallet_connect_scanner_error_unsupported_network">Сеть не поддерживается. Пожалуйста, выберите другую сеть.</string>
|
||||
<string name="wallet_connect_select_network">Выберите сеть</string>
|
||||
<string name="wallet_connect_service_no_chain_id">Dapp не предоставил необходимые данные для открытия сессии WalletConnect</string>
|
||||
<string name="wallet_connect_subtitle">Подключение к Dapps</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_transaction_signed">Транзакция успешно подписана и отправлена в Dapp</string>
|
||||
<string name="wallet_connect_transaction_signed_and_send">Транзакция успешно подписана и отправлена в блокчейн</string>
|
||||
<string name="wallet_connect_tx_not_found">Не удалось найти хэш транзакции</string>
|
||||
<string name="wallet_currency_subtitle">Сеть %s</string>
|
||||
<string name="wallet_error_no_account">Аккаунт не создан</string>
|
||||
<string name="wallet_error_unsupported_blockchain">Эта карта не поддерживается</string>
|
||||
<string name="wallet_error_unsupported_blockchain_subtitle">Ваша карта Tangem предназначена для работы с другим приложением. Пожалуйста, ознакомьтесь с названием и инструкциями на вашей карте и установите правильное приложение.</string>
|
||||
<string name="wallet_marketplace_block_title">Рыночная цена %s</string>
|
||||
<string name="wallet_marketprice_block_update_time">за 24 часа</string>
|
||||
<string name="wallet_network_group_title">Сеть %s</string>
|
||||
<string name="wallet_notification_address_copied">Адрес скопирован в буфер обмена</string>
|
||||
<string name="wallet_notification_no_internet">Нет соединения с интернетом</string>
|
||||
|
|
|
|||
|
|
@ -46,23 +46,29 @@
|
|||
<string name="common_balance">餘額: %s</string>
|
||||
<string name="common_biometric_authentication">生物識別</string>
|
||||
<string name="common_biometrics">生物</string>
|
||||
<string name="common_buy">購買</string>
|
||||
<string name="common_camera_denied_alert_message">您尚未授予相機訪問權限,請更改您的隱私設置</string>
|
||||
<string name="common_cancel">刪除</string>
|
||||
<string name="common_close">關閉</string>
|
||||
<string name="common_copy">複製</string>
|
||||
<string name="common_copy_address">複製地址</string>
|
||||
<string name="common_create">創造</string>
|
||||
<string name="common_delete">刪除</string>
|
||||
<string name="common_disabled">禁用</string>
|
||||
<string name="common_dislike">不喜歡</string>
|
||||
<string name="common_done">完成</string>
|
||||
<string name="common_enable">允許</string>
|
||||
<string name="common_enabled">啟用</string>
|
||||
<string name="common_exchange">交易</string>
|
||||
<string name="common_like">喜歡</string>
|
||||
<string name="common_no">否</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">主卡片</string>
|
||||
<string name="common_reject">拒絕</string>
|
||||
<string name="common_save_changes">保存設置</string>
|
||||
<string name="common_search">搜索</string>
|
||||
<string name="common_sell">銷售</string>
|
||||
<string name="common_send">發送</string>
|
||||
<string name="common_server_unavailable">伺服器不可用,請稍後在試</string>
|
||||
<string name="common_share">分享</string>
|
||||
<string name="common_sign">簽署</string>
|
||||
|
|
@ -74,6 +80,7 @@
|
|||
<string name="common_terms_and_conditions">條款和條件</string>
|
||||
<string name="common_transactions">交易</string>
|
||||
<string name="common_understand">我了解</string>
|
||||
<string name="common_unreachable">無法觸達</string>
|
||||
<string name="common_yes">是</string>
|
||||
<string name="contract_address_copied_message">已複製代幣地址</string>
|
||||
<string name="currency_subtitle_expanded">支持的網路</string>
|
||||
|
|
@ -164,11 +171,8 @@
|
|||
<string name="onboarding_button_claim">獲取</string>
|
||||
<string name="onboarding_button_continue_wallet">繼續至我的錢包</string>
|
||||
<string name="onboarding_button_finalize_backup">完成備份過程</string>
|
||||
<string name="onboarding_button_kyc_start">通過Utorg驗證</string>
|
||||
<string name="onboarding_button_kyc_waiting">刷新</string>
|
||||
<string name="onboarding_button_pin">設置PIN碼</string>
|
||||
<string name="onboarding_button_receive_crypto">接收貨幣</string>
|
||||
<string name="onboarding_button_register_wallet">註冊</string>
|
||||
<string name="onboarding_button_scan_origin_card">掃描主卡</string>
|
||||
<string name="onboarding_button_skip_backup">暫時略過</string>
|
||||
<string name="onboarding_button_what_does_it_mean">它是如何運作的?</string>
|
||||
|
|
@ -183,18 +187,9 @@
|
|||
<string name="onboarding_exit_alert_message">這此情況,您必須要重新開始</string>
|
||||
<string name="onboarding_exit_alert_title">您想要離開啟用程序嗎?</string>
|
||||
<string name="onboarding_getting_started">開始</string>
|
||||
<string name="onboarding_navbar_kyc_progress">驗證身分</string>
|
||||
<string name="onboarding_navbar_kyc_start">KYC</string>
|
||||
<string name="onboarding_navbar_pin">PIN 碼</string>
|
||||
<string name="onboarding_navbar_register_wallet">連接</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">創建備份</string>
|
||||
<string name="onboarding_saltpay_button_backup_origin">點擊 SaltPay 卡</string>
|
||||
<string name="onboarding_saltpay_subtitle_no_backup_cards">要開始備份過程,您必須添加 Tangem 卡片作為備份</string>
|
||||
<string name="onboarding_saltpay_subtitle_one_backup_card">通過創建訪問密碼完成備份</string>
|
||||
<string name="onboarding_saltpay_title_backup_card">點擊 Tangem 卡片</string>
|
||||
<string name="onboarding_saltpay_title_no_backup_card">沒有備份卡片</string>
|
||||
<string name="onboarding_saltpay_title_one_backup_card">備份卡片已準備完成</string>
|
||||
<string name="onboarding_saltpay_title_prepare_origin">準備SaltPay卡</string>
|
||||
<string name="onboarding_seed_button_read_more">閱讀更多關於助記詞的訊息</string>
|
||||
<string name="onboarding_seed_generate_message">按照下面給出的順序寫下這 12 個單詞,並將它們存放在安全秘密的地方。</string>
|
||||
<string name="onboarding_seed_generate_title">您的助記詞</string>
|
||||
|
|
@ -210,13 +205,8 @@
|
|||
<string name="onboarding_seed_user_validation_title">那麼,讓我們檢查一下</string>
|
||||
<string name="onboarding_subtitle_claim">要開始,只需將 wxDAI 獲取到您的錢包</string>
|
||||
<string name="onboarding_subtitle_claim_progress">這會花上幾秒</string>
|
||||
<string name="onboarding_subtitle_kyc_retry">請查看Email已獲得更多指示</string>
|
||||
<string name="onboarding_subtitle_kyc_start">要開始使用您的卡,您必須通過 KYC驗證</string>
|
||||
<string name="onboarding_subtitle_kyc_waiting">請等待驗證完成,您將收到電子郵件通知。通常最多需要 1 小時。您可以關閉該應用程序,稍後再回來</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">要開始備份過程,最多可添加兩張備份卡。</string>
|
||||
<string name="onboarding_subtitle_one_backup_card">您可以再添加一張卡或完成備份過程</string>
|
||||
<string name="onboarding_subtitle_pin">設置一個 4 位密碼。 \n它將用在付款時使用。</string>
|
||||
<string name="onboarding_subtitle_register_wallet">將您的卡片連接到去中心化支付系統</string>
|
||||
<string name="onboarding_subtitle_scan_backup_card_format">準備編號為 %s 的備份卡</string>
|
||||
<string name="onboarding_subtitle_scan_origin_card">準備主卡</string>
|
||||
<string name="onboarding_subtitle_scan_primary_card_format">準備編號為 %s 的主卡</string>
|
||||
|
|
@ -227,13 +217,9 @@
|
|||
<string name="onboarding_title_backup_card_format">備份卡 #%d</string>
|
||||
<string name="onboarding_title_claim">獲取%s</string>
|
||||
<string name="onboarding_title_claim_progress">獲取中</string>
|
||||
<string name="onboarding_title_kyc_retry">有東西出錯了</string>
|
||||
<string name="onboarding_title_kyc_start">確認你的身分</string>
|
||||
<string name="onboarding_title_kyc_waiting">KYC 認證中</string>
|
||||
<string name="onboarding_title_no_backup_cards">沒有備用卡</string>
|
||||
<string name="onboarding_title_one_backup_card">添加了一張備用卡</string>
|
||||
<string name="onboarding_title_pin">PIN 碼</string>
|
||||
<string name="onboarding_title_register_wallet">連接你的錢包</string>
|
||||
<string name="onboarding_title_scan_origin_card">準備好你的卡片</string>
|
||||
<string name="onboarding_title_two_backup_cards">添加了兩張備用卡</string>
|
||||
<string name="onboarding_top_up_body">要開始使用,只需為錢包充值任意金額</string>
|
||||
|
|
@ -279,12 +265,6 @@
|
|||
<string name="reset_card_without_backup_to_factory_message">恢復原廠設置將從所選卡中完全刪除錢包並將其從應用程序中刪除。您將無法恢復當前錢包</string>
|
||||
<string name="russian_bank_card_warning_subtitle">您有其他國家的銀行卡或銀聯卡嗎?</string>
|
||||
<string name="russian_bank_card_warning_title">目前不接受俄羅斯銀行卡</string>
|
||||
<string name="saltpay_error_empty_backup_message">輕觸帶有 visa 標誌的卡片</string>
|
||||
<string name="saltpay_error_empty_backup_title">注意</string>
|
||||
<string name="saltpay_error_no_gas_message">請聯繫客服</string>
|
||||
<string name="saltpay_error_no_gas_title">沒有激活資金</string>
|
||||
<string name="saltpay_error_pin_weak_message">這樣的 PIN 很容易被暴力破解</string>
|
||||
<string name="saltpay_error_pin_weak_title">四個相同的數字不安全</string>
|
||||
<string name="save_user_wallet_agreement_access_description">登錄應用程序並在不掃描卡片的情況下檢查您的資產</string>
|
||||
<string name="save_user_wallet_agreement_access_title">訪問應用程序</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">允許使用生物辨識</string>
|
||||
|
|
@ -301,11 +281,7 @@
|
|||
<string name="search_tokens_title">搜尋代幣</string>
|
||||
<string name="send_amount_label">數量</string>
|
||||
<string name="send_destination_hint_address">地址</string>
|
||||
<string name="send_destination_hint_address_payid">地址或 PayString</string>
|
||||
<string name="send_error_address_same_as_wallet">地址與錢包地址相同</string>
|
||||
<string name="send_error_payid_not_registered">PayString 未註冊</string>
|
||||
<string name="send_error_payid_request_failed">PayString 請求失敗</string>
|
||||
<string name="send_error_payid_unsupported_by_blockchain">PayString 不被區塊鏈支持</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">標籤無效。它不會被添加到交易中</string>
|
||||
<string name="send_extras_error_invalid_memo">Memo無效。 它不會被添加到交易中</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
|
|
@ -317,7 +293,6 @@
|
|||
<string name="send_fee_picker_priority">優先</string>
|
||||
<string name="send_max_amount_label">最大值</string>
|
||||
<string name="send_network_fee_title">網路費</string>
|
||||
<string name="send_title">發送</string>
|
||||
<string name="send_title_currency_format">發送 %s</string>
|
||||
<string name="send_total_label">總計</string>
|
||||
<string name="send_total_subtitle_asset_format">%1$s 和 %2$s 將被發送</string>
|
||||
|
|
@ -333,6 +308,7 @@
|
|||
<string name="solana_rent_warning">Solana 網絡每 2 天收取 %1$s 的費用。無法付此費用的帳戶將從網絡中清除。向您的帳戶存入超過 %2$s 即可免費使用</string>
|
||||
<string name="story_awe_description">安全地存儲您的加密貨幣,同時將私鑰保存在您的卡中</string>
|
||||
<string name="story_awe_title">創新式的硬體錢包</string>
|
||||
<string name="story_backup_description">最多 **3張實體卡片** 到一個錢包</string>
|
||||
<string name="story_backup_description_1">最多</string>
|
||||
<string name="story_backup_description_2_bold">3張實體卡片</string>
|
||||
<string name="story_backup_description_3">到一個錢包</string>
|
||||
|
|
@ -415,7 +391,6 @@
|
|||
<string name="user_wallet_list_single_header">單一幣種</string>
|
||||
<string name="user_wallet_list_title">我的錢包</string>
|
||||
<string name="user_wallet_list_unlock_all">用 %s 解鎖全部</string>
|
||||
<string name="wallet_address_button_create_payid">創建支付字符串</string>
|
||||
<string name="wallet_address_button_explore">交易記錄</string>
|
||||
<string name="wallet_balance_blockchain_unreachable">網路無法使用</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">區塊鍊無法使用。稍後再試</string>
|
||||
|
|
@ -424,21 +399,26 @@
|
|||
<string name="wallet_balance_tx_in_progress">交易進行中</string>
|
||||
<string name="wallet_balance_verified">檢視餘額</string>
|
||||
<string name="wallet_button_actions">動作</string>
|
||||
<string name="wallet_button_buy">購買</string>
|
||||
<string name="wallet_button_sell">銷售</string>
|
||||
<string name="wallet_button_send">發送</string>
|
||||
<string name="wallet_choose_trade_action">您想要購買或賣出交易貨幣?</string>
|
||||
<string name="wallet_connect_alert_sign_message">請求籤署消息。%s</string>
|
||||
<string name="wallet_connect_bnb_sign_message">Dapp %1$s,請求\n簽署 BNB 交易。\n%2$s</string>
|
||||
<string name="wallet_connect_bnb_trade_order_message">%1$s 的交易訂單\n價格: %2$s\n接收金額:%3$s\n支付數量: %4$s</string>
|
||||
<string name="wallet_connect_bnb_transaction_message">交易明細:\n從: %1$s\n到: %2$s\n數量: %3$s</string>
|
||||
<string name="wallet_connect_bnb_transaction_signed">BNB 交易已成功簽署並發送至 Dapp</string>
|
||||
<string name="wallet_connect_clipboard_alert">剪貼板包含 WalletConnect 代碼。使用複制的值或掃描二維碼</string>
|
||||
<string name="wallet_connect_create_tx_message">請求為 %1$s 創建交易\n%2$s\n\n數量: %3$s\n費用: %4$s\n全部的: %5$s\n餘額: %6$s</string>
|
||||
<string name="wallet_connect_create_tx_not_enough_funds">無法交易,無足夠資金</string>
|
||||
<string name="wallet_connect_error_failed_to_connect">未能建立 WalletConnect 連接。請稍後再試</string>
|
||||
<string name="wallet_connect_error_missing_blockchains">並非所有代幣都已添加到您的列表中。請先添加它們,然後重試。缺少標記:\n</string>
|
||||
<string name="wallet_connect_error_sing_failed">無法簽署消息。請重試</string>
|
||||
<string name="wallet_connect_error_timeout">無法建立 WalletConnect 連接:超時錯誤。請稍後再試</string>
|
||||
<string name="wallet_connect_error_unsupported_blockchains">會話請求包含不支持 WalletConnect 連接的區塊鏈。不支持的區塊鏈:</string>
|
||||
<string name="wallet_connect_error_unsupported_blockchains">會話請求包含不支持 WalletConnect 連接的區塊鏈。不支持的區塊鏈:\n</string>
|
||||
<string name="wallet_connect_error_unsupported_dapp">由於技術問題,無法與此 Dapp 建立連接</string>
|
||||
<string name="wallet_connect_error_with_framework_message">我們遇到了未知錯誤。錯誤信息:%s。如果問題仍然存在-請隨時聯繫我們的客服人員</string>
|
||||
<string name="wallet_connect_error_wrong_card_selected">在 Tangem App 中選擇了錯誤的卡</string>
|
||||
<string name="wallet_connect_failed_to_build_tx">無法從 Dapp 數據創建交易。代碼: %s</string>
|
||||
<string name="wallet_connect_generic_error_with_code">我們遇到了未知錯誤。錯誤代碼:%d。如果問題仍然存在-請隨時聯繫我們的支持人員</string>
|
||||
<string name="wallet_connect_message_signed">消息已成功簽名並發送至Dapp</string>
|
||||
<string name="wallet_connect_network_not_found_format">沒有 %s 網路,請先加入後再試一次</string>
|
||||
<string name="wallet_connect_paste_from_clipboard">從剪貼板貼上</string>
|
||||
<string name="wallet_connect_request_session_start">請求開始會話\n%1$s\n\n網絡: %2$s\n\n網址:%3$s</string>
|
||||
|
|
@ -447,8 +427,12 @@
|
|||
<string name="wallet_connect_scanner_error_not_valid_card">此卡不能用於建立 WalletConnect 連接</string>
|
||||
<string name="wallet_connect_scanner_error_unsupported_network">不支持此網絡。請選擇其他網絡</string>
|
||||
<string name="wallet_connect_select_network">選擇網路</string>
|
||||
<string name="wallet_connect_service_no_chain_id">Dapp 沒有提供必要的數據來建立 WalletConnect 連接</string>
|
||||
<string name="wallet_connect_subtitle">連結到Dapps</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_transaction_signed">交易已成功簽署並發送至 Dapp</string>
|
||||
<string name="wallet_connect_transaction_signed_and_send">交易已成功簽署並發送至區塊鏈</string>
|
||||
<string name="wallet_connect_tx_not_found">未能找到交易哈希</string>
|
||||
<string name="wallet_currency_subtitle">%s 網路</string>
|
||||
<string name="wallet_error_no_account">帳號尚未被創造</string>
|
||||
<string name="wallet_error_unsupported_blockchain">不支持此卡</string>
|
||||
|
|
|
|||
|
|
@ -50,27 +50,41 @@
|
|||
<string name="common_balance">Balance: %s</string>
|
||||
<string name="common_biometric_authentication">biometric authentication</string>
|
||||
<string name="common_biometrics">biometrics</string>
|
||||
<string name="common_buy">Buy</string>
|
||||
<string name="common_camera_denied_alert_message">You have not given access to your camera, please adjust your privacy settings</string>
|
||||
<string name="common_cancel">Cancel</string>
|
||||
<string name="common_close">Close</string>
|
||||
<string name="common_copy">Copy</string>
|
||||
<string name="common_copy_address">Copy address</string>
|
||||
<string name="common_create">Create</string>
|
||||
<string name="common_delete">Delete</string>
|
||||
<string name="common_disabled">Disabled</string>
|
||||
<string name="common_dislike">Dislike</string>
|
||||
<string name="common_done">Done</string>
|
||||
<string name="common_enable">Enable</string>
|
||||
<string name="common_enabled">Enabled</string>
|
||||
<string name="common_exchange">Exchange</string>
|
||||
<string name="common_explore_transaction_history">Explore transaction history</string>
|
||||
<string name="common_explorer">Explorer</string>
|
||||
<string name="common_like">Like</string>
|
||||
<string name="common_main_network">Main network</string>
|
||||
<string name="common_no">No</string>
|
||||
<string name="common_no_data">No data</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">Primary Card</string>
|
||||
<string name="common_receive">Receive</string>
|
||||
<string name="common_reject">Reject</string>
|
||||
<string name="common_reload">Reload</string>
|
||||
<string name="common_save_changes">Save changes</string>
|
||||
<string name="common_search">Search</string>
|
||||
<string name="common_seed_phrase">Seed phrase</string>
|
||||
<string name="common_sell">Sell</string>
|
||||
<string name="common_send">Send</string>
|
||||
<string name="common_server_unavailable">The server is not available, please try again later</string>
|
||||
<string name="common_share">Share</string>
|
||||
<string name="common_sign">Sign</string>
|
||||
<string name="common_sign_and_send">Sign and send</string>
|
||||
<string name="common_stake">Stake</string>
|
||||
<string name="common_start">Start</string>
|
||||
<string name="common_submit">Submit</string>
|
||||
<string name="common_success">Success</string>
|
||||
|
|
@ -79,6 +93,7 @@
|
|||
<string name="common_transactions">Transactions</string>
|
||||
<string name="common_transfer">Transfer</string>
|
||||
<string name="common_understand">I understand</string>
|
||||
<string name="common_unreachable">Unreachable</string>
|
||||
<string name="common_yes">Yes</string>
|
||||
<string name="contract_address_copied_message">Contract address copied!</string>
|
||||
<string name="currency_subtitle_expanded">Available networks</string>
|
||||
|
|
@ -143,11 +158,20 @@
|
|||
<string name="initial_message_tap_header">Tap the card</string>
|
||||
<string name="internal_error_wallet_manager_not_found">Internal error: wallet manager not found</string>
|
||||
<string name="key_invalidated_warning_description">You have updated biometrics, scan your card to enter</string>
|
||||
<string name="main_get_bonus_subtitle">You have completed the training and can get your 1inch tokens</string>
|
||||
<string name="main_get_bonus_title">Get a bonus</string>
|
||||
<plurals name="main_learn_subtitle">
|
||||
<item quantity="one">Complete the training and get %d 1inch token on your wallet</item>
|
||||
<item quantity="other">Complete the training and get %d 1inch tokens on your wallet</item>
|
||||
</plurals>
|
||||
<string name="main_learn_title">Learn & Earn</string>
|
||||
<string name="main_manage_tokens">Manage tokens</string>
|
||||
<string name="main_no_backup_warning_subtitle">To protect your assets, we advise you to carry out this procedure</string>
|
||||
<string name="main_no_backup_warning_title">Your wallet has not been backed up</string>
|
||||
<string name="main_page_balance">Total balance</string>
|
||||
<string name="main_processing_full_amount">The amount does not include some of your funds</string>
|
||||
<string name="main_promotion_credited">1inch tokens will be credited to your %s wallet address within 24 hours</string>
|
||||
<string name="main_promotion_no_purchase">There was no purchase of a wallet using your promo code, which means you cannot receive a bonus. Buy Tangem wallet, scan it in the app, and get the bonus.</string>
|
||||
<string name="main_scan_card_warning_view_subtitle">To access all the networks you need to scan the card</string>
|
||||
<string name="main_scan_card_warning_view_title">Scan your card</string>
|
||||
<string name="onboarding_access_code_feature_1_description">You have to set up a single access code to protect all your wallets</string>
|
||||
|
|
@ -171,11 +195,8 @@
|
|||
<string name="onboarding_button_claim">Claim</string>
|
||||
<string name="onboarding_button_continue_wallet">Continue to my wallet</string>
|
||||
<string name="onboarding_button_finalize_backup">Finalize the backup</string>
|
||||
<string name="onboarding_button_kyc_start">Verify via Utorg</string>
|
||||
<string name="onboarding_button_kyc_waiting">Refresh</string>
|
||||
<string name="onboarding_button_pin">Set PIN code</string>
|
||||
<string name="onboarding_button_receive_crypto">Receive crypto</string>
|
||||
<string name="onboarding_button_register_wallet">Register</string>
|
||||
<string name="onboarding_button_scan_origin_card">Scan primary card</string>
|
||||
<string name="onboarding_button_skip_backup">Skip for later</string>
|
||||
<string name="onboarding_button_what_does_it_mean">How does it work?</string>
|
||||
|
|
@ -191,18 +212,9 @@
|
|||
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
|
||||
<string name="onboarding_getting_started">Getting started</string>
|
||||
<string name="onboarding_linking_error_card_with_wallets">Another wallet has already been created on the card you\'re trying to add. Do you want to reset it and use the card for a new wallet?</string>
|
||||
<string name="onboarding_navbar_kyc_progress">Verify your identity</string>
|
||||
<string name="onboarding_navbar_kyc_start">KYC</string>
|
||||
<string name="onboarding_navbar_pin">Pin code</string>
|
||||
<string name="onboarding_navbar_register_wallet">Connect</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Creating a backup</string>
|
||||
<string name="onboarding_saltpay_button_backup_origin">Tap the SaltPay card</string>
|
||||
<string name="onboarding_saltpay_subtitle_no_backup_cards">To start the backup process you have to add the Tangem card as your backup</string>
|
||||
<string name="onboarding_saltpay_subtitle_one_backup_card">Finalize the backup process by creating an access code</string>
|
||||
<string name="onboarding_saltpay_title_backup_card">Tap the Tangem card</string>
|
||||
<string name="onboarding_saltpay_title_no_backup_card">No backup card</string>
|
||||
<string name="onboarding_saltpay_title_one_backup_card">Backup card ready</string>
|
||||
<string name="onboarding_saltpay_title_prepare_origin">Prepare the SaltPay card</string>
|
||||
<string name="onboarding_seed_button_read_more">Read more about seed phrases</string>
|
||||
<string name="onboarding_seed_generate_message">Write these 12 words down in the order given below and store them in a safe and secret place.</string>
|
||||
<string name="onboarding_seed_generate_title">Your secret phrase</string>
|
||||
|
|
@ -218,13 +230,8 @@
|
|||
<string name="onboarding_seed_user_validation_title">So, let’s check</string>
|
||||
<string name="onboarding_subtitle_claim">To get started, simply claim wxDAI to your wallet</string>
|
||||
<string name="onboarding_subtitle_claim_progress">It will take a few seconds</string>
|
||||
<string name="onboarding_subtitle_kyc_retry">Please check your email for further instructions</string>
|
||||
<string name="onboarding_subtitle_kyc_start">To start using your card you have to pass the KYC process</string>
|
||||
<string name="onboarding_subtitle_kyc_waiting">Please wait until the verification is completed. You\'ll be notified via email. Usually it takes up to 1 hour. You can close the app and come back later.</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">To start the backup process add up to two backup cards.</string>
|
||||
<string name="onboarding_subtitle_one_backup_card">You can add one more card or finalize the backup process</string>
|
||||
<string name="onboarding_subtitle_pin">Set up a 4-digit code.\nIt will be used for payments.</string>
|
||||
<string name="onboarding_subtitle_register_wallet">Connect your card to the decentralized payment system</string>
|
||||
<string name="onboarding_subtitle_scan_backup_card_format">Prepare the backup card with number %s</string>
|
||||
<string name="onboarding_subtitle_scan_origin_card">Prepare the primary card</string>
|
||||
<string name="onboarding_subtitle_scan_primary_card_format">Prepare the primary card with number %s</string>
|
||||
|
|
@ -235,13 +242,9 @@
|
|||
<string name="onboarding_title_backup_card_format">Backup card #%d</string>
|
||||
<string name="onboarding_title_claim">Claim %s</string>
|
||||
<string name="onboarding_title_claim_progress">Claiming</string>
|
||||
<string name="onboarding_title_kyc_retry">Something went wrong</string>
|
||||
<string name="onboarding_title_kyc_start">Verify your identity</string>
|
||||
<string name="onboarding_title_kyc_waiting">KYC is in progress</string>
|
||||
<string name="onboarding_title_no_backup_cards">No backup cards</string>
|
||||
<string name="onboarding_title_one_backup_card">One backup card added</string>
|
||||
<string name="onboarding_title_pin">PIN Code</string>
|
||||
<string name="onboarding_title_register_wallet">Connect your card</string>
|
||||
<string name="onboarding_title_scan_origin_card">Prepare your card</string>
|
||||
<string name="onboarding_title_two_backup_cards">Two backup cards added</string>
|
||||
<string name="onboarding_top_up_body">To get started, simply top up the wallet with any amount</string>
|
||||
|
|
@ -262,6 +265,10 @@
|
|||
<string name="organize_tokens_group">Group</string>
|
||||
<string name="organize_tokens_sort_by_balance">By balance</string>
|
||||
<string name="organize_tokens_title">Organize tokens</string>
|
||||
<string name="organize_tokens_ungroup">Ungroup</string>
|
||||
<string name="receive_bottom_sheet_title">%1$s %2$s address on %3$s network</string>
|
||||
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) on %3$s network</string>
|
||||
<string name="receive_bottom_sheet_warning_message_full">Send only %s to this address. Sending any other currency will result in its irreversible loss.</string>
|
||||
<string name="referral_button_participate">Participate</string>
|
||||
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Error code: %s. Please try again later.</string>
|
||||
|
|
@ -291,12 +298,6 @@
|
|||
<string name="reset_card_without_backup_to_factory_message">Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet.</string>
|
||||
<string name="russian_bank_card_warning_subtitle">Do you have a bank card of another country or a UnionPay card?</string>
|
||||
<string name="russian_bank_card_warning_title">Russian bank cards are not accepted at the moment</string>
|
||||
<string name="saltpay_error_empty_backup_message">Tap the card with the visa logo</string>
|
||||
<string name="saltpay_error_empty_backup_title">Attention</string>
|
||||
<string name="saltpay_error_no_gas_message">Please contact support</string>
|
||||
<string name="saltpay_error_no_gas_title">No funds for activation</string>
|
||||
<string name="saltpay_error_pin_weak_message">Such a PIN can be brute-forced easily</string>
|
||||
<string name="saltpay_error_pin_weak_title">Four identical digits isn\'t safe</string>
|
||||
<string name="save_user_wallet_agreement_access_description">Log into the app and check your balance without scanning the card</string>
|
||||
<string name="save_user_wallet_agreement_access_title">Access the app</string>
|
||||
<string name="save_user_wallet_agreement_allow_biometrics">Allow to use biometrics</string>
|
||||
|
|
@ -313,11 +314,7 @@
|
|||
<string name="search_tokens_title">Search tokens</string>
|
||||
<string name="send_amount_label">Amount</string>
|
||||
<string name="send_destination_hint_address">Address</string>
|
||||
<string name="send_destination_hint_address_payid">Address or PayString</string>
|
||||
<string name="send_error_address_same_as_wallet">Address is the same as wallet address</string>
|
||||
<string name="send_error_payid_not_registered">PayString not registered</string>
|
||||
<string name="send_error_payid_request_failed">PayString request failed</string>
|
||||
<string name="send_error_payid_unsupported_by_blockchain">PayString unsupported by blockchain</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Invalid Tag. It won\'t be added to the transaction.</string>
|
||||
<string name="send_extras_error_invalid_memo">Invalid Memo. It won\'t be added to the transaction.</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
|
|
@ -329,7 +326,6 @@
|
|||
<string name="send_fee_picker_priority">Priority</string>
|
||||
<string name="send_max_amount_label">Maximum amount</string>
|
||||
<string name="send_network_fee_title">Network fee</string>
|
||||
<string name="send_title">Send</string>
|
||||
<string name="send_title_currency_format">Sending %s</string>
|
||||
<string name="send_total_label">Total</string>
|
||||
<string name="send_total_subtitle_asset_format">%1$s and %2$s will be sent</string>
|
||||
|
|
@ -346,6 +342,7 @@
|
|||
<string name="solana_rent_warning">Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free.</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
<string name="story_awe_title">Revolutionary Hardware Wallet</string>
|
||||
<string name="story_backup_description">Up to **3 physical cards** to one wallet</string>
|
||||
<string name="story_backup_description_1">Up to</string>
|
||||
<string name="story_backup_description_2_bold">3 physical cards</string>
|
||||
<string name="story_backup_description_3">to one wallet</string>
|
||||
|
|
@ -354,6 +351,9 @@
|
|||
<string name="story_currencies_title">Thousands of Currencies</string>
|
||||
<string name="story_finish_description">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_learn_description">Complete the training, get the opportunity to buy Tangem wallet with a discount and receive 1inch tokens on your wallet</string>
|
||||
<string name="story_learn_learn">Learn</string>
|
||||
<string name="story_learn_title">Learn and get a bonus</string>
|
||||
<string name="story_meet_borrow">Borrow</string>
|
||||
<string name="story_meet_buy">Buy</string>
|
||||
<string name="story_meet_exchange">Exchange</string>
|
||||
|
|
@ -404,6 +404,7 @@
|
|||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_send_blocked_fee_format">%1$s is a token in the %2$s network. To make a %3$s transaction you need to deposit some %4$s (%5$s) to cover the network fee.</string>
|
||||
<string name="token_details_send_blocked_tx_format">Please wait for %s transaction to complete to be able to send funds</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s token in %%image%% %2$s network</string>
|
||||
<string name="token_details_unable_hide_alert_message">The %1$s token is the main currency on the %2$s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
|
||||
<string name="token_item_no_rate">No rate</string>
|
||||
|
|
@ -438,7 +439,6 @@
|
|||
<string name="user_wallet_list_single_header">Single-currency</string>
|
||||
<string name="user_wallet_list_title">My Wallets</string>
|
||||
<string name="user_wallet_list_unlock_all">Unlock all with %s</string>
|
||||
<string name="wallet_address_button_create_payid">Create PayString</string>
|
||||
<string name="wallet_address_button_explore">Transaction history</string>
|
||||
<string name="wallet_balance_blockchain_unreachable">Network is unreachable</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Blockchain is unreachable. Try later</string>
|
||||
|
|
@ -447,21 +447,26 @@
|
|||
<string name="wallet_balance_tx_in_progress">Transaction is in progress…</string>
|
||||
<string name="wallet_balance_verified">Verified Balance</string>
|
||||
<string name="wallet_button_actions">Actions</string>
|
||||
<string name="wallet_button_buy">Buy</string>
|
||||
<string name="wallet_button_sell">Sell</string>
|
||||
<string name="wallet_button_send">Send</string>
|
||||
<string name="wallet_choose_trade_action">Do you want to buy or sell crypto?</string>
|
||||
<string name="wallet_connect_alert_sign_message">Requesting to sign a message.\n\n%s</string>
|
||||
<string name="wallet_connect_bnb_sign_message">Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s</string>
|
||||
<string name="wallet_connect_bnb_trade_order_message">Trade order for %1$s\nPrice: %2$s\nAmount to receive: %3$s\nAmount to pay: %4$s</string>
|
||||
<string name="wallet_connect_bnb_transaction_message">Transaction details:\nFrom: %1$s\nTo: %2$s\nAmount: %3$s</string>
|
||||
<string name="wallet_connect_bnb_transaction_signed">The BNB transaction has been successfully signed and sent to the Dapp</string>
|
||||
<string name="wallet_connect_clipboard_alert">Clipboard contain WalletConnect code. Use copied value or scan QR-code</string>
|
||||
<string name="wallet_connect_create_tx_message">Request to create transaction for %1$s\n%2$s\n\nAmount: %3$s\nFee: %4$s\nTotal: %5$s\nBalance: %6$s</string>
|
||||
<string name="wallet_connect_create_tx_not_enough_funds">Can\'t send transaction. Not enough funds.</string>
|
||||
<string name="wallet_connect_error_failed_to_connect">Failed to establish WalletConnect session. Please, try again later.</string>
|
||||
<string name="wallet_connect_error_missing_blockchains">Not all tokens were added to your list. Please add them first and try again. Missing tokens:\n</string>
|
||||
<string name="wallet_connect_error_sing_failed">Failed to sign message.\nPlease, try again</string>
|
||||
<string name="wallet_connect_error_timeout">Failed to establish WalletConnect session: timeout error. Please, try again later.</string>
|
||||
<string name="wallet_connect_error_unsupported_blockchains">Session request contains unsupported blockchains for WalletConnect connection. Unsupported blockchains:\n</string>
|
||||
<string name="wallet_connect_error_unsupported_dapp">Connection with this Dapp cannot be established due to its technical implementation.</string>
|
||||
<string name="wallet_connect_error_with_framework_message">We\'ve encountered unknown error. Error message: %s. If the problem persists — feel free to contact our support</string>
|
||||
<string name="wallet_connect_error_wrong_card_selected">Wrong card selected in Tangem App</string>
|
||||
<string name="wallet_connect_failed_to_build_tx">Failed to create transaction from Dapp data. Code: %s</string>
|
||||
<string name="wallet_connect_generic_error_with_code">We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support</string>
|
||||
<string name="wallet_connect_message_signed">The message has been successfully signed and sent to the Dapp</string>
|
||||
<string name="wallet_connect_network_not_found_format">%s network not found. Please, add it first and try again.</string>
|
||||
<string name="wallet_connect_paste_from_clipboard">Paste from clipboard</string>
|
||||
<string name="wallet_connect_request_session_start">Request to start a session for\n%1$s\n\nNETWORK: %2$s\n\nURL: %3$s</string>
|
||||
|
|
@ -470,12 +475,18 @@
|
|||
<string name="wallet_connect_scanner_error_not_valid_card">This card can\'t be used to establish WalletConnect session</string>
|
||||
<string name="wallet_connect_scanner_error_unsupported_network">This network is not supported. Please select another network.</string>
|
||||
<string name="wallet_connect_select_network">Select network</string>
|
||||
<string name="wallet_connect_service_no_chain_id">Dapp didn\'t provide essential data to establish WalletConnect session</string>
|
||||
<string name="wallet_connect_subtitle">Connect to Dapps</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_transaction_signed">The transaction has been successfully signed and sent to the Dapp</string>
|
||||
<string name="wallet_connect_transaction_signed_and_send">The transaction has been succesfully signed and sent to the blockchain</string>
|
||||
<string name="wallet_connect_tx_not_found">Failed to find transaction hash</string>
|
||||
<string name="wallet_currency_subtitle">%s network</string>
|
||||
<string name="wallet_error_no_account">Account is not created</string>
|
||||
<string name="wallet_error_unsupported_blockchain">This card is not supported</string>
|
||||
<string name="wallet_error_unsupported_blockchain_subtitle">Your Tangem card was made to work with a different application. Please see the name and instructions on your card, and install the correct app</string>
|
||||
<string name="wallet_marketplace_block_title">%s Market Price</string>
|
||||
<string name="wallet_marketprice_block_update_time">last 24h</string>
|
||||
<string name="wallet_network_group_title">%s network</string>
|
||||
<string name="wallet_notification_address_copied">Address was copied to clipboard</string>
|
||||
<string name="wallet_notification_no_internet">No internet connection</string>
|
||||
|
|
|
|||
|
|
@ -2,19 +2,17 @@ package com.tangem.core.ui.components
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
// region TextButton
|
||||
|
|
@ -26,7 +24,7 @@ fun TextButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier,
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
icon = TangemButtonIconPosition.None,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
|
|
@ -49,7 +47,7 @@ fun TextButtonIconStart(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
icon = TangemButtonIconPosition.Start(iconResId),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
|
|
@ -63,7 +61,7 @@ fun WarningTextButton(text: String, onClick: () -> Unit, modifier: Modifier = Mo
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
icon = TangemButtonIconPosition.None,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
|
|
@ -85,7 +83,7 @@ fun PrimaryButton(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
icon = TangemButtonIconPosition.None,
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = enabled,
|
||||
|
|
@ -108,7 +106,7 @@ fun PrimaryButtonIconEnd(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.End(iconResId),
|
||||
icon = TangemButtonIconPosition.End(iconResId),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = enabled,
|
||||
|
|
@ -131,7 +129,7 @@ fun PrimaryButtonIconStart(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
icon = TangemButtonIconPosition.Start(iconResId),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = enabled,
|
||||
|
|
@ -152,7 +150,7 @@ fun SecondaryButton(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.None,
|
||||
icon = TangemButtonIconPosition.None,
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = enabled,
|
||||
|
|
@ -175,7 +173,7 @@ fun SecondaryButtonIconEnd(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.End(iconResId),
|
||||
icon = TangemButtonIconPosition.End(iconResId),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = enabled,
|
||||
|
|
@ -198,7 +196,7 @@ fun SecondaryButtonIconStart(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
icon = TangemButtonIconPosition.Start(iconResId),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = enabled,
|
||||
|
|
@ -214,7 +212,7 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif
|
|||
modifier = modifier,
|
||||
text = text,
|
||||
textStyle = TangemTheme.typography.subtitle2,
|
||||
icon = TangemButtonIcon.End(iconResId = R.drawable.ic_chevron_24),
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.selectorButtonColors,
|
||||
showProgress = false,
|
||||
|
|
@ -224,362 +222,6 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif
|
|||
}
|
||||
// endregion Other
|
||||
|
||||
// region Action
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-305&t=3z98eFnTeyIx5TH5-4)
|
||||
* */
|
||||
@Composable
|
||||
fun RoundedActionButton(
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
size = TangemButtonSize.RoundedAction,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=1208-1395&t=3z98eFnTeyIx5TH5-4)
|
||||
* */
|
||||
@Composable
|
||||
fun ActionButton(
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
size = TangemButtonSize.Action,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as [RoundedActionButton] but colored in primary background color
|
||||
* */
|
||||
@Composable
|
||||
fun BackgroundActionButton(
|
||||
text: String,
|
||||
@DrawableRes iconResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.backgroundButtonColors,
|
||||
size = TangemButtonSize.RoundedAction,
|
||||
)
|
||||
}
|
||||
// endregion Action
|
||||
|
||||
// region Defaults
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun TangemButton(
|
||||
text: String,
|
||||
icon: TangemButtonIcon,
|
||||
onClick: () -> Unit,
|
||||
colors: ButtonColors,
|
||||
showProgress: Boolean,
|
||||
enabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
size: TangemButtonSize = TangemButtonSize.Default,
|
||||
elevation: ButtonElevation = TangemButtonsDefaults.elevation,
|
||||
textStyle: TextStyle = TangemTheme.typography.button,
|
||||
) {
|
||||
Button(
|
||||
modifier = modifier.heightIn(min = size.toHeightDp()),
|
||||
onClick = { if (!showProgress) onClick() },
|
||||
enabled = enabled,
|
||||
elevation = elevation,
|
||||
shape = size.toShape(),
|
||||
colors = colors,
|
||||
contentPadding = size.toContentPadding(icon = icon),
|
||||
) {
|
||||
ButtonContent(
|
||||
text = text,
|
||||
textStyle = textStyle,
|
||||
buttonIcon = icon,
|
||||
colors = colors,
|
||||
showProgress = showProgress,
|
||||
enabled = enabled,
|
||||
size = size,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun ButtonContent(
|
||||
text: String,
|
||||
textStyle: TextStyle,
|
||||
buttonIcon: TangemButtonIcon,
|
||||
colors: ButtonColors,
|
||||
size: TangemButtonSize,
|
||||
enabled: Boolean,
|
||||
showProgress: Boolean,
|
||||
) {
|
||||
val icon = @Composable { iconResId: Int ->
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = painterResource(id = iconResId),
|
||||
tint = colors.contentColor(enabled = enabled).value,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
if (showProgress) {
|
||||
Box(modifier = Modifier.wrapContentSize()) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(TangemTheme.dimens.size24),
|
||||
color = colors.contentColor(enabled = enabled).value,
|
||||
strokeWidth = TangemTheme.dimens.size4,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(size.toIconPadding()),
|
||||
) {
|
||||
if (buttonIcon is TangemButtonIcon.Start) {
|
||||
icon(buttonIcon.iconResId)
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
style = textStyle,
|
||||
color = colors.contentColor(enabled = enabled).value,
|
||||
maxLines = 1,
|
||||
)
|
||||
if (buttonIcon is TangemButtonIcon.End) {
|
||||
icon(buttonIcon.iconResId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private sealed interface TangemButtonIcon {
|
||||
val iconResId: Int?
|
||||
|
||||
data class Start(override val iconResId: Int) : TangemButtonIcon
|
||||
|
||||
data class End(override val iconResId: Int) : TangemButtonIcon
|
||||
|
||||
object None : TangemButtonIcon {
|
||||
override val iconResId: Int? = null
|
||||
}
|
||||
}
|
||||
|
||||
private enum class TangemButtonSize {
|
||||
Default,
|
||||
Text,
|
||||
Selector,
|
||||
Action,
|
||||
RoundedAction,
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TangemButtonSize.toHeightDp(): Dp = when (this) {
|
||||
TangemButtonSize.Default -> TangemTheme.dimens.size48
|
||||
TangemButtonSize.Text -> TangemTheme.dimens.size40
|
||||
TangemButtonSize.Selector -> TangemTheme.dimens.size24
|
||||
TangemButtonSize.Action,
|
||||
TangemButtonSize.RoundedAction,
|
||||
-> TangemTheme.dimens.size36
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TangemButtonSize.toShape(): Shape = when (this) {
|
||||
TangemButtonSize.Default -> TangemTheme.shapes.roundedCornersMedium
|
||||
TangemButtonSize.Text -> TangemTheme.shapes.roundedCornersSmall
|
||||
TangemButtonSize.Selector -> TangemTheme.shapes.roundedCornersSmall
|
||||
TangemButtonSize.Action -> TangemTheme.shapes.roundedCornersMedium
|
||||
TangemButtonSize.RoundedAction -> TangemTheme.shapes.roundedCornersLarge
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TangemButtonSize.toIconPadding(): Dp = when (this) {
|
||||
TangemButtonSize.Default -> TangemTheme.dimens.spacing8
|
||||
TangemButtonSize.Text -> TangemTheme.dimens.spacing8
|
||||
TangemButtonSize.Selector -> 0.dp
|
||||
TangemButtonSize.Action,
|
||||
TangemButtonSize.RoundedAction,
|
||||
-> TangemTheme.dimens.spacing8
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TangemButtonSize.toContentPadding(icon: TangemButtonIcon): PaddingValues {
|
||||
val horizontalPadding = this.toHorizontalContentPadding(icon = icon)
|
||||
|
||||
return when (this) {
|
||||
TangemButtonSize.Default -> PaddingValues(
|
||||
top = TangemTheme.dimens.spacing14,
|
||||
bottom = TangemTheme.dimens.spacing14,
|
||||
start = horizontalPadding.first,
|
||||
end = horizontalPadding.second,
|
||||
)
|
||||
TangemButtonSize.Text -> PaddingValues(
|
||||
top = TangemTheme.dimens.spacing10,
|
||||
bottom = TangemTheme.dimens.spacing10,
|
||||
start = horizontalPadding.first,
|
||||
end = horizontalPadding.second,
|
||||
)
|
||||
TangemButtonSize.Selector -> PaddingValues(
|
||||
top = TangemTheme.dimens.spacing0_5,
|
||||
bottom = TangemTheme.dimens.spacing0_5,
|
||||
start = horizontalPadding.first,
|
||||
end = horizontalPadding.second,
|
||||
)
|
||||
TangemButtonSize.Action,
|
||||
TangemButtonSize.RoundedAction,
|
||||
-> PaddingValues(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
bottom = TangemTheme.dimens.spacing8,
|
||||
start = horizontalPadding.first,
|
||||
end = horizontalPadding.second,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun TangemButtonSize.toHorizontalContentPadding(icon: TangemButtonIcon): Pair<Dp, Dp> {
|
||||
return when (this) {
|
||||
TangemButtonSize.Default -> TangemTheme.dimens.spacing32 to TangemTheme.dimens.spacing32
|
||||
TangemButtonSize.Text -> when (icon) {
|
||||
is TangemButtonIcon.None -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing16
|
||||
is TangemButtonIcon.Start -> TangemTheme.dimens.spacing14 to TangemTheme.dimens.spacing16
|
||||
is TangemButtonIcon.End -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing14
|
||||
}
|
||||
TangemButtonSize.Selector -> TangemTheme.dimens.spacing0_5 to TangemTheme.dimens.spacing0_5
|
||||
TangemButtonSize.Action,
|
||||
TangemButtonSize.RoundedAction,
|
||||
-> when (icon) {
|
||||
is TangemButtonIcon.None -> TangemTheme.dimens.spacing24 to TangemTheme.dimens.spacing24
|
||||
is TangemButtonIcon.Start -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing24
|
||||
is TangemButtonIcon.End -> TangemTheme.dimens.spacing24 to TangemTheme.dimens.spacing16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object TangemButtonsDefaults {
|
||||
val elevation: ButtonElevation
|
||||
@Composable get() = ButtonDefaults
|
||||
.elevation(
|
||||
defaultElevation = TangemTheme.dimens.elevation0,
|
||||
pressedElevation = TangemTheme.dimens.elevation0,
|
||||
)
|
||||
|
||||
val primaryButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = TangemTheme.colors.button.primary,
|
||||
contentColor = TangemTheme.colors.text.primary2,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val secondaryButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = TangemTheme.colors.button.secondary,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val defaultTextButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = TangemTheme.colors.text.secondary,
|
||||
disabledBackgroundColor = Color.Transparent,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val warningTextButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = TangemTheme.colors.text.warning,
|
||||
disabledBackgroundColor = Color.Transparent,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val selectorButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = TangemTheme.colors.text.tertiary,
|
||||
disabledBackgroundColor = Color.Transparent,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val backgroundButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private open class TangemButtonColors(
|
||||
private val backgroundColor: Color,
|
||||
private val contentColor: Color,
|
||||
private val disabledBackgroundColor: Color,
|
||||
private val disabledContentColor: Color,
|
||||
) : ButtonColors {
|
||||
@Composable
|
||||
override fun backgroundColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(newValue = if (enabled) backgroundColor else disabledBackgroundColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun contentColor(enabled: Boolean): State<Color> {
|
||||
return rememberUpdatedState(newValue = if (enabled) contentColor else disabledContentColor)
|
||||
}
|
||||
}
|
||||
// endregion Defaults
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun PrimaryButtonSample() {
|
||||
|
|
@ -730,34 +372,4 @@ private fun TextButtonPreview_DarkTheme() {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionButtonSample() {
|
||||
Column(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
RoundedActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { })
|
||||
ActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { })
|
||||
BackgroundActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { })
|
||||
RoundedActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { })
|
||||
ActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { })
|
||||
BackgroundActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { })
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ActionButtonPreview_LightTheme() {
|
||||
TangemTheme {
|
||||
ActionButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ActionButtonPreview_DarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
ActionButtonSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* Closable notification with custom icon
|
||||
* Child of parent component
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=1045-807&t=6CVvYDJe0sB7wBKE-0">Figma component</a>
|
||||
*
|
||||
* Use to show banner with custom icon and possibility to close
|
||||
* i.e. Feedback notification
|
||||
*
|
||||
* @param title notification title
|
||||
* @param icon drawable res on icon
|
||||
* @param iconColor icon color
|
||||
* @param onClick callback on click
|
||||
* @param onCloseClick callback on close icon click
|
||||
*/
|
||||
@Composable
|
||||
fun ClosableNotification(
|
||||
title: String,
|
||||
@DrawableRes icon: Int,
|
||||
iconColor: Color,
|
||||
onClick: (() -> Unit),
|
||||
onCloseClick: (() -> Unit),
|
||||
) {
|
||||
NotificationCardTemplate(onClick) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.align(Alignment.CenterStart),
|
||||
painter = painterResource(id = icon),
|
||||
tint = iconColor,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing28)
|
||||
.align(Alignment.CenterStart),
|
||||
text = title,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.align(Alignment.CenterEnd)
|
||||
.clickable(onClick = onCloseClick),
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification component from Design system
|
||||
* There are few states for this component, but only one parent, see link below
|
||||
*
|
||||
* Use this for Notification with title, subtitle, clickable or not
|
||||
*
|
||||
* @param title notification title
|
||||
* @param subtitle notification subtitle
|
||||
* @param onClick click on notification, if its null then no chevron icon
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=1045-807&t=6CVvYDJe0sB7wBKE-0">Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun WarningNotification(title: String, subtitle: String?, onClick: (() -> Unit)?) {
|
||||
NotificationCardTemplate(onClick) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.align(Alignment.CenterStart),
|
||||
painter = painterResource(id = R.drawable.img_attention_20),
|
||||
contentDescription = null,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing28)
|
||||
.align(Alignment.CenterStart),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
if (!subtitle.isNullOrEmpty()) {
|
||||
SpacerH2()
|
||||
Text(
|
||||
text = subtitle,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (onClick != null) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.align(Alignment.CenterEnd),
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun NotificationCardTemplate(onClick: (() -> Unit)? = null, content: @Composable BoxScope.() -> Unit) {
|
||||
Surface(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius18),
|
||||
onClick = onClick ?: {},
|
||||
enabled = onClick != null,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
vertical = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
.wrapContentSize(),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
|
||||
@Composable
|
||||
private fun WarningNotificationPreview() {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
WarningNotification(
|
||||
title = "Your wallet hasn’t been backed up",
|
||||
subtitle = "Lorem ipsum dolor sit amet, consectetur " +
|
||||
"adipiscing elit, sed do eiusmod tempor incididunt ut labore et...",
|
||||
onClick = {},
|
||||
)
|
||||
SpacerH32()
|
||||
WarningNotification(
|
||||
title = "Your wallet hasn’t been backed up",
|
||||
subtitle = null,
|
||||
onClick = {},
|
||||
)
|
||||
SpacerH32()
|
||||
WarningNotification(
|
||||
title = "Your wallet hasn’t been backed up",
|
||||
subtitle = "Lorem ipsum dolor sit amet, consectetur " +
|
||||
"adipiscing elit, sed do eiusmod tempor incididunt ut labore et...",
|
||||
onClick = null,
|
||||
)
|
||||
SpacerH32()
|
||||
WarningNotification(
|
||||
title = "Your wallet hasn’t been backed up",
|
||||
subtitle = null,
|
||||
onClick = null,
|
||||
)
|
||||
SpacerH32()
|
||||
ClosableNotification(
|
||||
title = "Like tangem app?",
|
||||
icon = R.drawable.ic_star_24,
|
||||
iconColor = TangemTheme.colors.icon.attention,
|
||||
onClick = {},
|
||||
onCloseClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_WarningNotification_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
WarningNotificationPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_WarningNotification_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
WarningNotificationPreview()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.core.ui.components.buttons.actions
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
/**
|
||||
* Action button config
|
||||
*
|
||||
* @property text text
|
||||
* @property iconResId icon resource id
|
||||
* @property onClick lambda be invoked when action component is clicked
|
||||
* @property enabled enabled
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class ActionButtonConfig(
|
||||
val text: String,
|
||||
@DrawableRes val iconResId: Int,
|
||||
val onClick: () -> Unit,
|
||||
val enabled: Boolean = true,
|
||||
)
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package com.tangem.core.ui.components.buttons.actions
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.components.buttons.common.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* Rounded action button
|
||||
*
|
||||
* @param config component config
|
||||
* @param modifier modifier
|
||||
* @param color background color
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-306&mode=design&t=
|
||||
* cSLQtIbR2J1h5M9U-4">Show in Figma</a>
|
||||
*/
|
||||
@Composable
|
||||
fun RoundedActionButton(
|
||||
config: ActionButtonConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.button.secondary,
|
||||
) {
|
||||
Button(
|
||||
config = config,
|
||||
shape = RoundedCornerShape(size = TangemTheme.dimens.radius24),
|
||||
modifier = modifier,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Action button
|
||||
*
|
||||
* @param config component config
|
||||
* @param modifier modifier
|
||||
* @param color background color
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-306&mode=design&t=
|
||||
* cSLQtIbR2J1h5M9U-4">Show in Figma</a>
|
||||
*/
|
||||
@Composable
|
||||
fun ActionButton(
|
||||
config: ActionButtonConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.button.secondary,
|
||||
) {
|
||||
Button(
|
||||
config = config,
|
||||
shape = RoundedCornerShape(size = TangemTheme.dimens.radius12),
|
||||
modifier = modifier,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Button(
|
||||
config: ActionButtonConfig,
|
||||
shape: RoundedCornerShape,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.button.secondary,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.heightIn(min = TangemTheme.dimens.size36)
|
||||
.clip(shape)
|
||||
.background(
|
||||
color = if (config.enabled) color else TangemTheme.colors.button.disabled,
|
||||
shape = shape,
|
||||
)
|
||||
.clickable(enabled = config.enabled, onClick = config.onClick)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing24,
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size20),
|
||||
tint = if (config.enabled) TangemTheme.colors.icon.primary1 else TangemTheme.colors.icon.informative,
|
||||
)
|
||||
|
||||
SpacerW8()
|
||||
|
||||
Text(
|
||||
text = config.text,
|
||||
color = if (config.enabled) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.disabled,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.button,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = "RoundedActionButton", showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_RoundedActionButton_Light(@PreviewParameter(ActionStateProvider::class) state: ActionButtonConfig) {
|
||||
TangemTheme(isDark = false) {
|
||||
RoundedActionButton(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = "RoundedActionButton", showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_RoundedActionButton_Dark(@PreviewParameter(ActionStateProvider::class) state: ActionButtonConfig) {
|
||||
TangemTheme(isDark = true) {
|
||||
RoundedActionButton(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = "ActionButton", showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_ActionButton_Light(@PreviewParameter(ActionStateProvider::class) state: ActionButtonConfig) {
|
||||
TangemTheme(isDark = false) {
|
||||
ActionButton(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = "ActionButton", showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_ActionButton_Dark(@PreviewParameter(ActionStateProvider::class) state: ActionButtonConfig) {
|
||||
TangemTheme(isDark = true) {
|
||||
ActionButton(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionStateProvider : CollectionPreviewParameterProvider<ActionButtonConfig>(
|
||||
collection = listOf(
|
||||
ActionButtonConfig(text = "Enabled", iconResId = R.drawable.ic_arrow_up_24, enabled = true, onClick = {}),
|
||||
ActionButtonConfig(text = "Disabled", iconResId = R.drawable.ic_arrow_down_24, enabled = false, onClick = {}),
|
||||
),
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue