Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-07 13:19:09 +03:00
commit 9c868cf221
202 changed files with 4333 additions and 775 deletions

View file

@ -92,6 +92,9 @@ dependencies {
implementation(projects.features.tokendetails.api)
implementation(projects.features.tokendetails.impl)
implementation(projects.features.send.api)
implementation(projects.features.manageTokens.api)
implementation(projects.features.manageTokens.impl)
implementation(projects.features.send.impl)
/** AndroidX libraries */
implementation(deps.androidx.core.ktx)
@ -183,4 +186,10 @@ dependencies {
testImplementation(deps.test.truth)
androidTestImplementation(deps.test.junit.android)
androidTestImplementation(deps.test.espresso)
/** Chucker */
debugImplementation(deps.chucker)
externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)
}

View file

@ -511,8 +511,7 @@
"id": "aleph-zero",
"symbol": "AZERO",
"name": "Aleph Zero",
"networks":
[
"networks": [
{
"networkId": "aleph-zero/test"
}
@ -522,12 +521,21 @@
"id": "near",
"symbol": "NEAR",
"name": "NEAR",
"networks":
[
"networks": [
{
"networkId": "near-protocol/test"
}
]
},
{
"id": "decimal",
"name": "Decimal",
"symbol": "tDEL",
"networks": [
{
"networkId": "decimal/test"
}
]
}
]
}

View file

@ -1,14 +1,21 @@
package com.tangem.tap
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
@ -16,14 +23,20 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.data.card.sdk.CardSdkLifecycleObserver
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
@ -46,12 +59,15 @@ import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandle
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.main.MainViewModel
import com.tangem.tap.features.main.model.Toast
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.features.shop.redux.ShopAction
import com.tangem.tap.features.welcome.ui.WelcomeFragment
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import com.tangem.wallet.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@ -109,9 +125,17 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var tokenDetailsRouter: TokenDetailsRouter
@Inject
lateinit var manageTokensRouter: ManageTokensRouter
@Inject
lateinit var walletConnectInteractor: WalletConnectInteractor
@Inject
lateinit var sendRouter: SendRouter
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
// TODO: fixme: inject through DI
@ -136,6 +160,29 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
initContent()
checkGooglePayAvailability()
checkForNotificationPermission()
observeStateUpdates()
}
private fun observeStateUpdates() {
viewModel.state
.flowWithLifecycle(lifecycle)
.onEach { state ->
if (state.toast is StateEvent.Triggered) {
showToast(state.toast.data)
state.toast.onConsume()
}
}
.launchIn(lifecycleScope)
}
private fun showToast(toast: Toast) {
dismissSnackbar()
showSnackbar(toast.message, Snackbar.LENGTH_LONG, toast.action.text) {
toast.action.onClick()
dismissSnackbar()
}
}
private fun installActivityDependencies() {
@ -156,7 +203,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
walletRouter = walletRouter,
walletConnectInteractor = walletConnectInteractor,
tokenDetailsRouter = tokenDetailsRouter,
manageTokensRouter = manageTokensRouter,
cardSdkConfigRepository = cardSdkConfigRepository,
sendRouter = sendRouter,
),
)
}
@ -285,18 +334,22 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
override fun showSnackbar(text: Int, buttonTitle: Int?, action: View.OnClickListener?) {
if (snackbar != null) return
override fun showSnackbar(
@StringRes text: Int,
length: Int,
@StringRes buttonTitle: Int?,
action: View.OnClickListener?,
) {
showSnackbar(getString(text), length, buttonTitle?.let(::getString), action)
}
snackbar = Snackbar.make(
binding.fragmentContainer,
getString(text),
Snackbar.LENGTH_INDEFINITE,
)
if (buttonTitle != null && action != null) {
snackbar?.setAction(getString(buttonTitle), action)
}
snackbar?.show()
override fun showSnackbar(
text: TextReference,
length: Int,
buttonTitle: TextReference?,
action: View.OnClickListener?,
) {
showSnackbar(text.resolveReference(resources), length, buttonTitle?.resolveReference(resources), action)
}
override fun dismissSnackbar() {
@ -332,6 +385,33 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
lockUserWalletsTimer?.restart()
}
private fun showSnackbar(text: String, length: Int, buttonTitle: String?, action: View.OnClickListener?) {
if (snackbar != null) return
snackbar = Snackbar.make(binding.fragmentContainer, text, length).apply {
val textColor = getColor(R.color.text_primary_2)
setBackgroundTint(getColor(R.color.button_primary))
setActionTextColor(textColor)
setTextColor(textColor)
if (buttonTitle != null && action != null) {
setAction(buttonTitle, action)
}
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
snackbar = null
removeCallback(this)
}
},
)
}
snackbar?.show()
}
private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) {
val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
val isNotScannedBefore = store.state.globalState.scanResponse == null
@ -371,4 +451,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
store.dispatch(BackupAction.CheckForUnfinishedBackup)
}
private fun checkForNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
BuildConfig.DEBUG &&
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) !=
PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
}
}
}

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.content.pm.PackageManager
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.Log
@ -36,6 +37,8 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.WalletManagersRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
@ -170,6 +173,9 @@ internal class TapApplication : Application(), ImageLoaderFactory {
// @Inject
// lateinit var learn2earnInteractor: Learn2earnInteractor
@Inject
lateinit var manageTokensFeatureToggles: ManageTokensFeatureToggles
@Inject
lateinit var scanCardProcessor: ScanCardProcessor
@ -211,6 +217,9 @@ internal class TapApplication : Application(), ImageLoaderFactory {
@Inject
lateinit var walletsRepository: WalletsRepository
@Inject
lateinit var sendFeatureToggles: SendFeatureToggles
// endregion Injected
override fun onCreate() {
@ -255,6 +264,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
createNetworkLoggingInterceptor(),
ChuckerInterceptor(this),
)
}
@ -290,6 +300,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
walletFeatureToggles = walletFeatureToggles,
walletConnectRepository = walletConnect2Repository,
walletConnectSessionsRepository = walletConnectSessionsRepository,
manageTokensFeatureToggles = manageTokensFeatureToggles,
scanCardProcessor = scanCardProcessor,
appCurrencyRepository = appCurrencyRepository,
walletManagersFacade = walletManagersFacade,
@ -300,6 +311,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
balanceHidingRepository = balanceHidingRepository,
detailsFeatureToggles = detailsFeatureToggles,
walletsRepository = walletsRepository,
sendFeatureToggles = sendFeatureToggles,
),
),
)

View file

@ -1,8 +1,25 @@
package com.tangem.tap.common
import android.view.View
import androidx.annotation.StringRes
import com.google.android.material.snackbar.Snackbar
import com.tangem.core.ui.extensions.TextReference
interface SnackbarHandler {
fun showSnackbar(text: Int, buttonTitle: Int? = null, action: View.OnClickListener? = null)
fun showSnackbar(
@StringRes text: Int,
length: Int = Snackbar.LENGTH_INDEFINITE,
@StringRes buttonTitle: Int? = null,
action: View.OnClickListener? = null,
)
fun showSnackbar(
text: TextReference,
length: Int = Snackbar.LENGTH_INDEFINITE,
buttonTitle: TextReference? = null,
action: View.OnClickListener? = null,
)
fun dismissSnackbar()
}

View file

@ -1,56 +1,7 @@
package com.tangem.tap.common.extensions
import androidx.annotation.DrawableRes
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.remove
import com.tangem.wallet.R
@Suppress("ComplexMethod")
@DrawableRes
fun Blockchain.getGreyedOutIconRes(): Int {
return when (this) {
Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> R.drawable.ic_arbitrum_no_color
Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> R.drawable.ic_bitcoin_no_color
Blockchain.BitcoinCash -> R.drawable.ic_bitcoin_cash_no_color
Blockchain.Litecoin -> R.drawable.ic_litecoin_no_color
Blockchain.Ethereum, Blockchain.EthereumTestnet -> R.drawable.ic_eth_no_color
Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet -> R.drawable.ic_eth_no_color
Blockchain.RSK -> R.drawable.ic_rsk_no_color
Blockchain.Cardano -> R.drawable.ic_cardano_no_color
Blockchain.Tezos -> R.drawable.ic_tezos_no_color
Blockchain.XRP -> R.drawable.ic_xrp_no_color
Blockchain.Stellar -> R.drawable.ic_stellar_no_color
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> R.drawable.ic_avalanche_no_color
Blockchain.Polygon, Blockchain.PolygonTestnet -> R.drawable.ic_polygon_no_color
Blockchain.Solana, Blockchain.SolanaTestnet -> R.drawable.ic_solana_no_color
Blockchain.Fantom, Blockchain.FantomTestnet -> R.drawable.ic_fantom_no_color
Blockchain.BSC, Blockchain.BSCTestnet, Blockchain.Binance, Blockchain.BinanceTestnet ->
R.drawable.ic_bsc_no_color
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_no_color
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color
Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color
Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> R.drawable.ic_ethereumpow_no_color
Blockchain.EthereumFair -> R.drawable.ic_ethereumfair_no_color
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_no_color
Blockchain.Kusama -> R.drawable.ic_kusama_no_color
Blockchain.Optimism, Blockchain.OptimismTestnet -> R.drawable.ic_optimism_no_color
Blockchain.Dash -> R.drawable.ic_dash_no_color
Blockchain.Kaspa -> R.drawable.ic_kaspa_no_color
Blockchain.TON, Blockchain.TONTestnet -> R.drawable.ic_ton_no_color
Blockchain.Kava, Blockchain.KavaTestnet -> R.drawable.ic_kava_no_color
Blockchain.Ravencoin, Blockchain.RavencoinTestnet -> R.drawable.ic_ravencoin_no_color
Blockchain.Cosmos, Blockchain.CosmosTestnet -> R.drawable.ic_cosmos_no_color
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
Blockchain.OctaSpace, Blockchain.OctaSpaceTestnet -> R.drawable.ic_octaspace_no_color
Blockchain.Chia, Blockchain.ChiaTestnet -> R.drawable.ic_chia_no_color
Blockchain.Near, Blockchain.NearTestnet -> R.drawable.ic_near_no_color
else -> R.drawable.ic_tangem_logo
}
}
fun Blockchain.getNetworkName(): String {
return when (this) {

View file

@ -18,6 +18,7 @@ import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
import com.tangem.tap.features.home.HomeFragment
import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment
import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment
@ -59,12 +60,8 @@ fun FragmentActivity.openFragment(
}
}
if (screen.isDialogFragment) {
val dialogFragment = requireNotNull(fragment as? DialogFragment) {
"If screen.isDialogFragment == true then fragment must be a DialogFragment"
}
dialogFragment.showAllowingStateLoss(
if (screen.isDialogFragment && fragment is DialogFragment) {
fragment.showAllowingStateLoss(
fragmentManager = supportFragmentManager,
baseTransaction = transaction,
tag = screen.name,
@ -125,7 +122,7 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? {
return tag?.let { AppScreen.valueOf(tag) }
}
@Suppress("ComplexMethod")
@Suppress("ComplexMethod", "LongMethod")
private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
AppScreen.Home -> HomeFragment()
@ -146,7 +143,18 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
WalletFragment()
}
}
AppScreen.Send -> SendFragment()
AppScreen.Send -> {
val featureToggles = store.state.daggerGraphState.get(
getDependency = DaggerGraphState::sendFeatureToggles,
)
if (featureToggles.isRedesignedSendEnabled) {
store.state.daggerGraphState
.get(getDependency = DaggerGraphState::sendRouter)
.getEntryFragment()
} else {
SendFragment()
}
}
AppScreen.Details -> DetailsFragment()
AppScreen.DetailsSecurity -> SecurityModeFragment()
AppScreen.CardSettings -> CardSettingsFragment()
@ -154,7 +162,18 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.ResetToFactory -> ResetCardFragment()
AppScreen.AccessCodeRecovery -> AccessCodeRecoveryFragment()
AppScreen.Disclaimer -> DisclaimerFragment()
AppScreen.AddTokens -> TokensListFragment()
AppScreen.ManageTokens -> {
val featureToggles = store.state.daggerGraphState.get(
getDependency = DaggerGraphState::manageTokensFeatureToggles,
)
if (featureToggles.isRedesignedScreenEnabled) {
store.state.daggerGraphState
.get(getDependency = DaggerGraphState::manageTokensRouter)
.getEntryFragment()
} else {
TokensListFragment()
}
}
AppScreen.AddCustomToken -> AddCustomTokenFragment()
AppScreen.WalletDetails -> {
val featureToggles = store.state.daggerGraphState.get(
@ -176,5 +195,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.SaveWallet -> SaveWalletBottomSheetFragment()
AppScreen.WalletSelector -> WalletSelectorBottomSheetFragment()
AppScreen.AppCurrencySelector -> AppCurrencySelectorFragment()
AppScreen.ModalNotification -> ModalNotificationBottomSheetFragment()
}
}

View file

@ -1,8 +1,9 @@
package com.tangem.tap.di.domain
import com.tangem.domain.balancehiding.DeviceFlipDetector
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
import com.tangem.domain.settings.*
import com.tangem.domain.settings.repositories.AppRatingRepository
@ -63,8 +64,10 @@ internal object SettingsDomainModule {
@Provides
@ViewModelScoped
fun providesIsBalanceHiddenUseCase(balanceHidingRepository: BalanceHidingRepository): IsBalanceHiddenUseCase {
return IsBalanceHiddenUseCase(
fun providesGetBalanceHidingSettingsUseCase(
balanceHidingRepository: BalanceHidingRepository,
): GetBalanceHidingSettingsUseCase {
return GetBalanceHidingSettingsUseCase(
balanceHidingRepository = balanceHidingRepository,
)
}
@ -80,4 +83,12 @@ internal object SettingsDomainModule {
balanceHidingRepository = balanceHidingRepository,
)
}
@Provides
@ViewModelScoped
fun provideUpdateHideBalancesSettingsUseCase(
balanceHidingRepository: BalanceHidingRepository,
): UpdateBalanceHidingSettingsUseCase {
return UpdateBalanceHidingSettingsUseCase(balanceHidingRepository)
}
}

View file

@ -13,7 +13,6 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.balancehiding.BalanceHidingSettings
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
@ -280,7 +279,7 @@ class DetailsMiddleware {
val repository = store.state.daggerGraphState.get(DaggerGraphState::balanceHidingRepository)
scope.launch {
val newState = BalanceHidingSettings(
val newState = repository.getBalanceHidingSettings().copy(
isHidingEnabledInSettings = hideBalance,
isBalanceHidden = false,
)

View file

@ -94,7 +94,7 @@ class HomeFragment : ComposeFragment(), StoreSubscriber<HomeState> {
},
onSearchTokensClick = {
Analytics.send(IntroductionProcess.ButtonTokensList())
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
store.dispatch(NavigationAction.NavigateTo(AppScreen.ManageTokens))
store.dispatch(TokensAction.SetArgs.ReadAccess)
},
)

View file

@ -0,0 +1,12 @@
package com.tangem.tap.features.main
internal interface MainIntents {
fun onHiddenBalanceToastAction()
fun onShownBalanceToastAction()
fun onHiddenBalanceNotificationAction(isPermanent: Boolean)
fun onDismissBottomSheet()
}

View file

@ -0,0 +1,62 @@
package com.tangem.tap.features.main
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.event.triggeredEvent
import com.tangem.tap.features.main.model.MainScreenState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
internal class MainScreenStateHolder(private val intents: MainIntents) {
private val notificationsFactory = NotificationsFactory(intents)
private val stateFlowInternal: MutableStateFlow<MainScreenState> = MutableStateFlow(getInitialState())
val stateFlow: StateFlow<MainScreenState> = stateFlowInternal
fun updateWithHiddenBalancesToast(isBalanceHidden: Boolean) {
stateFlowInternal.update { state ->
state.copy(
toast = triggeredEvent(
data = if (isBalanceHidden) {
notificationsFactory.createBalancesAreHiddenToast()
} else {
notificationsFactory.createBalancesAreShownToast()
},
onConsume = ::consumeToastEvent,
),
)
}
}
fun updateWithHiddenBalancesNotification() {
stateFlowInternal.update { state ->
state.copy(
modalNotification = TangemBottomSheetConfig(
isShow = true,
onDismissRequest = intents::onDismissBottomSheet,
content = notificationsFactory.createBalancesAreHiddenModalNotification(),
),
)
}
}
fun updateWithoutModalNotification() {
stateFlowInternal.update { state ->
state.copy(modalNotification = state.modalNotification?.copy(isShow = false))
}
}
private fun consumeToastEvent() {
stateFlowInternal.update { state ->
state.copy(toast = consumedEvent())
}
}
private fun getInitialState() = MainScreenState(
toast = consumedEvent(),
modalNotification = null,
)
}

View file

@ -0,0 +1,127 @@
package com.tangem.tap.features.main
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.domain.balancehiding.BalanceHidingSettings
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
import com.tangem.tap.features.main.model.MainScreenState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
internal class MainViewModel @Inject constructor(
private val updateBalanceHidingSettingsUseCase: UpdateBalanceHidingSettingsUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val reduxNavController: ReduxNavController,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel(), MainIntents {
private val stateHolder = MainScreenStateHolder(
intents = this,
)
private val balanceHidingSettingsFlow: SharedFlow<BalanceHidingSettings> = getBalanceHidingSettingsUseCase()
.shareIn(viewModelScope, SharingStarted.WhileSubscribed())
val state: StateFlow<MainScreenState> = stateHolder.stateFlow
init {
observeFlips()
displayBalancesHidingStatusToast()
displayHiddenBalancesModalNotification()
}
private fun observeFlips() {
listenToFlipsUseCase().launchIn(viewModelScope)
}
private fun displayHiddenBalancesModalNotification() {
balanceHidingSettingsFlow
.drop(count = 1) // Skip initial emit
.distinctUntilChanged()
.filter {
it.isBalanceHidingNotificationEnabled && it.isBalanceHidden
}
.onEach {
if (state.value.modalNotification?.isShow != true) {
stateHolder.updateWithHiddenBalancesNotification()
reduxNavController.navigate(NavigationAction.NavigateTo(AppScreen.ModalNotification))
}
}
.launchIn(viewModelScope)
}
private fun displayBalancesHidingStatusToast() {
var previousSettings: BalanceHidingSettings? = null
balanceHidingSettingsFlow
.onEach { settings ->
if (previousSettings == null) {
previousSettings = settings
return@onEach
}
/*
* Skip if the feature to hide balances has just been
* enabled or disabled in the settings, or if the hide balances status has not been changed
* */
if (settings.isHidingEnabledInSettings != previousSettings?.isHidingEnabledInSettings ||
settings.isBalanceHidden == previousSettings?.isBalanceHidden
) {
previousSettings = settings
return@onEach
}
displayBalancesHiddenStatusToast(settings)
previousSettings = settings
}
.launchIn(viewModelScope)
}
private fun displayBalancesHiddenStatusToast(settings: BalanceHidingSettings) {
// If modal notification is enabled and balances are hidden, the toast will not show
if (!settings.isBalanceHidingNotificationEnabled || !settings.isBalanceHidden) {
stateHolder.updateWithHiddenBalancesToast(settings.isBalanceHidden)
}
}
override fun onHiddenBalanceToastAction() {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidden = false)
}
}
}
override fun onShownBalanceToastAction() {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidden = true)
}
}
}
override fun onHiddenBalanceNotificationAction(isPermanent: Boolean) {
onDismissBottomSheet()
if (isPermanent) {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidingNotificationEnabled = false)
}
}
}
}
override fun onDismissBottomSheet() {
stateHolder.updateWithoutModalNotification()
}
}

View file

@ -0,0 +1,48 @@
package com.tangem.tap.features.main
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.tap.features.main.model.ActionConfig
import com.tangem.tap.features.main.model.ModalNotification
import com.tangem.tap.features.main.model.Toast
import com.tangem.wallet.R
internal class NotificationsFactory(
private val intents: MainIntents,
) {
fun createBalancesAreHiddenToast(): Toast {
return Toast(
message = resourceReference(R.string.toast_balances_hidden),
action = ActionConfig(
text = resourceReference(R.string.toast_undo),
onClick = intents::onHiddenBalanceToastAction,
),
)
}
fun createBalancesAreShownToast(): Toast {
return Toast(
message = resourceReference(R.string.toast_balances_shown),
action = ActionConfig(
text = resourceReference(R.string.toast_undo),
onClick = intents::onShownBalanceToastAction,
),
)
}
fun createBalancesAreHiddenModalNotification(): ModalNotification {
return ModalNotification(
iconResId = R.drawable.ic_eye_off_outline_24,
title = resourceReference(R.string.balance_hidden_title),
message = resourceReference(R.string.balance_hidden_description),
primaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_got_it_button),
onClick = { intents.onHiddenBalanceNotificationAction(isPermanent = false) },
),
secondaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_do_not_show_button),
onClick = { intents.onHiddenBalanceNotificationAction(isPermanent = true) },
),
)
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.tap.features.main.model
import com.tangem.core.ui.extensions.TextReference
internal data class ActionConfig(
val text: TextReference,
val onClick: () -> Unit,
)

View file

@ -0,0 +1,9 @@
package com.tangem.tap.features.main.model
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.event.StateEvent
internal data class MainScreenState(
val toast: StateEvent<Toast>,
val modalNotification: TangemBottomSheetConfig?,
)

View file

@ -0,0 +1,13 @@
package com.tangem.tap.features.main.model
import androidx.annotation.DrawableRes
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.extensions.TextReference
internal data class ModalNotification(
@DrawableRes val iconResId: Int,
val title: TextReference,
val message: TextReference,
val primaryAction: ActionConfig,
val secondaryAction: ActionConfig?,
) : TangemBottomSheetConfigContent

View file

@ -0,0 +1,8 @@
package com.tangem.tap.features.main.model
import com.tangem.core.ui.extensions.TextReference
internal data class Toast(
val message: TextReference,
val action: ActionConfig,
)

View file

@ -0,0 +1,57 @@
package com.tangem.tap.features.main.ui
import android.content.DialogInterface
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.screen.ComposeBottomSheetFragment
import com.tangem.core.ui.theme.AppThemeModeHolder
import com.tangem.tap.features.main.MainViewModel
import com.tangem.tap.features.main.model.ModalNotification
import com.tangem.tap.features.main.ui.components.ModalNotificationContent
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
internal class ModalNotificationBottomSheetFragment : ComposeBottomSheetFragment() {
@Inject
override lateinit var appThemeModeHolder: AppThemeModeHolder
private val viewModel: MainViewModel by activityViewModels()
@Composable
override fun ScreenContent(modifier: Modifier) {
val state by viewModel.state.collectAsStateWithLifecycle()
val notification = state.modalNotification
if (notification == null) {
dismiss()
return
}
BackHandler(onBack = notification.onDismissRequest)
when (val content = notification.content) {
is ModalNotification -> ModalNotificationContent(
modifier = modifier,
notification = content,
)
else -> Unit
}
LaunchedEffect(notification) {
if (!notification.isShow) {
dismiss()
}
}
}
override fun onDismiss(dialog: DialogInterface) {
viewModel.state.value.modalNotification?.onDismissRequest?.invoke()
super.onDismiss(dialog)
}
}

View file

@ -0,0 +1,133 @@
package com.tangem.tap.features.main.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
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.res.painterResource
import androidx.compose.ui.text.style.TextAlign
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.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.main.model.ActionConfig
import com.tangem.tap.features.main.model.ModalNotification
import com.tangem.wallet.R
@Composable
internal fun ModalNotificationContent(notification: ModalNotification, modifier: Modifier = Modifier) {
Column(
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing40),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Hand()
Icon(
modifier = Modifier.size(TangemTheme.dimens.size48),
painter = painterResource(notification.iconResId),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
)
Column(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = notification.title.resolveReference(),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
Text(
text = notification.message.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
}
Column(
modifier = Modifier
.padding(bottom = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
horizontalAlignment = Alignment.CenterHorizontally,
) {
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = notification.primaryAction.text.resolveReference(),
onClick = notification.primaryAction.onClick,
)
when (val secondaryAction = notification.secondaryAction) {
null -> Unit
else -> SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = secondaryAction.text.resolveReference(),
onClick = secondaryAction.onClick,
)
}
}
}
}
// region Preview
@Preview(widthDp = 360, heightDp = 404)
@Composable
private fun ModalNotificationContentPreview_Light(
@PreviewParameter(GlobalNotificationProvider::class) param: ModalNotification,
) {
TangemTheme(isDark = false) {
ModalNotificationContent(
param,
modifier = Modifier.background(
color = TangemTheme.colors.background.plain,
shape = TangemTheme.shapes.bottomSheet,
),
)
}
}
@Preview(widthDp = 360, heightDp = 404)
@Composable
private fun ModalNotificationContentPreview_Dark(
@PreviewParameter(GlobalNotificationProvider::class) param: ModalNotification,
) {
TangemTheme(isDark = true) {
ModalNotificationContent(
param,
modifier = Modifier.background(
color = TangemTheme.colors.background.plain,
shape = TangemTheme.shapes.bottomSheet,
),
)
}
}
private class GlobalNotificationProvider : CollectionPreviewParameterProvider<ModalNotification>(
collection = listOf(
ModalNotification(
iconResId = R.drawable.ic_eye_off_outline_24,
title = resourceReference(R.string.balance_hidden_title),
message = resourceReference(R.string.balance_hidden_description),
primaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_got_it_button),
onClick = {},
),
secondaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_do_not_show_button),
onClick = {},
),
),
),
)
// endregion Preview

View file

@ -4,9 +4,7 @@ import com.google.android.material.button.MaterialButton
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.postUiDelayBg
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupState
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.FIRST_BACKUP
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.ORIGIN
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.SECOND_BACKUP
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.*
import com.tangem.wallet.databinding.FragmentOnboardingWalletBinding
/**
@ -29,7 +27,7 @@ class WalletBackupAnimator(
private val viewState: State = when (cardsWidget.leapfrogWidget.getViewsCount()) {
2 -> State.TwoCards
3 -> State.TreeCards
3 -> State.ThreeCards
else -> throw UnsupportedOperationException()
}
@ -131,7 +129,7 @@ class WalletBackupAnimator(
}
}
}
State.TreeCards -> {
State.ThreeCards -> {
when (backupCard) {
1 -> {
if (firstBackupCardAnimated) return
@ -147,6 +145,15 @@ class WalletBackupAnimator(
if (secondBackupCardAnimated) return
secondBackupCardAnimated = true
/*
* !!! Workaround !!!
* For interrupted backup
* */
if (!firstBackupCardAnimated) {
showWriteBackupCard(state, backupCard = 1)
return
}
SECOND_BACKUP.alpha(1f)
cardsWidget.leapfrogWidget.leap {
ORIGIN.alpha(0.4f)
@ -165,7 +172,7 @@ class WalletBackupAnimator(
}
private enum class State {
TreeCards,
ThreeCards,
TwoCards,
}
@ -186,7 +193,7 @@ class WalletBackupAnimator(
}
}
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
if (viewState != State.ThreeCards) return
cardsWidget.getSecondBackupCardView().animate().apply {
alpha(alpha)
this.startDelay = startDelay
@ -201,7 +208,7 @@ class WalletBackupAnimator(
ORIGIN -> cardsWidget.getOriginCardView().alpha = alpha
FIRST_BACKUP -> cardsWidget.getFirstBackupCardView().alpha = alpha
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
if (viewState != State.ThreeCards) return
cardsWidget.getSecondBackupCardView().alpha = alpha
}
}
@ -212,7 +219,7 @@ class WalletBackupAnimator(
ORIGIN -> cardsWidget.getOriginCardView().show()
FIRST_BACKUP -> cardsWidget.getFirstBackupCardView().show()
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
if (viewState != State.ThreeCards) return
cardsWidget.getSecondBackupCardView().show()
}
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.send.ui
import androidx.lifecycle.*
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
@ -9,7 +9,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.send.navigation.SendRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.tap.di.DelayedWork
import com.tangem.tap.features.send.redux.AmountAction
import com.tangem.tap.proxy.AppStateHolder
@ -29,7 +29,7 @@ import javax.inject.Inject
internal class SendViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val appStateHolder: AppStateHolder,
private val isBalanceHiddenUseCase: IsBalanceHiddenUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
@ -41,11 +41,11 @@ internal class SendViewModel @Inject constructor(
private val cryptoCurrency: CryptoCurrency? = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY]
override fun onCreate(owner: LifecycleOwner) {
isBalanceHiddenUseCase()
getBalanceHidingSettingsUseCase()
.flowWithLifecycle(owner.lifecycle)
.onEach { isBalanceHidden ->
.onEach {
withContext(dispatchers.main) {
appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(isBalanceHidden))
appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(it.isBalanceHidden))
}
}
.launchIn(viewModelScope)

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.tokens.impl.presentation.states
import androidx.compose.runtime.MutableState
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.tap.common.extensions.getGreyedOutIconRes
import com.tangem.core.ui.extensions.getGreyedOutIconRes
/**
* Network item state
@ -76,7 +76,7 @@ sealed interface NetworkItemState {
fun changeToggleState() {
val reverseState = !isAdded.value
isAdded.value = reverseState
iconResId.value = if (reverseState) getActiveIconRes(blockchain.id) else blockchain.getGreyedOutIconRes()
iconResId.value = if (reverseState) getActiveIconRes(blockchain.id) else getGreyedOutIconRes(blockchain.id)
}
}
}

View file

@ -40,7 +40,7 @@ object TokenListPreviewData {
NetworkItemState.ManageContent(
name = "Ethereum",
protocolName = "MAIN",
iconResId = mutableStateOf(R.drawable.ic_eth_no_color),
iconResId = mutableStateOf(R.drawable.ic_eth_16),
isMainNetwork = true,
isAdded = mutableStateOf(true),
id = "",
@ -53,7 +53,7 @@ object TokenListPreviewData {
NetworkItemState.ManageContent(
name = "BNB SMART CHAIN",
protocolName = "BEP20",
iconResId = mutableStateOf(R.drawable.ic_bsc_no_color),
iconResId = mutableStateOf(R.drawable.ic_bsc_16),
isMainNetwork = false,
isAdded = mutableStateOf(false),
id = "",
@ -71,13 +71,13 @@ object TokenListPreviewData {
NetworkItemState.ReadContent(
name = "Ethereum",
protocolName = "MAIN",
iconResId = mutableStateOf(R.drawable.ic_eth_no_color),
iconResId = mutableStateOf(R.drawable.ic_eth_16),
isMainNetwork = true,
),
NetworkItemState.ReadContent(
name = "BNB SMART CHAIN",
protocolName = "BEP20",
iconResId = mutableStateOf(R.drawable.ic_bsc_no_color),
iconResId = mutableStateOf(R.drawable.ic_bsc_16),
isMainNetwork = false,
),
)

View file

@ -9,6 +9,7 @@ import androidx.paging.*
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.core.ui.extensions.getGreyedOutIconRes
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.common.extensions.canHandleBlockchain
import com.tangem.domain.common.extensions.canHandleToken
@ -20,7 +21,6 @@ import com.tangem.domain.tokens.TokenWithBlockchain
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.tap.common.extensions.fullNameWithoutTestnet
import com.tangem.tap.common.extensions.getGreyedOutIconRes
import com.tangem.tap.common.extensions.getNetworkName
import com.tangem.tap.features.tokens.impl.domain.TokensListInteractor
import com.tangem.tap.features.tokens.impl.domain.models.Token
@ -225,7 +225,7 @@ internal class TokensListViewModel @Inject constructor(
return if (isAdded(address = address, blockchain = blockchain)) {
getActiveIconRes(blockchain.id)
} else {
blockchain.getGreyedOutIconRes()
getGreyedOutIconRes(blockchain.id)
}
}

View file

@ -15,7 +15,7 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.features.send.navigation.SendRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
@ -366,7 +366,10 @@ class TradeCryptoMiddleware {
),
)
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
val bundle = bundleOf(
SendRouter.CRYPTO_CURRENCY_KEY to currency,
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}
@ -416,7 +419,10 @@ class TradeCryptoMiddleware {
is CryptoCurrency.Token -> error("Action.tokenStatus.currency is Token")
}
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
val bundle = bundleOf(
SendRouter.CRYPTO_CURRENCY_KEY to currency,
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}

View file

@ -106,7 +106,7 @@ class MultiWalletView : WalletView() {
Analytics.send(Portfolio.ButtonManageTokens())
store.dispatch(action = TokensAction.SetArgs.ManageAccess)
store.dispatch(action = NavigationAction.NavigateTo(screen = AppScreen.AddTokens))
store.dispatch(action = NavigationAction.NavigateTo(screen = AppScreen.ManageTokens))
}
handleErrorStates(state = state, binding = binding, fragment = fragment)
}

View file

@ -2,6 +2,8 @@ package com.tangem.tap.proxy.redux
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
@ -16,6 +18,8 @@ sealed interface DaggerGraphAction : Action {
val walletRouter: WalletRouter,
val walletConnectInteractor: WalletConnectInteractor,
val tokenDetailsRouter: TokenDetailsRouter,
val manageTokensRouter: ManageTokensRouter,
val cardSdkConfigRepository: CardSdkConfigRepository,
val sendRouter: SendRouter,
) : DaggerGraphAction
}

View file

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

View file

@ -12,6 +12,10 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
@ -36,6 +40,8 @@ data class DaggerGraphState(
val walletConnectSessionsRepository: WalletConnectSessionsRepository? = null,
val walletConnectInteractor: WalletConnectInteractor? = null,
val tokenDetailsRouter: TokenDetailsRouter? = null,
val manageTokensFeatureToggles: ManageTokensFeatureToggles? = null,
val manageTokensRouter: ManageTokensRouter? = null,
val scanCardProcessor: ScanCardProcessor? = null,
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
val appCurrencyRepository: AppCurrencyRepository? = null,
@ -46,6 +52,8 @@ data class DaggerGraphState(
val detailsFeatureToggles: DetailsFeatureToggles? = null,
val walletsRepository: WalletsRepository? = null,
val networksRepository: NetworksRepository? = null,
val sendFeatureToggles: SendFeatureToggles? = null,
val sendRouter: SendRouter? = null,
// FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList
val currenciesRepository: CurrenciesRepository? = null,

View file

@ -1,12 +0,0 @@
<vector android:autoMirrored="true" android:height="22dp"
android:viewportHeight="22" android:viewportWidth="22"
android:width="22dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M0,0h22v22h-22z"/>
<path android:fillColor="#A1A1A4" android:pathData="M16.5486,12.9469L13.6283,8.469L12.5575,10.2212L15.3805,14.7964L16.646,14.2124L16.5486,12.9469Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M15.186,15.3805L12.2656,10.9026L11.0001,12.8495L13.3364,16.5486L14.7966,15.9645L15.186,15.3805Z"/>
<path android:fillColor="#A1A1A4" android:fillType="evenOdd" android:pathData="M5.6461,7.8851L11.0001,4.7701L16.3541,7.8851V14.1152L11.0001,17.2303L7.0089,14.9913L7,16L10.9028,18.3984C11.0001,18.4958 11.0974,18.4958 11.1948,18.3984L17.3275,14.894C17.4249,14.7966 17.5222,14.6993 17.5222,14.602V7.5931C17.5222,7.4958 17.4249,7.3984 17.3275,7.3011L11.1948,3.7966C11.0974,3.6993 11.0001,3.6993 10.9028,3.7966L4.77,7.2037C4.6727,7.3011 4.5753,7.3984 4.5753,7.4958V14.5046C4.5753,14.602 4.6727,14.6993 4.77,14.7966L6,15.5L6.5,14.5L5.7435,14.1152V7.8851H5.6461Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M13.3364,7.96H11.2921L6.5222,15.7476L8.1771,16.721L13.3364,7.96Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M8.1771,8.6636C8.6638,7.8849 10.0266,7.8849 10.6107,7.9822L6,15.5C5.8053,15.4027 5.3541,15.1857 5.062,14.9911C4.6727,14.7964 4.5753,14.7964 4.5753,14.5043V14.1149C5.6461,12.5574 7.6904,9.4424 8.1771,8.6636Z"/>
</group>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M10.6797,5.3445C10.272,4.4259 9.3351,4.2295 8.1882,4.4198L7.8196,3L6.9507,3.223L7.3131,4.6387C7.0845,4.696 6.8498,4.7451 6.6171,4.8106L6.2547,3.403L5.3858,3.626L5.7543,5.0458C5.567,5.099 4,5.498 4,5.498L4.2389,6.4227C4.2389,6.4227 4.8772,6.2447 4.871,6.259C5.2252,6.1669 5.3919,6.3429 5.4702,6.5086L6.4833,10.3998C6.4956,10.5123 6.475,10.7046 6.232,10.77C6.2465,10.7782 5.5999,10.9317 5.5999,10.9317L5.6946,12.0098C5.6946,12.0098 7.2472,11.615 7.451,11.5638L7.8237,13L8.6927,12.777L8.32,11.3306C8.5588,11.2754 8.7915,11.2181 9.018,11.1588L9.3886,12.597L10.2576,12.374L9.8849,10.9399C11.2233,10.6166 12.1684,9.7778 11.9748,8.4951C11.8513,7.7218 11.0009,7.0876 10.2946,7.016C10.7291,6.6334 10.9494,6.0749 10.6797,5.3445ZM10.2617,8.7304C10.4346,10.0008 8.6577,10.1563 8.0708,10.3097L7.5602,8.4092C8.1491,8.2557 9.9693,7.6113 10.2617,8.7304ZM9.191,6.1424C9.3742,7.2717 7.8546,7.4026 7.3646,7.5274L6.8992,5.8028C7.3913,5.6821 8.8183,5.097 9.191,6.1424Z"/>
</vector>

View file

@ -1,8 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M4.3968,8.0001L4.4025,10.1154L6.2,11.1731V12.4116L3.3506,10.7404V7.3815L4.3968,8.0001ZM4.3968,5.8847V7.1174L3.35,6.4981V5.2655L4.3968,4.6462L5.4487,5.2655L4.3968,5.8847ZM6.9506,5.2655L7.9974,4.6462L9.0493,5.2655L7.9974,5.8847L6.9506,5.2655Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M5.1532,9.677V8.4385L6.2,9.0577V10.2904L5.1532,9.677ZM6.9506,11.6167L7.9974,12.2359L9.0493,11.6167V12.8494L7.9974,13.4686L6.9506,12.8494V11.6167ZM10.5505,5.2655L11.5973,4.6462L12.6492,5.2655V6.4981L11.5973,7.1174V5.8847L10.5505,5.2655ZM11.5973,10.1154L11.6031,8.0001L12.6499,7.3808V10.7398L9.8006,12.4109V11.1725L11.5973,10.1154Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M10.8468,9.6769L9.8,10.2903V9.0577L10.8468,8.4385V9.6769Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M10.8468,6.3232L10.8526,7.5616L9.05,8.6193V10.7398L8.0032,11.3533L6.9564,10.7398V8.6193L5.1538,7.5616V6.3232L6.2051,5.7039L7.9968,6.7667L9.7993,5.7039L10.8513,6.3232H10.8468ZM5.1532,4.2084L7.9974,2.5315L10.8468,4.2084L9.8,4.8276L7.9974,3.7648L6.2,4.8276L5.1532,4.2084Z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M7.8738,3.2662C8.098,3.1493 8.3717,3.4204 8.2526,3.6444C8.1834,3.827 7.9211,3.8755 7.7871,3.7364C7.6452,3.6028 7.6915,3.3388 7.8738,3.2662ZM5.4626,3.5261C5.5988,3.47 5.7712,3.5948 5.7573,3.7432C5.7718,3.9043 5.5747,4.0229 5.4359,3.9464C5.252,3.8751 5.273,3.5766 5.4626,3.5261ZM10.4011,3.9509C10.1883,3.9252 10.1546,3.5906 10.3595,3.5232C10.5184,3.4457 10.6642,3.591 10.6875,3.7417C10.6541,3.8703 10.5465,3.9881 10.4011,3.9509ZM6.0113,4.5523C6.2389,4.4083 6.5638,4.6228 6.5288,4.8872C6.517,5.1543 6.1623,5.309 5.9622,5.1284C5.7687,4.9867 5.798,4.6614 6.0113,4.5523ZM9.4869,4.7051C9.5884,4.4434 10,4.4354 10.1156,4.6903C10.2299,4.8886 10.0894,5.1342 9.8857,5.2016C9.616,5.2519 9.3599,4.9604 9.4869,4.7051ZM7.5835,5.1867C7.5813,4.9617 7.7862,4.7941 8.0001,4.7787C8.1419,4.8072 8.2965,4.8676 8.3573,5.0107C8.4707,5.2082 8.37,5.48 8.1651,5.5706C8.0714,5.6231 7.9607,5.6034 7.8586,5.5973C7.7027,5.5208 7.5713,5.3706 7.5835,5.1867ZM3.9976,5.5146C4.2056,5.3867 4.4862,5.62 4.4075,5.846C4.3641,6.0414 4.1014,6.1209 3.9512,5.9962C3.7909,5.8793 3.8163,5.5982 3.9976,5.5146ZM11.6978,5.5142C11.8742,5.3772 12.1654,5.5343 12.1444,5.7571C12.1562,5.9603 11.9045,6.1135 11.7293,6.0089C11.5364,5.9222 11.5167,5.6253 11.6978,5.5142ZM8.5919,5.9187C8.8831,5.814 9.2289,5.9392 9.3949,6.1988C9.6138,6.5162 9.4974,6.9995 9.1576,7.1816C8.8056,7.397 8.2916,7.2144 8.1615,6.8213C8.014,6.4694 8.229,6.029 8.5919,5.9187ZM6.8834,5.9533C7.2062,5.8009 7.6405,5.9287 7.7964,6.2589C7.9798,6.5802 7.838,7.0276 7.51,7.1935C7.1816,7.38 6.7183,7.2386 6.5594,6.8936C6.3812,6.56 6.5395,6.1069 6.8834,5.9533ZM5.1728,6.5381C5.1973,6.3302 5.3934,6.1949 5.5913,6.1809C5.8046,6.2066 5.9749,6.3722 5.9981,6.588C5.9841,6.8016 5.819,7.0104 5.5926,7.0091C5.3436,7.0306 5.1252,6.7828 5.1728,6.5381ZM10.2255,6.2261C10.4812,6.0773 10.8363,6.2939 10.8249,6.5877C10.8425,6.8989 10.4495,7.1183 10.1949,6.9357C9.9304,6.7837 9.951,6.3555 10.2255,6.2261ZM6.1882,7.3231C6.4872,7.2408 6.8292,7.3818 6.9759,7.6566C7.1365,7.9333 7.0708,8.3177 6.8213,8.5206C6.5013,8.8183 5.911,8.6988 5.7407,8.2926C5.545,7.9201 5.7827,7.4211 6.1882,7.3231ZM9.4286,7.3215C9.7194,7.2384 10.0609,7.3504 10.2172,7.6153C10.4238,7.9182 10.3252,8.37 10.0176,8.5653C9.6953,8.792 9.1961,8.673 9.0188,8.3184C8.8038,7.9517 9.0202,7.4334 9.4286,7.3215ZM4.2647,7.6671C4.523,7.5694 4.8094,7.8545 4.7047,8.1106C4.6408,8.3439 4.312,8.4302 4.1421,8.2581C3.9433,8.0966 4.0169,7.7372 4.2647,7.6671ZM11.2667,8.0388C11.2593,7.8247 11.4159,7.6352 11.6354,7.6234C11.8123,7.6514 11.9843,7.7941 11.9703,7.9867C11.9865,8.2411 11.68,8.414 11.4633,8.2944C11.3617,8.2428 11.3096,8.1386 11.2667,8.0388ZM2.9945,7.7928C3.1499,7.7249 3.3422,7.8596 3.3067,8.0322C3.2983,8.2276 3.0098,8.3028 2.9038,8.1395C2.8102,8.0261 2.8662,7.8512 2.9945,7.7928ZM12.7939,7.7889C12.9033,7.7109 13.0789,7.767 13.1223,7.8948C13.1958,8.0344 13.061,8.2257 12.9038,8.2021C12.6832,8.2222 12.6118,7.886 12.7939,7.7889ZM7.0323,8.727C7.4413,8.6206 7.8809,8.9616 7.8835,9.3827C7.9102,9.8083 7.4772,10.1856 7.0585,10.0873C6.735,10.0352 6.4811,9.7235 6.4915,9.3965C6.4907,9.0838 6.7267,8.7931 7.0323,8.727ZM8.6519,8.7261C9.067,8.6101 9.5197,8.9572 9.5131,9.388C9.5351,9.8057 9.1156,10.1703 8.7057,10.0875C8.32,10.0376 8.0354,9.6138 8.1431,9.2395C8.1974,8.9901 8.4058,8.7872 8.6519,8.7261ZM5.5292,8.9853C5.8165,8.9363 6.0857,9.2567 5.9692,9.5295C5.8843,9.8201 5.4613,9.8989 5.2743,9.6615C5.055,9.4335 5.2187,9.0168 5.5292,8.9853ZM10.2789,8.9981C10.535,8.8908 10.8494,9.1124 10.8297,9.3904C10.84,9.6964 10.4519,9.9074 10.2014,9.7288C9.9177,9.5684 9.9694,9.0965 10.2789,8.9981ZM11.6065,10.3297C11.5062,10.1427 11.6884,9.895 11.8959,9.9296C11.9993,9.9278 12.0715,10.0092 12.1377,10.0775C12.1512,10.1883 12.174,10.3205 12.0821,10.4059C11.9572,10.5639 11.6743,10.5188 11.6065,10.3297ZM3.9968,9.9816C4.1907,9.8525 4.4699,10.0451 4.4263,10.2719C4.4031,10.4725 4.1504,10.5871 3.984,10.4733C3.8054,10.37 3.812,10.0758 3.9968,9.9816ZM7.8292,10.4015C8.0832,10.2894 8.4067,10.5105 8.3826,10.7902C8.4015,11.0993 8.0013,11.3134 7.753,11.1278C7.4772,10.9684 7.5232,10.4995 7.8292,10.4015ZM6.0542,10.8156C6.2705,10.7018 6.5607,10.8983 6.541,11.1404C6.5476,11.3392 6.3536,11.5086 6.1575,11.4792C5.995,11.4757 5.8891,11.3301 5.837,11.1916C5.8396,11.0406 5.9018,10.8725 6.0542,10.8156ZM9.6497,10.8191C9.8722,10.6816 10.1883,10.8791 10.1673,11.1379C10.173,11.4085 9.8157,11.5818 9.6086,11.4041C9.4081,11.2648 9.4326,10.9299 9.6497,10.8191ZM10.275,12.3649C10.1761,12.2152 10.2925,12.0348 10.4532,12.0007C10.5824,12.0269 10.719,12.1241 10.6918,12.2739C10.673,12.4765 10.3766,12.5387 10.275,12.3649ZM5.3116,12.2349C5.351,12.1162 5.4464,11.9983 5.5861,12.0292C5.7897,12.0436 5.8501,12.3505 5.6719,12.4451C5.5117,12.5531 5.3258,12.4051 5.3116,12.2349ZM7.7062,12.371C7.7451,12.1955 7.9723,12.1101 8.1221,12.2033C8.2211,12.2458 8.25,12.3565 8.2754,12.4507C8.2618,12.4997 8.2496,12.5487 8.2377,12.5978C8.1764,12.6704 8.0976,12.7431 7.9956,12.7405C7.806,12.7627 7.6349,12.5483 7.7062,12.371Z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:fillType="evenOdd" android:pathData="M5.3545,12.185H8.1164H8.1165C8.1165,12.185 11.9999,12.5157 11.9999,8.0674C11.9999,3.7917 8.4536,3.8068 7.7682,3.8098C7.7521,3.8098 7.7376,3.8099 7.7248,3.8099H5.3544V7.5406H4.3809V8.4545H5.3545V12.185ZM6.9101,5.3579H7.9998C8.4074,5.3579 10.4578,5.5245 10.4611,8.0975C10.4643,10.6399 8.397,10.6373 8.0645,10.6368C8.0604,10.6368 8.0567,10.6368 8.0532,10.6368H6.9101V8.4544H8.6245V7.5405H6.9101V5.3579Z"/>
</vector>

View file

@ -1,13 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M8.0002,2.8001L8.0002,2.8001A3.2001,3.2001 0,0 1,11.2003 6.0001L11.2003,10.0003A3.2001,3.2001 0,0 1,8.0002 13.2003L8.0002,13.2003A3.2001,3.2001 0,0 1,4.8002 10.0003L4.8002,6.0001A3.2001,3.2001 0,0 1,8.0002 2.8001z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9994,2.8001L7.9296,3.037V9.9123L7.9994,9.9819L11.1986,8.0955L7.9994,2.8001Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9993,2.8001L4.8002,8.0955L7.9993,9.9819V6.6449V2.8001Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9992,10.5862L7.9598,10.6341V13.0832L7.9992,13.198L11.2003,8.7007L7.9992,10.5862Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9993,13.1982V10.5864L4.8002,8.7009L7.9993,13.1982Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9991,9.9821L11.1982,8.0956L7.9991,6.645V9.9821Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M4.8002,8.0956L7.9992,9.9821V6.645L4.8002,8.0956Z"/>
</group>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="22dp"
android:viewportHeight="22" android:viewportWidth="22"
android:width="22dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:fillType="evenOdd" android:pathData="M11.825,8.8687L14.3,7.425V10.3125L11.825,8.8687ZM14.3,15.0562L11,16.9813L7.7,15.0562V11.6875L11,13.6125L14.3,11.6875V15.0562ZM7.7,7.425L10.175,8.8687L7.7,10.3125V7.425ZM11.4125,9.5562L13.8875,11L11.4125,12.4438V9.5562ZM10.5875,12.4438L8.1125,11L10.5875,9.5562V12.4438ZM13.8875,6.7375L11,8.3875L8.1125,6.7375L11,5.0188L13.8875,6.7375ZM6.875,6.4625V15.4688L11,17.8062L15.125,15.4688V6.4625L11,4.125L6.875,6.4625Z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M12.4155,5.3206C12.265,5.2041 12.0856,5.0449 11.7587,5.0051C11.452,4.9653 11.1395,5.1671 10.9282,5.3007C10.717,5.4343 10.3177,5.8266 10.1528,5.946C9.9879,6.0654 9.5654,6.1762 8.8854,6.577C8.2054,6.9778 5.5376,8.6606 5.5376,8.6606L6.2321,8.6691L3.136,10.2354H3.4456L3,10.5679C3,10.5679 3.3935,10.6703 3.7234,10.4656V10.5594C3.7234,10.5594 7.4097,9.1325 8.1215,9.502L7.6875,9.6271C7.7251,9.6271 8.4254,9.6725 8.4254,9.6725C8.4423,9.815 8.4911,9.952 8.5681,10.0738C8.6451,10.1957 8.7485,10.2994 8.8709,10.3775C9.2934,10.6504 9.3021,10.801 9.3021,10.801C9.3021,10.801 9.0822,10.8891 9.0822,11C9.0822,11 9.4063,10.9034 9.7072,10.9119C9.8982,10.919 10.0876,10.9486 10.2714,11C10.2714,11 10.2483,10.8806 9.956,10.801C9.6638,10.7214 9.3744,10.4088 9.2326,10.2382C9.1458,10.1296 9.0892,10.0008 9.0684,9.8642C9.0476,9.7276 9.0633,9.5881 9.114,9.4593C9.2153,9.2007 9.5683,9.0585 10.2975,8.689C11.1568,8.2513 11.3536,7.9272 11.4751,7.6742C11.5966,7.4212 11.776,6.9181 11.8773,6.6822C12.0046,6.378 12.1609,6.216 12.2911,6.1194C12.4213,6.0227 13,5.8095 13,5.8095C13,5.8095 12.5573,5.4315 12.4155,5.3206Z"/>
</vector>

View file

@ -1,10 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M7.9994,4.5169C9.2703,4.5169 10.3006,3.9535 10.3006,3.2585C10.3006,2.5634 9.2703,2 7.9994,2C6.7285,2 5.6982,2.5634 5.6982,3.2585C5.6982,3.9535 6.7285,4.5169 7.9994,4.5169Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9994,13.9991C9.2703,13.9991 10.3006,13.4357 10.3006,12.7406C10.3006,12.0456 9.2703,11.4822 7.9994,11.4822C6.7285,11.4822 5.6982,12.0456 5.6982,12.7406C5.6982,13.4357 6.7285,13.9991 7.9994,13.9991Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M4.7927,6.259C5.4282,5.2241 5.424,4.1032 4.7835,3.7555C4.1429,3.4077 3.1086,3.9648 2.4731,4.9997C1.8376,6.0346 1.8418,7.1554 2.4823,7.5032C3.1228,7.8509 4.1572,7.2939 4.7927,6.259Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M13.5251,10.9994C14.1605,9.9645 14.1567,8.8438 13.5166,8.4963C12.8765,8.1488 11.8425,8.706 11.207,9.7409C10.5715,10.7758 10.5753,11.8965 11.2154,12.244C11.8555,12.5915 12.8896,12.0343 13.5251,10.9994Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M4.7837,12.2438C5.4243,11.896 5.4284,10.7752 4.793,9.7403C4.1575,8.7054 3.1231,8.1483 2.4826,8.4961C1.842,8.8438 1.8379,9.9647 2.4733,10.9996C3.1088,12.0345 4.1432,12.5915 4.7837,12.2438Z"/>
<path android:fillColor="#A1A1A4" android:pathData="M13.517,7.5035C14.1571,7.156 14.1609,6.0353 13.5255,5.0004C12.89,3.9655 11.856,3.4083 11.2159,3.7558C10.5758,4.1033 10.572,5.224 11.2075,6.2589C11.8429,7.2938 12.8769,7.851 13.517,7.5035Z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M10.6283,6.3937C10.4378,6.2889 10.1902,6.2889 9.9808,6.3937L8.4952,7.1968L7.4858,7.7205L6.0003,8.5237C5.8098,8.6284 5.5622,8.6284 5.3527,8.5237L4.1719,7.8951C3.9815,7.7904 3.8482,7.5983 3.8482,7.3888V6.1492C3.8482,5.9397 3.9624,5.7477 4.1719,5.6429L5.3337,5.0319C5.5241,4.9271 5.7717,4.9271 5.9812,5.0319L7.143,5.6429C7.3335,5.7477 7.4668,5.9397 7.4668,6.1492V6.9524L8.4762,6.4111V5.608C8.4762,5.3985 8.3619,5.2064 8.1524,5.1017L6.0003,3.9494C5.8098,3.8447 5.5622,3.8447 5.3527,3.9494L3.1625,5.1017C2.953,5.2064 2.8387,5.3985 2.8387,5.608V7.93C2.8387,8.1395 2.953,8.3316 3.1625,8.4363L5.3527,9.5886C5.5432,9.6934 5.7908,9.6934 6.0003,9.5886L7.4858,8.803L8.4952,8.2618L9.9808,7.4761C10.1712,7.3714 10.4188,7.3714 10.6283,7.4761L11.7901,8.0872C11.9805,8.1919 12.1138,8.384 12.1138,8.5935V9.8331C12.1138,10.0426 11.9996,10.2346 11.7901,10.3394L10.6283,10.9679C10.4378,11.0727 10.1902,11.0727 9.9808,10.9679L8.819,10.3568C8.6285,10.2521 8.4952,10.06 8.4952,9.8505V9.0474L7.4858,9.5886V10.3917C7.4858,10.6013 7.6001,10.7933 7.8096,10.8981L9.9998,12.0504C10.1902,12.1551 10.4378,12.1551 10.6473,12.0504L12.8376,10.8981C13.028,10.7933 13.1613,10.6013 13.1613,10.3917V8.0697C13.1613,7.8602 13.0471,7.6682 12.8376,7.5634L10.6283,6.3937Z"/>
</vector>

File diff suppressed because one or more lines are too long

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M12.3419,10.4366L10.8935,12.0202C10.862,12.0546 10.8239,12.082 10.7816,12.1008C10.7392,12.1195 10.6936,12.1292 10.6474,12.1292H3.7811C3.7483,12.1292 3.7163,12.1194 3.6889,12.1011C3.6615,12.0828 3.6399,12.0567 3.6268,12.0261C3.6138,11.9954 3.6098,11.9616 3.6154,11.9287C3.6209,11.8957 3.6358,11.8652 3.6581,11.8408L5.1077,10.2572C5.1391,10.2228 5.177,10.1955 5.2192,10.1767C5.2614,10.158 5.307,10.1483 5.353,10.1482H12.219C12.2517,10.1482 12.2838,10.158 12.3112,10.1763C12.3385,10.1946 12.3601,10.2207 12.3732,10.2513C12.3862,10.2819 12.3902,10.3158 12.3847,10.3487C12.3791,10.3816 12.3643,10.4122 12.3419,10.4366ZM10.8935,7.2476C10.862,7.2132 10.8239,7.1858 10.7816,7.167C10.7392,7.1483 10.6936,7.1386 10.6474,7.1387H3.7811C3.7483,7.1387 3.7163,7.1484 3.6889,7.1667C3.6615,7.1851 3.6399,7.2111 3.6268,7.2418C3.6138,7.2724 3.6098,7.3063 3.6154,7.3392C3.6209,7.3721 3.6358,7.4026 3.6581,7.4271L5.1077,9.0107C5.1391,9.045 5.177,9.0723 5.2192,9.0911C5.2614,9.1098 5.307,9.1195 5.353,9.1196H12.219C12.2517,9.1196 12.2838,9.1099 12.3112,9.0915C12.3385,9.0732 12.3601,9.0472 12.3732,9.0165C12.3862,8.9859 12.3902,8.952 12.3847,8.9191C12.3791,8.8862 12.3643,8.8557 12.3419,8.8312L10.8935,7.2476ZM3.7811,6.1101H10.6474C10.6936,6.1101 10.7392,6.1005 10.7816,6.0817C10.8239,6.063 10.862,6.0356 10.8935,6.0012L12.3419,4.4175C12.3643,4.3931 12.3791,4.3626 12.3847,4.3296C12.3902,4.2967 12.3862,4.2629 12.3732,4.2323C12.3601,4.2016 12.3385,4.1755 12.3112,4.1572C12.2838,4.1389 12.2517,4.1291 12.219,4.1291H5.353C5.307,4.1292 5.2614,4.1389 5.2192,4.1577C5.177,4.1764 5.1391,4.2038 5.1077,4.2381L3.6585,5.8217C3.6362,5.8461 3.6213,5.8766 3.6157,5.9095C3.6102,5.9424 3.6141,5.9762 3.6272,6.0068C3.6402,6.0374 3.6617,6.0635 3.689,6.0819C3.7163,6.1002 3.7483,6.11 3.7811,6.1101Z"/>
</vector>

View file

@ -1,11 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M0,0h16v16h-16z"/>
<group>
<clip-path android:pathData="M7.871,2.8386L7.871,2.8386A5.0322,5.0322 0,0 1,12.9032 7.8709L12.9032,7.8709A5.0322,5.0322 0,0 1,7.871 12.9031L7.871,12.9031A5.0322,5.0322 0,0 1,2.8387 7.8709L2.8387,7.8709A5.0322,5.0322 0,0 1,7.871 2.8386z"/>
<path android:fillColor="#A1A1A4" android:pathData="M7.9897,3.6149C7.4207,3.5991 6.8543,3.6974 6.324,3.9042C5.7937,4.111 5.3102,4.4221 4.9022,4.8189C4.4941,5.2158 4.1698,5.6904 3.9483,6.2148C3.7269,6.7392 3.6128,7.3026 3.6129,7.8718C3.6129,7.9804 3.6171,8.0882 3.6254,8.196C3.637,8.3484 3.6031,8.5007 3.5282,8.6339C3.4533,8.7671 3.3406,8.8752 3.2044,8.9445L2.8387,9.1311V10.0009L3.9156,9.4519L4.2646,9.2741L4.6084,9.0993L10.7771,5.9558L11.4703,5.6027L12.9033,4.8726V4.0028L11.4829,4.7275L10.271,5.3452L4.4189,8.326C4.3992,8.175 4.3894,8.0228 4.3895,7.8706C4.3903,7.2632 4.5498,6.6667 4.8521,6.1399C5.1544,5.6132 5.589,5.1746 6.113,4.8675C6.637,4.5605 7.2321,4.3956 7.8393,4.3893C8.4466,4.3829 9.045,4.5354 9.5753,4.8315L10.2689,4.478L10.3725,4.4251C9.678,3.9211 8.8474,3.6388 7.9897,3.6149ZM12.9033,5.7427L4.9586,9.7878L4.2654,10.1418L2.8387,10.8694V11.7383L4.2553,11.0161L5.4673,10.3984L11.3256,7.4139C11.3453,7.5657 11.3551,7.7187 11.355,7.8718C11.3546,8.4798 11.1952,9.0771 10.8926,9.6045C10.59,10.1319 10.1548,10.5709 9.6301,10.8781C9.1054,11.1853 8.5094,11.3498 7.9015,11.3555C7.2935,11.3612 6.6946,11.2078 6.1642,10.9105L6.1219,10.9331L5.37,11.3164C6.0053,11.7776 6.7557,12.0541 7.5383,12.1155C8.3209,12.177 9.1053,12.0208 9.8048,11.6644C10.5042,11.308 11.0915,10.7651 11.5018,10.0958C11.912,9.4265 12.1293,8.6568 12.1296,7.8718C12.1296,7.7628 12.1254,7.6529 12.117,7.5447C12.1055,7.3924 12.1393,7.24 12.2141,7.1069C12.289,6.9737 12.4015,6.8656 12.5376,6.7962L12.9033,6.6095V5.7427Z"/>
</group>
</group>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#A1A1A4" android:pathData="M8.5216,13C7.8414,13 7.345,12.8281 7.0322,12.4844C6.7425,12.1934 6.5744,11.7939 6.5646,11.3732C6.5611,11.2455 6.5893,11.1189 6.6465,11.006C6.6999,10.9085 6.777,10.8275 6.8699,10.7715C6.9779,10.715 7.097,10.6856 7.2177,10.6856C7.3384,10.6856 7.4574,10.715 7.5654,10.7715C7.6582,10.8273 7.7349,10.9083 7.7875,11.006C7.8464,11.1184 7.8757,11.2451 7.8724,11.3732C7.8784,11.5263 7.8366,11.6772 7.7532,11.8029C7.6838,11.9056 7.5844,11.9818 7.4702,12.0203C7.5819,12.1695 7.74,12.2729 7.917,12.3125C8.1149,12.3728 8.3199,12.4038 8.5261,12.4046C8.799,12.4071 9.0663,12.3233 9.2931,12.164C9.5284,11.9896 9.7038,11.7403 9.7935,11.4531C9.9071,11.1112 9.9626,10.7511 9.9573,10.3891C9.9665,10.0117 9.9058,9.636 9.7786,9.2828C9.6829,9.0003 9.5013,8.7587 9.2618,8.5953C9.0392,8.4475 8.7807,8.3699 8.5173,8.3719C8.2877,8.3923 8.0663,8.4711 7.8724,8.6016L7.3942,8.8531V8.6016L9.5433,5.5769H6.5646V8.7156C6.5562,8.9421 6.6133,9.166 6.7284,9.3578C6.7842,9.4414 6.8595,9.5085 6.9472,9.5525C7.0348,9.5966 7.1319,9.6162 7.2288,9.6093C7.4081,9.6055 7.5814,9.5415 7.7233,9.4265C7.8841,9.3029 8.0241,9.1518 8.1373,8.9796C8.1475,8.9495 8.1663,8.9233 8.1912,8.9047C8.2111,8.8866 8.2366,8.8766 8.263,8.8766C8.3147,8.8815 8.3637,8.9034 8.403,8.939C8.4528,8.9974 8.4798,9.0732 8.4789,9.1515C8.4732,9.2042 8.4642,9.2563 8.4521,9.3078C8.3463,9.5765 8.1689,9.8075 7.9412,9.9732C7.7298,10.1233 7.48,10.2029 7.2247,10.2016C6.5798,10.2016 6.133,10.0682 5.8844,9.8016C5.7566,9.6542 5.6581,9.4815 5.5947,9.2937C5.5312,9.1059 5.5042,8.9067 5.515,8.7078V5.5769H4V4.9937H5.5221V3.664L5.1736,3.2982V3H6.1848L6.5646,3.2063V4.9937L10.4993,4.9812L10.8911,5.3937L8.4784,7.9372C8.6241,7.8757 8.7776,7.8368 8.9341,7.8216C9.2437,7.8358 9.5458,7.9263 9.8159,8.0857C10.1287,8.2417 10.3923,8.4885 10.5755,8.7969C10.7396,9.0579 10.8558,9.3488 10.9181,9.6547C10.9692,9.8959 10.9967,10.142 11,10.3891C11.0016,10.8607 10.8997,11.3264 10.7021,11.75C10.5148,12.1602 10.1995,12.491 9.8085,12.6876C9.4082,12.8945 8.9678,13.0014 8.5216,13Z"/>
</vector>

View file

@ -1,8 +0,0 @@
<vector android:autoMirrored="true" android:height="22dp"
android:viewportHeight="22" android:viewportWidth="22"
android:width="22dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M5,6h12v12h-12z"/>
<path android:fillColor="#A1A1A4" android:pathData="M16.5405,9.6149C15.978,9.0955 15.1999,8.3024 14.5662,7.7399L14.5287,7.7137C14.4663,7.6636 14.3959,7.6243 14.3205,7.5974C12.7924,7.3124 5.6805,5.983 5.5418,5.9999C5.5029,6.0054 5.4657,6.0195 5.433,6.0412L5.3974,6.0693C5.3535,6.1138 5.3202,6.1677 5.2999,6.2268L5.2905,6.2512V6.3843V6.4049C6.0911,8.6343 9.2524,15.9374 9.8749,17.6512C9.9124,17.7674 9.9837,17.9887 10.1168,17.9999H10.1468C10.218,17.9999 10.5218,17.5987 10.5218,17.5987C10.5218,17.5987 15.9518,11.0137 16.5012,10.3124C16.5723,10.226 16.635,10.1331 16.6887,10.0349C16.7023,9.9581 16.6959,9.879 16.6699,9.8054C16.644,9.7318 16.5994,9.6662 16.5405,9.6149ZM11.9149,10.3818L14.2324,8.4599L15.5918,9.7124L11.9149,10.3818ZM11.0149,10.2562L7.0249,6.9862L13.4805,8.1768L11.0149,10.2562ZM11.3749,11.113L15.4587,10.4549L10.7899,16.0799L11.3749,11.113ZM6.483,7.3124L10.6812,10.8749L10.0737,16.0837L6.483,7.3124Z"/>
</group>
</vector>

View file

@ -47,4 +47,11 @@ dependencies {
/** Security */
implementation(deps.spongecastle.core)
/** Chucker */
/** Chucker */
debugImplementation(deps.chucker)
externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)
}

View file

@ -1,38 +0,0 @@
package com.tangem.datasource.api.paymentology
import com.tangem.datasource.api.paymentology.models.request.CheckRegistrationRequests
import com.tangem.datasource.api.paymentology.models.request.RegisterKYCRequest
import com.tangem.datasource.api.paymentology.models.request.RegisterWalletRequest
import com.tangem.datasource.api.paymentology.models.response.AttestationResponse
import com.tangem.datasource.api.paymentology.models.response.RegisterWalletResponse
import com.tangem.datasource.api.paymentology.models.response.RegistrationResponse
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
/**
* Interface of Paymentology Api.
*
* IMPORTANT: Cannot replace [Headers] annotations with OkHttpClient.addHeader(), because OkHttp adds encoding to
* content type value. But Paymentology API expects a value without the charset.
*
[REDACTED_AUTHOR]
*/
interface PaymentologyApi {
@Headers("Content-Type: application/json")
@POST("card/verify")
suspend fun checkRegistration(@Body request: CheckRegistrationRequests): RegistrationResponse
@Headers("Content-Type: application/json")
@POST("card/get_challenge")
suspend fun requestAttestationChallenge(@Body request: CheckRegistrationRequests.Item): AttestationResponse
@Headers("Content-Type: application/json")
@POST("card/set_pin")
suspend fun registerWallet(@Body request: RegisterWalletRequest): RegisterWalletResponse
@Headers("Content-Type: application/json")
@POST("card/kyc")
suspend fun registerKYC(@Body request: RegisterKYCRequest): RegisterWalletResponse
}

View file

@ -1,30 +0,0 @@
package com.tangem.datasource.api.paymentology
import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.utils.allowLogging
import okhttp3.OkHttpClient
import retrofit2.Retrofit
/**
[REDACTED_AUTHOR]
*/
// TODO("Remove after removing Redux")
@Deprecated("Use PaymentologyApi")
object PaymentologyApiService {
val api = createApi()
private const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/"
private fun createApi(): PaymentologyApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverter.networkMoshiConverter)
.baseUrl(PAYMENTOLOGY_BASE_URL)
.client(
OkHttpClient.Builder()
.allowLogging()
.build(),
)
.build()
.create(PaymentologyApi::class.java)
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.datasource.utils.RequestHeader
import com.tangem.datasource.utils.RequestHeader.AuthenticationHeader
import com.tangem.datasource.utils.RequestHeader.CacheControlHeader
import com.tangem.datasource.utils.addHeaders
import com.tangem.datasource.utils.allowLogging
import com.tangem.datasource.utils.addLoggers
import okhttp3.OkHttpClient
import retrofit2.Retrofit
@ -35,7 +35,7 @@ object TangemTechService {
.client(
OkHttpClient.Builder()
.addHeaders(*headers.toTypedArray())
.allowLogging()
.addLoggers()
.build(),
)
.build()

View file

@ -1,17 +1,18 @@
package com.tangem.datasource.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
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.datasource.utils.addLoggers
import com.tangem.lib.auth.AuthProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
@ -25,7 +26,7 @@ class NetworkModule {
@Provides
@Singleton
fun provideTangemTechApi(@NetworkMoshi moshi: Moshi): TangemTechApi {
fun provideTangemTechApi(@NetworkMoshi moshi: Moshi, @ApplicationContext context: Context): TangemTechApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
@ -37,35 +38,24 @@ class NetworkModule {
// TODO("refactor header init") get auth data after biometric auth to avoid race condition
// AuthenticationHeader(authProvider),
)
.allowLogging()
.addLoggers(context)
.build(),
)
.build()
.create(TangemTechApi::class.java)
}
@Provides
@Singleton
fun providePaymentologyApi(@NetworkMoshi moshi: Moshi): PaymentologyApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(PAYMENTOLOGY_BASE_URL)
.client(
OkHttpClient.Builder()
.allowLogging()
.build(),
)
.build()
.create(PaymentologyApi::class.java)
}
@Provides
@Singleton
@PromotionOneInch
fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi {
fun providePromotionOneInchApi(
authProvider: AuthProvider,
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
): PromotionApi {
val okClient = OkHttpClient.Builder()
.addHeaders(AuthenticationHeader(authProvider))
.allowLogging()
.addLoggers(context)
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.readTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)

View file

@ -1,12 +1,14 @@
package com.tangem.datasource.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.oneinch.OneInchApi
import com.tangem.datasource.api.oneinch.OneInchApiFactory
import com.tangem.datasource.utils.allowLogging
import com.tangem.datasource.utils.addLoggers
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
@ -19,23 +21,31 @@ class OneInchApisModule {
@Provides
@Singleton
fun provideOneInchApiFactory(@NetworkMoshi moshi: Moshi): OneInchApiFactory {
val apiFactory = OneInchApiFactory()
apiFactory.putApi(ETH_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_ETH_PATH, moshi))
apiFactory.putApi(BSC_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_BSC_PATH, moshi))
apiFactory.putApi(POLYGON_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_POLYGON_PATH, moshi))
apiFactory.putApi(OPTIMISM_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_OPTIMISM_PATH, moshi))
apiFactory.putApi(ARBITRUM_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_ARBITRUM_PATH, moshi))
apiFactory.putApi(GNOSIS_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_GNOSIS_PATH, moshi))
apiFactory.putApi(
AVALANCHE_NETWORK,
createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_AVALANCHE_PATH, moshi),
fun provideOneInchApiFactory(@NetworkMoshi moshi: Moshi, @ApplicationContext context: Context): OneInchApiFactory {
val networks = mapOf(
ETH_NETWORK to ONE_INCH_ETH_PATH,
BSC_NETWORK to ONE_INCH_BSC_PATH,
POLYGON_NETWORK to ONE_INCH_POLYGON_PATH,
OPTIMISM_NETWORK to ONE_INCH_OPTIMISM_PATH,
ARBITRUM_NETWORK to ONE_INCH_ARBITRUM_PATH,
GNOSIS_NETWORK to ONE_INCH_GNOSIS_PATH,
AVALANCHE_NETWORK to ONE_INCH_AVALANCHE_PATH,
FANTOM_NETWORK to ONE_INCH_FANTOM_PATH,
)
apiFactory.putApi(FANTOM_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_FANTOM_PATH, moshi))
val apiFactory = OneInchApiFactory()
for ((network, path) in networks) {
apiFactory.putApi(
networkId = network,
api = createOneInchApiWithUrl("$ONE_INCH_BASE_URL$path", moshi, context),
)
}
return apiFactory
}
private fun createOneInchApiWithUrl(url: String, moshi: Moshi): OneInchApi {
private fun createOneInchApiWithUrl(url: String, moshi: Moshi, context: Context): OneInchApi {
return Retrofit.Builder()
.addConverterFactory(
MoshiConverterFactory.create(moshi),
@ -43,7 +53,7 @@ class OneInchApisModule {
.baseUrl(url)
.client(
OkHttpClient.Builder()
.allowLogging()
.addLoggers(context)
.build(),
)
.build()

View file

@ -13,6 +13,7 @@ internal class BalanceStateHidingSettingsStore(
return getSyncOrNull() ?: BalanceHidingSettings(
isHidingEnabledInSettings = false,
isBalanceHidden = false,
isBalanceHidingNotificationEnabled = true,
)
}
}

View file

@ -1,5 +1,7 @@
package com.tangem.datasource.utils
import android.content.Context
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import okhttp3.Interceptor
@ -25,8 +27,11 @@ internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeade
*
* @param level logging level. By default, only the request body.
*/
internal fun OkHttpClient.Builder.allowLogging(): OkHttpClient.Builder {
return if (BuildConfig.DEBUG) {
internal fun OkHttpClient.Builder.addLoggers(context: Context? = null): OkHttpClient.Builder {
return if (BuildConfig.LOG_ENABLED) {
context?.let {
addInterceptor(interceptor = ChuckerInterceptor(it))
}
addInterceptor(interceptor = createNetworkLoggingInterceptor())
} else {
this

View file

@ -26,5 +26,13 @@
{
"name": "DARK_THEME_ENABLED",
"version": "5.0.0"
},
{
"name": "REDESIGNED_MANAGE_TOKENS_SCREEN_ENABLED",
"version": "undefined"
},
{
"name": "REDESIGNED_SEND_SCREEN_ENABLED",
"version": "undefined"
}
]

View file

@ -19,14 +19,14 @@ enum class AppScreen(val isDialogFragment: Boolean = false) {
OnboardingOther,
Wallet,
WalletDetails,
Send,
Send(isDialogFragment = true),
Details,
DetailsSecurity,
CardSettings,
AppSettings,
ResetToFactory,
AccessCodeRecovery,
AddTokens,
ManageTokens,
AddCustomToken,
WalletConnectSessions,
QrScan,
@ -36,4 +36,5 @@ enum class AppScreen(val isDialogFragment: Boolean = false) {
SaveWallet(isDialogFragment = true),
WalletSelector(isDialogFragment = true),
AppCurrencySelector,
ModalNotification(isDialogFragment = true),
}

View file

@ -28,6 +28,10 @@
<string name="app_settings_theme_mode_system">Как в системе</string>
<string name="app_settings_theme_selector_title">Тема</string>
<string name="app_settings_title">Настройки приложения</string>
<string name="balance_hidden_description">Чтобы скрыть или показать баланс, просто поверните ваше устройство вниз или отключите опцию его в разделе \"Настройки\"</string>
<string name="balance_hidden_do_not_show_button">Больше не показывать</string>
<string name="balance_hidden_got_it_button">Понятно</string>
<string name="balance_hidden_title">Балансы скрыты</string>
<string name="biometric_lockout_permanent_warning_description">Пожалуйста, отсканируйте карту</string>
<string name="biometric_lockout_warning_description">Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту</string>
<string name="biometric_lockout_warning_title">Слишком много попыток</string>
@ -210,6 +214,24 @@
<string name="main_tokens">Токены</string>
<string name="manage_tokens_add">Добавить</string>
<string name="manage_tokens_edit">Изменить</string>
<string name="manage_tokens_network_selector_native_title">Основная сеть</string>
<string name="manage_tokens_network_selector_non_native_subtitle">Не основной или основной блокчейн, на котором размещен токен</string>
<string name="manage_tokens_network_selector_non_native_title">Не основные сети</string>
<string name="manage_tokens_network_selector_other_title">Сети</string>
<string name="manage_tokens_network_selector_title">Выберите сети</string>
<string name="manage_tokens_network_selector_wallet">Кошелек</string>
<string name="manage_tokens_nothing_found">Не удалось найти этот токен, вы можете добавить его вручную.</string>
<plurals name="manage_tokens_number_of_wallets_android">
<item quantity="one">%1$d из %2$d кошелька</item>
<item quantity="few">%1$d из %2$d кошельков</item>
<item quantity="many">%1$d из %2$d кошельков</item>
<item quantity="other">%1$d из %2$d кошельков</item>
</plurals>
<string name="manage_tokens_search_placeholder">например Bitcoin</string>
<string name="manage_tokens_title">Рыночная капитализация</string>
<string name="manage_tokens_unavailable_description">Выбранный токен не доступен в кошельке на данный момент. Но не переживайте, вы можете выразить свой интерес проголосовав за его добавление.</string>
<string name="manage_tokens_unavailable_vote">Голосовать</string>
<string name="manage_tokens_wallet_selector_title">Выберите кошелек</string>
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
<string name="onboarding_access_code_feature_1_title">Защита</string>
<string name="onboarding_access_code_feature_2_description">Позже вы сможете установить индивидуальный код доступа для каждой карты</string>
@ -434,6 +456,9 @@
<string name="swapping_token_list_title">Выберите токен</string>
<string name="swapping_token_list_your_tokens">Ваши токены</string>
<string name="swapping_token_not_available">не доступен</string>
<string name="toast_balances_hidden">Балансы скрыты</string>
<string name="toast_balances_shown">Балансы показаны</string>
<string name="toast_undo">Отменить</string>
<plurals name="token_count">
<item quantity="one">%d токен</item>
<item quantity="few">%d токена</item>

View file

@ -28,6 +28,10 @@
<string name="app_settings_theme_mode_system">System default</string>
<string name="app_settings_theme_selector_title">Theme</string>
<string name="app_settings_title">App Settings</string>
<string name="balance_hidden_description">To hide or show your balances, simply flip your device screen down, or switch it off in Settings</string>
<string name="balance_hidden_do_not_show_button">Don\'t show again</string>
<string name="balance_hidden_got_it_button">Got it</string>
<string name="balance_hidden_title">Balances are hidden</string>
<string name="biometric_lockout_permanent_warning_description">Please scan the card</string>
<string name="biometric_lockout_warning_description">Please try again in 30 seconds or scan the card</string>
<string name="biometric_lockout_warning_title">Too many attempts</string>
@ -222,10 +226,9 @@
<string name="manage_tokens_network_selector_title">Choose networks</string>
<string name="manage_tokens_network_selector_wallet">Wallet</string>
<string name="manage_tokens_nothing_found">Couldnt find this token, you can add it manually</string>
<string name="manage_tokens_number_of_wallets">%d of %#@total_wallets@</string>
<plurals name="manage_tokens_number_of_walletstotal_wallets">
<item quantity="one">%d wallet</item>
<item quantity="other">%d wallets</item>
<plurals name="manage_tokens_number_of_wallets_android">
<item quantity="one">%1$d of %2$d wallet</item>
<item quantity="other">%1$d of %2$d wallets</item>
</plurals>
<string name="manage_tokens_search_placeholder">e.g. BTC I trust, hodl I must</string>
<string name="manage_tokens_title">Coin market cap</string>
@ -392,6 +395,7 @@
<string name="send_fee_picker_normal">Normal</string>
<string name="send_fee_picker_priority">Priority</string>
<string name="send_max_amount_label">Maximum amount</string>
<string name="send_max_amount">Max</string>
<string name="send_network_fee_title">Network fee</string>
<string name="send_title_currency_format">Sending %s</string>
<string name="send_total_label">Total</string>
@ -400,6 +404,7 @@
<string name="send_total_subtitle_format">%s will be sent</string>
<string name="send_transaction_success">Transaction has been successfully signed and sent to the blockchain node. Wallet balance will be updated in a while</string>
<string name="send_validation_invalid_address">Invalid address</string>
<string name="send_insufficient_funds">Insufficient funds for transfer</string>
<string name="shop_buy_now">Buy now</string>
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
<string name="shop_one_wallet">Tangem Wallet</string>
@ -452,6 +457,9 @@
<string name="swapping_token_list_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>
<string name="swapping_token_not_available">not available</string>
<string name="toast_balances_hidden">Balances hidden</string>
<string name="toast_balances_shown">Balances shown</string>
<string name="toast_undo">Undo</string>
<plurals name="token_count">
<item quantity="one">%d token</item>
<item quantity="other">%d tokens</item>

View file

@ -28,6 +28,7 @@ dependencies {
implementation(deps.compose.paging)
implementation(deps.compose.ui.tooling)
implementation(deps.compose.ui.utils)
implementation(deps.compose.coil)
/** Other libraries */
implementation(deps.compose.accompanist.systemUiController)

View file

@ -7,11 +7,10 @@ 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.Shape
import androidx.compose.ui.tooling.preview.Preview
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.*
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.res.TangemTheme
@ -118,6 +117,32 @@ fun PrimaryButtonIconEnd(
)
}
/**
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=68%3A20&mode=design&t=YXX8vqJcB9jn0wPp-1)
* */
@Composable
fun PrimaryButtonIconEndTwoLines(
text: String,
@DrawableRes iconResId: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier,
showProgress: Boolean = false,
enabled: Boolean = true,
additionalText: String? = null,
) {
TangemButton(
modifier = modifier,
text = text,
icon = TangemButtonIconPosition.End(iconResId),
onClick = onClick,
colors = TangemButtonsDefaults.primaryButtonColors,
enabled = enabled,
showProgress = showProgress,
additionalText = additionalText,
size = TangemButtonSize.TwoLines,
)
}
/**
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A258&t=TmfD6UBHPg9uYfev-4)
* */
@ -148,9 +173,10 @@ fun SecondaryButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
size: TangemButtonSize = TangemButtonSize.Default,
showProgress: Boolean = false,
enabled: Boolean = true,
size: TangemButtonSize = TangemButtonSize.Default,
shape: Shape = size.toShape(),
) {
TangemButton(
modifier = modifier,
@ -161,6 +187,7 @@ fun SecondaryButton(
enabled = enabled,
showProgress = showProgress,
size = size,
shape = shape,
)
}
@ -267,6 +294,13 @@ private fun PrimaryButtonSample() {
enabled = false,
onClick = { },
)
PrimaryButtonIconEndTwoLines(
modifier = Modifier.fillMaxWidth(),
text = "Manage tokens",
iconResId = R.drawable.ic_tangem_24,
onClick = { },
additionalText = "Manage these tokens",
)
PrimaryButtonIconStart(
modifier = Modifier.fillMaxWidth(),
text = "Manage tokens",

View file

@ -8,14 +8,15 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.res.TangemTheme
@Composable
fun TangemBottomSheetDraggableHeader() {
fun TangemBottomSheetDraggableHeader(color: Color = TangemTheme.colors.background.primary) {
Surface(
modifier = Modifier
.height(TangemTheme.dimens.size20),
color = TangemTheme.colors.background.primary,
color = color,
) {
Box(
modifier = Modifier

View file

@ -0,0 +1,106 @@
package com.tangem.core.ui.components.buttons
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.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.tooling.preview.Preview
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
/**
* Small button config
*
* @property text text
* @property onClick lambda be invoked when action component is clicked
*
*/
data class SmallButtonConfig(
val text: TextReference,
val onClick: () -> Unit,
)
/**
* Primary small button
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=68%3A20&mode=design&t=fjwUkRtMUA4Q4s5r-1)
*
* @property config Config, containing parameters for a button
*/
@Composable
fun PrimarySmallButton(config: SmallButtonConfig, modifier: Modifier = Modifier) {
SmallButton(config = config, isPrimary = true, modifier = modifier)
}
/**
* Secondary small button
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=68%3A20&mode=design&t=fjwUkRtMUA4Q4s5r-1)
*
* @property config Config, containing parameters for a button
*/
@Composable
fun SecondarySmallButton(config: SmallButtonConfig, modifier: Modifier = Modifier) {
SmallButton(config = config, isPrimary = false, modifier = modifier)
}
@Composable
private fun SmallButton(config: SmallButtonConfig, isPrimary: Boolean, modifier: Modifier = Modifier) {
val shape = RoundedCornerShape(size = TangemTheme.dimens.radius16)
Box(
modifier = modifier
.defaultMinSize(minWidth = TangemTheme.dimens.size46, minHeight = TangemTheme.dimens.size24)
.clip(shape)
.background(
color = if (isPrimary) TangemTheme.colors.button.primary else TangemTheme.colors.button.secondary,
shape = shape,
)
.clickable(enabled = true, onClick = config.onClick)
.padding(
vertical = TangemTheme.dimens.spacing2,
),
contentAlignment = Alignment.Center,
) {
Text(
text = config.text.resolveReference(),
color = if (isPrimary) TangemTheme.colors.text.primary2 else TangemTheme.colors.text.primary1,
maxLines = 1,
style = TangemTheme.typography.button,
)
}
}
@Preview(showBackground = true)
@Composable
private fun Preview_SmallButton_Light() {
TangemTheme(isDark = false) {
ButtonsSample()
}
}
@Preview(showBackground = true)
@Composable
private fun Preview_SmallButton_Dark() {
TangemTheme(isDark = true) {
ButtonsSample()
}
}
@Composable
private fun ButtonsSample() {
Column(
modifier = Modifier.background(TangemTheme.colors.background.primary),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
val config = SmallButtonConfig(
text = TextReference.Str(value = "Add"),
onClick = {},
)
PrimarySmallButton(config = config)
SecondarySmallButton(config = config.copy(text = TextReference.Str(value = "Add")))
}
}

View file

@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
@ -26,16 +27,18 @@ fun TangemButton(
showProgress: Boolean,
enabled: Boolean,
modifier: Modifier = Modifier,
additionalText: String? = null,
size: TangemButtonSize = TangemButtonSize.Default,
elevation: ButtonElevation = TangemButtonsDefaults.elevation,
textStyle: TextStyle = TangemTheme.typography.button,
shape: Shape = size.toShape(),
) {
Button(
modifier = modifier.heightIn(min = size.toHeightDp()),
onClick = { if (!showProgress) onClick() },
enabled = enabled,
elevation = elevation,
shape = size.toShape(),
shape = shape,
colors = colors,
contentPadding = size.toContentPadding(icon = icon),
) {
@ -73,6 +76,20 @@ fun TangemButton(
contentDescription = null,
)
},
additionalText = {
if (additionalText != null) {
Text(
modifier = Modifier
.fillMaxWidth(),
text = additionalText,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.disabled,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
},
)
}
}
@ -86,18 +103,27 @@ private inline fun RowScope.ButtonContentContainer(
progressIndicator: @Composable RowScope.() -> Unit,
text: @Composable RowScope.() -> Unit,
icon: @Composable RowScope.(Int) -> Unit,
additionalText: @Composable () -> Unit,
) {
if (showProgress) {
progressIndicator()
} else {
if (buttonIcon is TangemButtonIconPosition.Start) {
icon(buttonIcon.iconResId)
Spacer(modifier = Modifier.requiredWidth(iconPadding))
}
text()
if (buttonIcon is TangemButtonIconPosition.End) {
Spacer(modifier = Modifier.requiredWidth(iconPadding))
icon(buttonIcon.iconResId)
Column {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
if (buttonIcon is TangemButtonIconPosition.Start) {
icon(buttonIcon.iconResId)
Spacer(modifier = Modifier.requiredWidth(iconPadding))
}
text()
if (buttonIcon is TangemButtonIconPosition.End) {
Spacer(modifier = Modifier.requiredWidth(iconPadding))
icon(buttonIcon.iconResId)
}
}
additionalText()
}
}
}

View file

@ -15,12 +15,13 @@ enum class TangemButtonSize {
Action,
RoundedAction,
WideAction,
TwoLines,
}
@Composable
@ReadOnlyComposable
internal fun TangemButtonSize.toHeightDp(): Dp = when (this) {
TangemButtonSize.Default -> TangemTheme.dimens.size48
TangemButtonSize.Default, TangemButtonSize.TwoLines -> TangemTheme.dimens.size48
TangemButtonSize.Text -> TangemTheme.dimens.size40
TangemButtonSize.Selector -> TangemTheme.dimens.size24
TangemButtonSize.Action,
@ -38,6 +39,7 @@ internal fun TangemButtonSize.toShape(): Shape = when (this) {
TangemButtonSize.Text -> TangemTheme.shapes.roundedCornersSmall
TangemButtonSize.Selector -> TangemTheme.shapes.roundedCornersSmall
TangemButtonSize.Action -> TangemTheme.shapes.roundedCornersMedium
TangemButtonSize.TwoLines -> TangemTheme.shapes.roundedCornersXMedium
TangemButtonSize.RoundedAction -> TangemTheme.shapes.roundedCornersLarge
}
@ -46,6 +48,7 @@ internal fun TangemButtonSize.toShape(): Shape = when (this) {
internal fun TangemButtonSize.toIconPadding(): Dp = when (this) {
TangemButtonSize.Default,
TangemButtonSize.WideAction,
TangemButtonSize.TwoLines,
-> TangemTheme.dimens.spacing4
TangemButtonSize.Text -> TangemTheme.dimens.spacing8
TangemButtonSize.Selector -> 0.dp
@ -92,6 +95,12 @@ internal fun TangemButtonSize.toContentPadding(icon: TangemButtonIconPosition):
start = horizontalPadding.first,
end = horizontalPadding.second,
)
TangemButtonSize.TwoLines -> PaddingValues(
top = TangemTheme.dimens.spacing6,
bottom = TangemTheme.dimens.spacing6,
start = horizontalPadding.first,
end = horizontalPadding.second,
)
}
}
@ -101,6 +110,7 @@ internal fun TangemButtonSize.toHorizontalContentPadding(icon: TangemButtonIconP
return when (this) {
TangemButtonSize.Default,
TangemButtonSize.WideAction,
TangemButtonSize.TwoLines,
-> TangemTheme.dimens.spacing32 to TangemTheme.dimens.spacing32
TangemButtonSize.Text -> when (icon) {
is TangemButtonIconPosition.None -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing16

View file

@ -0,0 +1,161 @@
package com.tangem.core.ui.components.buttons.segmentedbutton
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
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.res.TangemTheme
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
/**
* Segmented buttons
*
* [Figma component](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=1961-2004&mode=design&t=OFPQ18YhLHVAANab-4)
*
* @param config list of buttons in SegmentedButtons
* @param onClick button click
* @param modifier component modifier
* @param color default button color
* @param selectedColor selected button color
* @param dividerColor border and divider color
* @param showIndication show ripple indication
* @param buttonContent content as separate button
*/
@Composable
inline fun <reified T> SegmentedButtons(
config: PersistentList<T>,
crossinline onClick: (T) -> Unit,
modifier: Modifier = Modifier,
color: Color = TangemTheme.colors.background.tertiary,
selectedColor: Color = TangemTheme.colors.background.action,
dividerColor: Color = TangemTheme.colors.stroke.primary,
showIndication: Boolean = true,
crossinline buttonContent: @Composable (T) -> Unit,
) {
if (config.isEmpty() || config.size == 1) return
var selected by remember { mutableIntStateOf(0) }
Row(
modifier = modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius26))
.background(dividerColor)
.padding(TangemTheme.dimens.spacing1),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing1),
) {
repeat(config.size) { index ->
val leftRadius = if (index == 0) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0
val rightRadius = if (index == config.lastIndex) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0
Box(
modifier = Modifier
.weight(1f)
.background(
color = if (index == selected) selectedColor else color,
shape = RoundedCornerShape(
topStart = leftRadius,
topEnd = rightRadius,
bottomEnd = rightRadius,
bottomStart = leftRadius,
),
)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = if (showIndication) LocalIndication.current else null,
) {
onClick(config[index])
selected = index
},
) {
buttonContent.invoke(config[index])
}
}
}
}
@Preview
@Composable
private fun SegmentedButtonsPreview_Light(
@PreviewParameter(SegmentedButtonsPreviewProvider::class) config: PersistentList<SegmentedButtonsConfigPreview>,
) {
TangemTheme {
SegmentedButtons(
config = config,
onClick = {},
) {
Text(
text = it.text,
modifier = Modifier.padding(TangemTheme.dimens.spacing16),
)
}
}
}
@Preview
@Composable
private fun SegmentedButtonsPreview_Dark(
@PreviewParameter(SegmentedButtonsPreviewProvider::class) config: PersistentList<SegmentedButtonsConfigPreview>,
) {
TangemTheme(isDark = true) {
SegmentedButtons(
config = config,
onClick = {},
) {
Text(
text = it.text,
modifier = Modifier.padding(TangemTheme.dimens.spacing16),
)
}
}
}
//region Preview config
/**
* Segmented buttons preview model
*
* @param text button title
*/
internal data class SegmentedButtonsConfigPreview(
val text: String,
)
/**
* Segmented button preview provider
*/
internal class SegmentedButtonsPreviewProvider :
CollectionPreviewParameterProvider<PersistentList<SegmentedButtonsConfigPreview>>(
collection = listOf(
persistentListOf(
SegmentedButtonsConfigPreview(
text = "Title 1",
),
SegmentedButtonsConfigPreview(
text = "Title 2",
),
SegmentedButtonsConfigPreview(
text = "Title 3",
),
),
persistentListOf(
SegmentedButtonsConfigPreview(
text = "Title 1",
),
SegmentedButtonsConfigPreview(
text = "Title 2",
),
),
),
)
//endregion

View file

@ -0,0 +1,60 @@
package com.tangem.core.ui.components.currency
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.components.currency.tokenicon.LoadingIcon
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.ImageBackgroundContrastChecker
import kotlinx.coroutines.launch
@Composable
internal inline fun DefaultCurrencyIcon(
iconData: Any,
alpha: Float,
colorFilter: ColorFilter?,
crossinline errorIcon: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
var iconBackgroundColor by remember { mutableStateOf(Color.Transparent) }
val itemBackgroundColor = TangemTheme.colors.background.primary.toArgb()
val isDarkTheme = isSystemInDarkTheme()
val coroutineScope = rememberCoroutineScope()
SubcomposeAsyncImage(
modifier = modifier
.background(
color = iconBackgroundColor,
shape = TangemTheme.shapes.roundedCorners8,
),
model = ImageRequest.Builder(context = LocalContext.current)
.data(iconData)
.crossfade(enable = true)
.allowHardware(false)
.listener(
onSuccess = { _, result ->
if (isDarkTheme) {
coroutineScope.launch {
val color = ImageBackgroundContrastChecker(
drawable = result.drawable,
backgroundColor = itemBackgroundColor,
).getContrastColorIfNeeded(isDarkTheme)
iconBackgroundColor = color
}
}
},
).build(),
loading = { LoadingIcon() },
error = { errorIcon() },
alpha = alpha,
colorFilter = colorFilter,
contentDescription = null,
)
}

View file

@ -0,0 +1,38 @@
package com.tangem.core.ui.components.currency.fiaticon
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import com.tangem.core.ui.R
import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
/**
* Simple icon from network
*
* @param url link to icon
* @param fallbackResId fallback icon
* @param modifier component modifier
*/
@Composable
fun FiatIcon(
url: String?,
modifier: Modifier = Modifier,
@DrawableRes fallbackResId: Int = R.drawable.ic_shape_circle,
) {
val iconData: Any = if (url.isNullOrBlank()) fallbackResId else url
DefaultCurrencyIcon(
modifier = modifier,
iconData = iconData,
errorIcon = {
Image(
painter = painterResource(id = fallbackResId),
contentDescription = null,
)
},
alpha = 1f,
colorFilter = null,
)
}

View file

@ -1,44 +1,36 @@
package com.tangem.feature.wallet.presentation.common.component.token.icon
package com.tangem.core.ui.components.currency.tokenicon
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.*
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.graphics.ColorFilter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.ImageBackgroundContrastChecker
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
import kotlinx.coroutines.launch
import com.tangem.core.ui.R
import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
@Composable
internal fun ContentIcon(
icon: TokenItemState.IconState,
icon: TokenIconState,
alpha: Float,
colorFilter: ColorFilter?,
modifier: Modifier = Modifier,
) {
when (icon) {
is TokenItemState.IconState.CoinIcon -> CoinIcon(
is TokenIconState.CoinIcon -> CoinIcon(
modifier = modifier,
url = icon.url,
fallbackResId = icon.fallbackResId,
alpha = alpha,
colorFilter = colorFilter,
)
is TokenItemState.IconState.TokenIcon -> TokenIcon(
is TokenIconState.TokenIcon -> TokenIcon(
modifier = modifier,
url = icon.url,
alpha = alpha,
@ -52,14 +44,14 @@ internal fun ContentIcon(
)
},
)
is TokenItemState.IconState.CustomTokenIcon -> CustomTokenIcon(
is TokenIconState.CustomTokenIcon -> CustomTokenIcon(
modifier = modifier,
tint = icon.tint,
background = icon.background,
alpha = alpha,
)
TokenItemState.IconState.Loading,
TokenItemState.IconState.Locked,
TokenIconState.Loading,
TokenIconState.Locked,
-> Unit
}
}
@ -128,48 +120,4 @@ private fun CustomTokenIcon(tint: Color, background: Color, alpha: Float, modifi
contentDescription = null,
)
}
}
@Composable
private inline fun DefaultCurrencyIcon(
iconData: Any,
alpha: Float,
colorFilter: ColorFilter?,
crossinline errorIcon: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
var iconBackgroundColor by remember { mutableStateOf(Color.Transparent) }
val itemBackgroundColor = TangemTheme.colors.background.primary.toArgb()
val isDarkTheme = isSystemInDarkTheme()
val coroutineScope = rememberCoroutineScope()
SubcomposeAsyncImage(
modifier = modifier
.background(
color = iconBackgroundColor,
shape = TangemTheme.shapes.roundedCorners8,
),
model = ImageRequest.Builder(context = LocalContext.current)
.data(iconData)
.crossfade(enable = true)
.allowHardware(false)
.listener(
onSuccess = { _, result ->
if (isDarkTheme) {
coroutineScope.launch {
val color = ImageBackgroundContrastChecker(
drawable = result.drawable,
backgroundColor = itemBackgroundColor,
).getContrastColorIfNeeded(isDarkTheme)
iconBackgroundColor = color
}
}
},
).build(),
loading = { LoadingIcon() },
error = { errorIcon() },
alpha = alpha,
colorFilter = colorFilter,
contentDescription = null,
)
}

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.common.component.token.icon
package com.tangem.core.ui.components.currency.tokenicon
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.common.component.token.icon
package com.tangem.core.ui.components.currency.tokenicon
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@ -14,14 +14,22 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.IconState as TokenIconState
private const val GRAY_SCALE_SATURATION = 0f
private const val GRAY_SCALE_ALPHA = 0.4f
private const val NORMAL_ALPHA = 1f
/**
* Cryptocurrency icon with network badge
*
* TODO [separate domain from ui]([REDACTED_JIRA])
*
* @param state cryptocurrency icon config
* @param modifier component modifier
* @param shouldDisplayNetwork specifies whether to display network badge
*/
@Composable
internal fun TokenIcon(state: TokenIconState, modifier: Modifier = Modifier) {
fun TokenIcon(state: TokenIconState, modifier: Modifier = Modifier, shouldDisplayNetwork: Boolean = true) {
BaseContainer(modifier = modifier) {
val iconModifier = Modifier
.align(Alignment.Center)
@ -34,7 +42,11 @@ internal fun TokenIcon(state: TokenIconState, modifier: Modifier = Modifier) {
is TokenIconState.CustomTokenIcon,
is TokenIconState.TokenIcon,
-> {
ContentIconContainer(modifier = iconModifier, icon = state)
ContentIconContainer(
icon = state,
modifier = iconModifier,
shouldDisplayNetwork = shouldDisplayNetwork,
)
}
}
}
@ -60,7 +72,11 @@ private fun LockedIcon(modifier: Modifier = Modifier) {
}
@Composable
private fun BoxScope.ContentIconContainer(icon: TokenIconState, modifier: Modifier = Modifier) {
private fun BoxScope.ContentIconContainer(
icon: TokenIconState,
modifier: Modifier = Modifier,
shouldDisplayNetwork: Boolean = true,
) {
val networkBadgeOffset = TangemTheme.dimens.spacing4
val (alpha, colorFilter) = remember(icon.isGrayscale) {
if (icon.isGrayscale) {
@ -77,7 +93,7 @@ private fun BoxScope.ContentIconContainer(icon: TokenIconState, modifier: Modifi
colorFilter = colorFilter,
)
if (icon.networkBadgeIconResId != null) {
if (icon.networkBadgeIconResId != null && shouldDisplayNetwork) {
NetworkBadge(
modifier = Modifier
.offset(x = networkBadgeOffset, y = -networkBadgeOffset)

View file

@ -0,0 +1,85 @@
package com.tangem.core.ui.components.currency.tokenicon
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
/**
* Represents the various states an icon can be in.
*
* [REDACTED_TODO_COMMENT]
*/
@Immutable
sealed class TokenIconState {
abstract val isGrayscale: Boolean
abstract val showCustomBadge: Boolean
abstract val networkBadgeIconResId: Int?
/**
* Represents a coin icon.
*
* @property url The URL where the coin icon can be fetched from. May be `null` if not found.
* @property fallbackResId The drawable resource ID to be used as a fallback if the URL is not available.
* @property isGrayscale Specifies whether to show the icon in grayscale.
* @property showCustomBadge Specifies whether to show the custom token badge.
*/
data class CoinIcon(
val url: String?,
@DrawableRes val fallbackResId: Int,
override val isGrayscale: Boolean,
override val showCustomBadge: Boolean,
) : TokenIconState() {
override val networkBadgeIconResId: Int? = null
}
/**
* Represents a token icon.
*
* @property url The URL where the token icon can be fetched from. May be `null` if not found.
* @property networkBadgeIconResId The drawable resource ID for the network badge.
* @property isGrayscale Specifies whether to show the icon in grayscale.
* @property showCustomBadge Specifies whether to show the custom token badge.
* @property fallbackTint The color to be used for tinting the fallback icon.
* @property fallbackBackground The background color to be used for the fallback icon.
*/
data class TokenIcon(
val url: String?,
@DrawableRes override val networkBadgeIconResId: Int,
override val isGrayscale: Boolean,
override val showCustomBadge: Boolean,
val fallbackTint: Color,
val fallbackBackground: Color,
) : TokenIconState()
/**
* Represents a custom token icon.
*
* @property tint The color to be used for tinting the icon.
* @property background The background color to be used for the icon.
* @property networkBadgeIconResId The drawable resource ID for the network badge.
* @property isGrayscale Specifies whether to show the icon in grayscale.
*/
data class CustomTokenIcon(
val tint: Color,
val background: Color,
@DrawableRes override val networkBadgeIconResId: Int,
override val isGrayscale: Boolean,
) : TokenIconState() {
override val showCustomBadge: Boolean = true
}
object Loading : TokenIconState() {
override val isGrayscale: Boolean = false
override val showCustomBadge: Boolean = false
override val networkBadgeIconResId: Int? = null
}
object Locked : TokenIconState() {
override val isGrayscale: Boolean = false
override val showCustomBadge: Boolean = false
override val networkBadgeIconResId: Int? = null
}
}

View file

@ -1,27 +1,27 @@
package com.tangem.feature.wallet.presentation.common.utils
package com.tangem.core.ui.components.currency.tokenicon.converter
import com.tangem.common.Converter
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
import com.tangem.core.ui.extensions.getTintForTokenIcon
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
import com.tangem.utils.converter.Converter
internal class CryptoCurrencyToIconStateConverter : Converter<CryptoCurrencyStatus, TokenItemState.IconState> {
/**
* Converts [CryptoCurrencyStatus] to [TokenIconState]
*/
class CryptoCurrencyToIconStateConverter : Converter<CryptoCurrencyStatus, TokenIconState> {
override fun convert(value: CryptoCurrencyStatus): TokenItemState.IconState {
override fun convert(value: CryptoCurrencyStatus): TokenIconState {
return when (val currency = value.currency) {
is CryptoCurrency.Coin -> getIconStateForCoin(currency, value.value.isError)
is CryptoCurrency.Token -> getIconStateForToken(currency, value.value.isError)
}
}
private fun getIconStateForCoin(
coin: CryptoCurrency.Coin,
isUnreachable: Boolean,
): TokenItemState.IconState.CoinIcon {
return TokenItemState.IconState.CoinIcon(
private fun getIconStateForCoin(coin: CryptoCurrency.Coin, isUnreachable: Boolean): TokenIconState.CoinIcon {
return TokenIconState.CoinIcon(
url = coin.iconUrl,
fallbackResId = coin.networkIconResId,
isGrayscale = coin.network.isTestnet || isUnreachable,
@ -29,20 +29,20 @@ internal class CryptoCurrencyToIconStateConverter : Converter<CryptoCurrencyStat
)
}
private fun getIconStateForToken(token: CryptoCurrency.Token, isErrorStatus: Boolean): TokenItemState.IconState {
private fun getIconStateForToken(token: CryptoCurrency.Token, isErrorStatus: Boolean): TokenIconState {
val isGrayscale = token.network.isTestnet || isErrorStatus
val background = token.tryGetBackgroundForTokenIcon(isGrayscale)
val tint = getTintForTokenIcon(background)
return if (token.isCustom && token.iconUrl == null) {
TokenItemState.IconState.CustomTokenIcon(
TokenIconState.CustomTokenIcon(
tint = tint,
background = background,
networkBadgeIconResId = token.networkIconResId,
isGrayscale = isGrayscale,
)
} else {
TokenItemState.IconState.TokenIcon(
TokenIconState.TokenIcon(
url = token.iconUrl,
networkBadgeIconResId = token.networkIconResId,
isGrayscale = isGrayscale,

View file

@ -28,6 +28,7 @@ data class NotificationConfig(
data class PrimaryButtonConfig(
val text: TextReference,
val additionalText: TextReference? = null,
@DrawableRes val iconResId: Int? = null,
val onClick: () -> Unit,
) : ButtonsState()

View file

@ -35,7 +35,7 @@ sealed class StateEvent<in A> {
*/
data class Triggered<A>(
val data: A,
internal val onConsume: () -> Unit,
val onConsume: () -> Unit,
) : StateEvent<A>()
}

View file

@ -45,6 +45,7 @@ fun getActiveIconRes(blockchainId: String): Int {
"aleph-zero", "aleph-zero/test" -> R.drawable.img_azero_22
"octaspace", "octaspace/test" -> R.drawable.img_octaspace_22
"chia", "chia/test" -> R.drawable.img_chia_22
"decimal", "decimal/testnet" -> R.drawable.img_decimal_22
else -> R.drawable.ic_alert_24
}
}
@ -88,6 +89,53 @@ fun getActiveIconResByCoinId(coinId: String): Int {
"octaspace" -> R.drawable.img_octaspace_22
"chia" -> R.drawable.img_chia_22
"near" -> R.drawable.img_near_22
"decimal" -> R.drawable.img_decimal_22
else -> R.drawable.ic_alert_24
}
}
@Suppress("ComplexMethod")
@DrawableRes
fun getGreyedOutIconRes(blockchainId: String): Int {
return when (blockchainId) {
"ARBITRUM-ONE", "ARBITRUM/test" -> R.drawable.ic_arbitrum_22
"BTC", "BTC/test" -> R.drawable.ic_bitcoin_16
"BCH" -> R.drawable.ic_bitcoin_cash_16
"LTC" -> R.drawable.ic_litecoin_22
"ETH", "ETH/test" -> R.drawable.ic_eth_16
"ETC", "ETC/test" -> R.drawable.ic_eth_16
"RSK" -> R.drawable.ic_rsk_16
"CARDANO", "CARDANO-S" -> R.drawable.ic_cardano_16
"XTZ" -> R.drawable.ic_tezos_16
"XRP" -> R.drawable.ic_xrp_22
"XLM", "XLM/test" -> R.drawable.ic_stellar_16
"AVALANCHE", "AVALANCHE/test" -> R.drawable.ic_avalanche_22
"POLYGON", "POLYGON/test" -> R.drawable.ic_polygon_16
"SOLANA", "SOLANA/test" -> R.drawable.ic_solana_16
"FTM", "FTM/test" -> R.drawable.ic_fantom_22
"BSC", "BSC/test", "BINANCE", "BINANCE/test" -> R.drawable.ic_bsc_16
"DOGE" -> R.drawable.ic_dogecoin_16
"TRON", "TRON/test" -> R.drawable.ic_tron_22
"GNO" -> R.drawable.ic_gnosis_22
"ETH-Pow", "ETH-Pow/test" -> R.drawable.ic_ethereumpow_22
"ETH-Fair" -> R.drawable.ic_ethereumfair_22
"Polkadot", "Polkadot/test" -> R.drawable.ic_polkadot_16
"Kusama" -> R.drawable.ic_kusama_16
"OPTIMISM", "OPTIMISM/test" -> R.drawable.ic_optimism_22
"DASH" -> R.drawable.ic_dash_22
"KAS" -> R.drawable.ic_kaspa_22
"The-Open-Network", "The-Open-Network/test" -> R.drawable.ic_ton_22
"KAVA", "KAVA/test" -> R.drawable.ic_kava_22
"ravencoin", "ravencoin/test" -> R.drawable.ic_ravencoin_22
"cosmos", "cosmos/test" -> R.drawable.ic_cosmos_22
"terra", "terra-luna" -> R.drawable.ic_terra_22
"terra-2", "terra-luna-2" -> R.drawable.ic_terra2_22
"cronos" -> R.drawable.ic_cronos_22
"TELOS", "TELOS/test" -> R.drawable.ic_telos_22
"aleph-zero", "aleph-zero/test" -> R.drawable.ic_azero_22
"octaspace", "octaspace/test" -> R.drawable.ic_octaspace_22
"chia", "chia/test" -> R.drawable.ic_chia_22
"decimal", "decimal/test" -> R.drawable.ic_decimal_22
else -> R.drawable.ic_alert_24
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.core.ui.extensions
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
/**
* Utility class for keeping image reference when it can be an image resource or a URL.
*
* It necessary to use [Immutable] annotation because all sealed interface has runtime stability.
* All subclasses are stable.
*/
sealed class ImageReference {
data class Url(val url: String) : ImageReference()
data class Res(@DrawableRes val resId: Int) : ImageReference()
/**
* Provides reference of any type. It can be used for image loading,
* when the type of reference is resolved further down the call site in the library.
*
* @return image reference either as an image resource ([Int]) or a URL ([String])
*/
fun getReference(): Any {
return when (this) {
is Res -> resId
is Url -> url
}
}
}

View file

@ -145,6 +145,7 @@ class TangemColors internal constructor(
class Background internal constructor(
primary: Color,
secondary: Color,
tertiary: Color,
plain: Color,
action: Color,
fade: Color,
@ -153,6 +154,8 @@ class TangemColors internal constructor(
private set
var secondary by mutableStateOf(secondary)
private set
var tertiary by mutableStateOf(tertiary)
private set
var plain by mutableStateOf(plain)
private set
var action by mutableStateOf(action)
@ -163,6 +166,7 @@ class TangemColors internal constructor(
fun update(other: Background) {
primary = other.primary
secondary = other.secondary
tertiary = other.tertiary
plain = other.plain
action = other.action
fade = other.fade

View file

@ -32,7 +32,9 @@ data class TangemDimens internal constructor(
val radius16: Dp = 16.dp,
val radius18: Dp = 18.dp,
val radius24: Dp = 24.dp,
val radius26: Dp = 26.dp,
val radius28: Dp = 28.dp,
val radius36: Dp = 36.dp,
// endregion Radius
// region Size
val size0: Dp = 0.dp,
@ -71,6 +73,7 @@ data class TangemDimens internal constructor(
val size80: Dp = 80.dp,
val size84: Dp = 84.dp,
val size86: Dp = 86.dp,
val size90: Dp = 90.dp,
val size93: Dp = 93.dp,
val size96: Dp = 96.dp,
val size102: Dp = 102.dp,
@ -82,10 +85,12 @@ data class TangemDimens internal constructor(
val size164: Dp = 164.dp,
val size200: Dp = 200.dp,
val size248: Dp = 248.dp,
val size350: Dp = 350.dp,
// endregion Size
// region Spacing
val spacing0: Dp = 0.dp,
val spacing0_5: Dp = 0.5.dp,
val spacing1: Dp = 1.dp,
val spacing2: Dp = 2.dp,
val spacing3: Dp = 3.dp,
val spacing4: Dp = 4.dp,

View file

@ -3,12 +3,7 @@ package com.tangem.core.ui.res
import androidx.compose.material.Colors
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.runtime.*
// TODO: use isSystemInDarkTheme() for automatic color detection
internal const val IS_SYSTEM_IN_DARK_THEME: Boolean = false
@ -116,12 +111,13 @@ private fun lightThemeColors(): TangemColors {
background = TangemColors.Background(
primary = TangemColorPalette.White,
secondary = TangemColorPalette.Light1,
tertiary = TangemColorPalette.Light1,
plain = TangemColorPalette.White,
action = TangemColorPalette.Black,
action = TangemColorPalette.White,
fade = TangemColorPalette.White,
),
control = TangemColors.Control(
checked = TangemColorPalette.Meadow,
checked = TangemColorPalette.Dark6,
unchecked = TangemColorPalette.Light2,
key = TangemColorPalette.White,
),
@ -168,14 +164,15 @@ private fun darkThemeColors(): TangemColors {
background = TangemColors.Background(
primary = TangemColorPalette.Dark6,
secondary = TangemColorPalette.Black,
tertiary = TangemColorPalette.Dark6,
plain = TangemColorPalette.Black,
action = TangemColorPalette.Light4,
action = TangemColorPalette.Dark5,
fade = TangemColorPalette.Black,
),
control = TangemColors.Control(
checked = TangemColorPalette.Meadow,
checked = TangemColorPalette.Azure,
unchecked = TangemColorPalette.Dark4,
key = TangemColorPalette.Light1,
key = TangemColorPalette.White,
),
stroke = TangemColors.Stroke(
primary = TangemColorPalette.Dark5,

View file

@ -12,7 +12,9 @@ object BigDecimalFormatter {
private const val TEMP_CURRENCY_CODE = "USD"
fun formatCryptoAmount(cryptoAmount: BigDecimal, cryptoCurrency: String, decimals: Int): String {
fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: String, decimals: Int): String {
if (cryptoAmount == null) return EMPTY_BALANCE_SIGN
val formatter = NumberFormat.getNumberInstance().apply {
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
minimumFractionDigits = 2
@ -23,11 +25,13 @@ object BigDecimalFormatter {
}
fun formatFiatAmount(
fiatAmount: BigDecimal,
fiatAmount: BigDecimal?,
fiatCurrencyCode: String,
fiatCurrencySymbol: String,
locale: Locale = Locale.getDefault(),
): String {
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
val formatterCurrency = getCurrency(fiatCurrencyCode)
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
currency = formatterCurrency

View file

@ -0,0 +1,23 @@
<vector android:autoMirrored="true" android:height="22dp"
android:viewportHeight="22" android:viewportWidth="22"
android:width="22dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M0,0h22v22h-22z" />
<path
android:fillColor="#000000"
android:pathData="M16.5486,12.9469L13.6283,8.469L12.5575,10.2212L15.3805,14.7964L16.646,14.2124L16.5486,12.9469Z" />
<path
android:fillColor="#000000"
android:pathData="M15.186,15.3805L12.2656,10.9026L11.0001,12.8495L13.3364,16.5486L14.7966,15.9645L15.186,15.3805Z" />
<path
android:fillColor="#000000"
android:fillType="evenOdd"
android:pathData="M5.6461,7.8851L11.0001,4.7701L16.3541,7.8851V14.1152L11.0001,17.2303L7.0089,14.9913L7,16L10.9028,18.3984C11.0001,18.4958 11.0974,18.4958 11.1948,18.3984L17.3275,14.894C17.4249,14.7966 17.5222,14.6993 17.5222,14.602V7.5931C17.5222,7.4958 17.4249,7.3984 17.3275,7.3011L11.1948,3.7966C11.0974,3.6993 11.0001,3.6993 10.9028,3.7966L4.77,7.2037C4.6727,7.3011 4.5753,7.3984 4.5753,7.4958V14.5046C4.5753,14.602 4.6727,14.6993 4.77,14.7966L6,15.5L6.5,14.5L5.7435,14.1152V7.8851H5.6461Z" />
<path
android:fillColor="#000000"
android:pathData="M13.3364,7.96H11.2921L6.5222,15.7476L8.1771,16.721L13.3364,7.96Z" />
<path
android:fillColor="#000000"
android:pathData="M8.1771,8.6636C8.6638,7.8849 10.0266,7.8849 10.6107,7.9822L6,15.5C5.8053,15.4027 5.3541,15.1857 5.062,14.9911C4.6727,14.7964 4.5753,14.7964 4.5753,14.5043V14.1149C5.6461,12.5574 7.6904,9.4424 8.1771,8.6636Z" />
</group>
</vector>

View file

@ -5,7 +5,7 @@
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:fillColor="#909090"
android:fillColor="#000000"
android:fillType="evenOdd"
android:pathData="M7.763,15.538H5.577C5.117,15.538 4.891,15.538 4.752,15.45C4.603,15.353 4.511,15.192 4.5,15.015C4.492,14.852 4.606,14.653 4.832,14.254L10.23,4.741C10.459,4.337 10.575,4.135 10.722,4.06C10.88,3.98 11.068,3.98 11.226,4.06C11.372,4.135 11.489,4.337 11.718,4.741L12.828,6.678L12.833,6.688C13.082,7.121 13.207,7.341 13.262,7.571C13.323,7.823 13.323,8.089 13.262,8.341C13.207,8.573 13.082,8.794 12.831,9.234L9.996,14.246L9.988,14.259C9.739,14.696 9.612,14.917 9.437,15.084C9.246,15.267 9.016,15.4 8.764,15.474C8.535,15.538 8.277,15.538 7.763,15.538ZM13.283,15.538H16.415C16.877,15.538 17.109,15.538 17.248,15.447C17.397,15.35 17.491,15.187 17.5,15.01C17.508,14.852 17.397,14.66 17.179,14.285C17.172,14.272 17.164,14.259 17.156,14.246L15.588,11.562L15.57,11.532C15.349,11.159 15.238,10.971 15.095,10.898C14.937,10.818 14.752,10.818 14.594,10.898C14.45,10.973 14.334,11.169 14.104,11.565L12.541,14.249L12.536,14.258C12.307,14.653 12.193,14.85 12.201,15.012C12.212,15.189 12.303,15.353 12.453,15.45C12.588,15.538 12.821,15.538 13.283,15.538Z" />
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="22">
<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"/>
android:fillColor="#000000" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:autoMirrored="true"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="#000000"
android:pathData="M10.6797,5.3445C10.272,4.4259 9.3351,4.2295 8.1882,4.4198L7.8196,3L6.9507,3.223L7.3131,4.6387C7.0845,4.696 6.8498,4.7451 6.6171,4.8106L6.2547,3.403L5.3858,3.626L5.7543,5.0458C5.567,5.099 4,5.498 4,5.498L4.2389,6.4227C4.2389,6.4227 4.8772,6.2447 4.871,6.259C5.2252,6.1669 5.3919,6.3429 5.4702,6.5086L6.4833,10.3998C6.4956,10.5123 6.475,10.7046 6.232,10.77C6.2465,10.7782 5.5999,10.9317 5.5999,10.9317L5.6946,12.0098C5.6946,12.0098 7.2472,11.615 7.451,11.5638L7.8237,13L8.6927,12.777L8.32,11.3306C8.5588,11.2754 8.7915,11.2181 9.018,11.1588L9.3886,12.597L10.2576,12.374L9.8849,10.9399C11.2233,10.6166 12.1684,9.7778 11.9748,8.4951C11.8513,7.7218 11.0009,7.0876 10.2946,7.016C10.7291,6.6334 10.9494,6.0749 10.6797,5.3445ZM10.2617,8.7304C10.4346,10.0008 8.6577,10.1563 8.0708,10.3097L7.5602,8.4092C8.1491,8.2557 9.9693,7.6113 10.2617,8.7304ZM9.191,6.1424C9.3742,7.2717 7.8546,7.4026 7.3646,7.5274L6.8992,5.8028C7.3913,5.6821 8.8183,5.097 9.191,6.1424Z" />
</vector>

View file

@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:autoMirrored="true"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="#000000"
android:pathData="M4.3968,8.0001L4.4025,10.1154L6.2,11.1731V12.4116L3.3506,10.7404V7.3815L4.3968,8.0001ZM4.3968,5.8847V7.1174L3.35,6.4981V5.2655L4.3968,4.6462L5.4487,5.2655L4.3968,5.8847ZM6.9506,5.2655L7.9974,4.6462L9.0493,5.2655L7.9974,5.8847L6.9506,5.2655Z" />
<path
android:fillColor="#000000"
android:pathData="M5.1532,9.677V8.4385L6.2,9.0577V10.2904L5.1532,9.677ZM6.9506,11.6167L7.9974,12.2359L9.0493,11.6167V12.8494L7.9974,13.4686L6.9506,12.8494V11.6167ZM10.5505,5.2655L11.5973,4.6462L12.6492,5.2655V6.4981L11.5973,7.1174V5.8847L10.5505,5.2655ZM11.5973,10.1154L11.6031,8.0001L12.6499,7.3808V10.7398L9.8006,12.4109V11.1725L11.5973,10.1154Z" />
<path
android:fillColor="#000000"
android:pathData="M10.8468,9.6769L9.8,10.2903V9.0577L10.8468,8.4385V9.6769Z" />
<path
android:fillColor="#000000"
android:pathData="M10.8468,6.3232L10.8526,7.5616L9.05,8.6193V10.7398L8.0032,11.3533L6.9564,10.7398V8.6193L5.1538,7.5616V6.3232L6.2051,5.7039L7.9968,6.7667L9.7993,5.7039L10.8513,6.3232H10.8468ZM5.1532,4.2084L7.9974,2.5315L10.8468,4.2084L9.8,4.8276L7.9974,3.7648L6.2,4.8276L5.1532,4.2084Z" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:autoMirrored="true"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="#000000"
android:pathData="M7.8738,3.2662C8.098,3.1493 8.3717,3.4204 8.2526,3.6444C8.1834,3.827 7.9211,3.8755 7.7871,3.7364C7.6452,3.6028 7.6915,3.3388 7.8738,3.2662ZM5.4626,3.5261C5.5988,3.47 5.7712,3.5948 5.7573,3.7432C5.7718,3.9043 5.5747,4.0229 5.4359,3.9464C5.252,3.8751 5.273,3.5766 5.4626,3.5261ZM10.4011,3.9509C10.1883,3.9252 10.1546,3.5906 10.3595,3.5232C10.5184,3.4457 10.6642,3.591 10.6875,3.7417C10.6541,3.8703 10.5465,3.9881 10.4011,3.9509ZM6.0113,4.5523C6.2389,4.4083 6.5638,4.6228 6.5288,4.8872C6.517,5.1543 6.1623,5.309 5.9622,5.1284C5.7687,4.9867 5.798,4.6614 6.0113,4.5523ZM9.4869,4.7051C9.5884,4.4434 10,4.4354 10.1156,4.6903C10.2299,4.8886 10.0894,5.1342 9.8857,5.2016C9.616,5.2519 9.3599,4.9604 9.4869,4.7051ZM7.5835,5.1867C7.5813,4.9617 7.7862,4.7941 8.0001,4.7787C8.1419,4.8072 8.2965,4.8676 8.3573,5.0107C8.4707,5.2082 8.37,5.48 8.1651,5.5706C8.0714,5.6231 7.9607,5.6034 7.8586,5.5973C7.7027,5.5208 7.5713,5.3706 7.5835,5.1867ZM3.9976,5.5146C4.2056,5.3867 4.4862,5.62 4.4075,5.846C4.3641,6.0414 4.1014,6.1209 3.9512,5.9962C3.7909,5.8793 3.8163,5.5982 3.9976,5.5146ZM11.6978,5.5142C11.8742,5.3772 12.1654,5.5343 12.1444,5.7571C12.1562,5.9603 11.9045,6.1135 11.7293,6.0089C11.5364,5.9222 11.5167,5.6253 11.6978,5.5142ZM8.5919,5.9187C8.8831,5.814 9.2289,5.9392 9.3949,6.1988C9.6138,6.5162 9.4974,6.9995 9.1576,7.1816C8.8056,7.397 8.2916,7.2144 8.1615,6.8213C8.014,6.4694 8.229,6.029 8.5919,5.9187ZM6.8834,5.9533C7.2062,5.8009 7.6405,5.9287 7.7964,6.2589C7.9798,6.5802 7.838,7.0276 7.51,7.1935C7.1816,7.38 6.7183,7.2386 6.5594,6.8936C6.3812,6.56 6.5395,6.1069 6.8834,5.9533ZM5.1728,6.5381C5.1973,6.3302 5.3934,6.1949 5.5913,6.1809C5.8046,6.2066 5.9749,6.3722 5.9981,6.588C5.9841,6.8016 5.819,7.0104 5.5926,7.0091C5.3436,7.0306 5.1252,6.7828 5.1728,6.5381ZM10.2255,6.2261C10.4812,6.0773 10.8363,6.2939 10.8249,6.5877C10.8425,6.8989 10.4495,7.1183 10.1949,6.9357C9.9304,6.7837 9.951,6.3555 10.2255,6.2261ZM6.1882,7.3231C6.4872,7.2408 6.8292,7.3818 6.9759,7.6566C7.1365,7.9333 7.0708,8.3177 6.8213,8.5206C6.5013,8.8183 5.911,8.6988 5.7407,8.2926C5.545,7.9201 5.7827,7.4211 6.1882,7.3231ZM9.4286,7.3215C9.7194,7.2384 10.0609,7.3504 10.2172,7.6153C10.4238,7.9182 10.3252,8.37 10.0176,8.5653C9.6953,8.792 9.1961,8.673 9.0188,8.3184C8.8038,7.9517 9.0202,7.4334 9.4286,7.3215ZM4.2647,7.6671C4.523,7.5694 4.8094,7.8545 4.7047,8.1106C4.6408,8.3439 4.312,8.4302 4.1421,8.2581C3.9433,8.0966 4.0169,7.7372 4.2647,7.6671ZM11.2667,8.0388C11.2593,7.8247 11.4159,7.6352 11.6354,7.6234C11.8123,7.6514 11.9843,7.7941 11.9703,7.9867C11.9865,8.2411 11.68,8.414 11.4633,8.2944C11.3617,8.2428 11.3096,8.1386 11.2667,8.0388ZM2.9945,7.7928C3.1499,7.7249 3.3422,7.8596 3.3067,8.0322C3.2983,8.2276 3.0098,8.3028 2.9038,8.1395C2.8102,8.0261 2.8662,7.8512 2.9945,7.7928ZM12.7939,7.7889C12.9033,7.7109 13.0789,7.767 13.1223,7.8948C13.1958,8.0344 13.061,8.2257 12.9038,8.2021C12.6832,8.2222 12.6118,7.886 12.7939,7.7889ZM7.0323,8.727C7.4413,8.6206 7.8809,8.9616 7.8835,9.3827C7.9102,9.8083 7.4772,10.1856 7.0585,10.0873C6.735,10.0352 6.4811,9.7235 6.4915,9.3965C6.4907,9.0838 6.7267,8.7931 7.0323,8.727ZM8.6519,8.7261C9.067,8.6101 9.5197,8.9572 9.5131,9.388C9.5351,9.8057 9.1156,10.1703 8.7057,10.0875C8.32,10.0376 8.0354,9.6138 8.1431,9.2395C8.1974,8.9901 8.4058,8.7872 8.6519,8.7261ZM5.5292,8.9853C5.8165,8.9363 6.0857,9.2567 5.9692,9.5295C5.8843,9.8201 5.4613,9.8989 5.2743,9.6615C5.055,9.4335 5.2187,9.0168 5.5292,8.9853ZM10.2789,8.9981C10.535,8.8908 10.8494,9.1124 10.8297,9.3904C10.84,9.6964 10.4519,9.9074 10.2014,9.7288C9.9177,9.5684 9.9694,9.0965 10.2789,8.9981ZM11.6065,10.3297C11.5062,10.1427 11.6884,9.895 11.8959,9.9296C11.9993,9.9278 12.0715,10.0092 12.1377,10.0775C12.1512,10.1883 12.174,10.3205 12.0821,10.4059C11.9572,10.5639 11.6743,10.5188 11.6065,10.3297ZM3.9968,9.9816C4.1907,9.8525 4.4699,10.0451 4.4263,10.2719C4.4031,10.4725 4.1504,10.5871 3.984,10.4733C3.8054,10.37 3.812,10.0758 3.9968,9.9816ZM7.8292,10.4015C8.0832,10.2894 8.4067,10.5105 8.3826,10.7902C8.4015,11.0993 8.0013,11.3134 7.753,11.1278C7.4772,10.9684 7.5232,10.4995 7.8292,10.4015ZM6.0542,10.8156C6.2705,10.7018 6.5607,10.8983 6.541,11.1404C6.5476,11.3392 6.3536,11.5086 6.1575,11.4792C5.995,11.4757 5.8891,11.3301 5.837,11.1916C5.8396,11.0406 5.9018,10.8725 6.0542,10.8156ZM9.6497,10.8191C9.8722,10.6816 10.1883,10.8791 10.1673,11.1379C10.173,11.4085 9.8157,11.5818 9.6086,11.4041C9.4081,11.2648 9.4326,10.9299 9.6497,10.8191ZM10.275,12.3649C10.1761,12.2152 10.2925,12.0348 10.4532,12.0007C10.5824,12.0269 10.719,12.1241 10.6918,12.2739C10.673,12.4765 10.3766,12.5387 10.275,12.3649ZM5.3116,12.2349C5.351,12.1162 5.4464,11.9983 5.5861,12.0292C5.7897,12.0436 5.8501,12.3505 5.6719,12.4451C5.5117,12.5531 5.3258,12.4051 5.3116,12.2349ZM7.7062,12.371C7.7451,12.1955 7.9723,12.1101 8.1221,12.2033C8.2211,12.2458 8.25,12.3565 8.2754,12.4507C8.2618,12.4997 8.2496,12.5487 8.2377,12.5978C8.1764,12.6704 8.0976,12.7431 7.9956,12.7405C7.806,12.7627 7.6349,12.5483 7.7062,12.371Z" />
</vector>

View file

@ -5,6 +5,6 @@
android:viewportHeight="22">
<path
android:pathData="M11.415,6.503C11.372,6.507 11.212,6.521 11.061,6.534C10.799,6.555 10.406,6.603 10.342,6.624C10.325,6.629 10.255,6.642 10.185,6.654C9.672,6.737 9.036,6.949 8.678,7.157C8.613,7.195 8.543,7.233 8.521,7.241C8.44,7.273 7.903,7.658 7.704,7.825C6.237,9.061 5.459,10.99 5.785,12.584C5.825,12.778 5.83,12.78 5.964,12.678C6.179,12.514 6.368,12.389 6.631,12.235C6.785,12.144 7.451,11.811 7.478,11.811C7.482,11.811 7.533,11.789 7.591,11.763C8.174,11.487 9.873,10.889 10.726,10.658C10.758,10.649 10.905,10.608 11.05,10.568C11.86,10.341 11.995,10.311 11.995,10.35C11.995,10.358 11.949,10.383 11.892,10.403C11.613,10.502 10.855,10.816 10.824,10.843C10.814,10.852 10.795,10.86 10.781,10.86C10.768,10.86 10.699,10.888 10.629,10.921C10.559,10.953 10.496,10.981 10.488,10.981C10.482,10.981 10.409,11.012 10.329,11.052C10.248,11.091 10.178,11.123 10.173,11.123C10.168,11.123 10.089,11.159 9.996,11.204C9.903,11.248 9.822,11.285 9.817,11.285C9.734,11.285 7.837,12.304 7.238,12.67C7.102,12.754 6.987,12.822 6.983,12.822C6.977,12.822 6.791,12.942 6.197,13.328C5.534,13.759 4.427,14.564 3.745,15.113C3.664,15.179 3.567,15.256 3.53,15.287C2.939,15.759 2.863,15.845 3.199,15.664C3.954,15.259 4.795,14.854 5.4,14.607C5.77,14.455 6.161,14.342 6.425,14.309L6.535,14.296L6.809,14.578C7.526,15.314 8.297,15.688 9.432,15.849C9.702,15.887 10.568,15.882 10.774,15.841C10.835,15.83 10.967,15.805 11.069,15.788C11.173,15.769 11.283,15.747 11.316,15.738C11.349,15.729 11.432,15.705 11.502,15.687C12.809,15.338 13.928,14.506 14.918,13.144C14.94,13.113 15.028,12.995 15.115,12.88C15.202,12.765 15.291,12.643 15.313,12.61C15.336,12.577 15.391,12.494 15.437,12.427C15.953,11.68 16.683,10.45 17.154,9.536C17.205,9.436 17.288,9.276 17.338,9.182C17.387,9.087 17.547,8.759 17.695,8.454C17.842,8.148 17.971,7.882 17.981,7.862C18.023,7.782 18.007,7.764 17.813,7.684C17.791,7.675 17.645,7.625 17.488,7.572C17.331,7.52 17.154,7.461 17.095,7.44C16.837,7.351 16.408,7.223 15.885,7.081C15.432,6.957 15.308,6.924 15.137,6.886C15.05,6.867 14.957,6.843 14.93,6.834C14.903,6.825 14.828,6.806 14.763,6.795C14.58,6.759 14.5,6.742 14.33,6.704C13.697,6.564 13.148,6.513 12.213,6.503C11.818,6.498 11.459,6.5 11.415,6.503Z"
android:fillColor="#909090"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View file

@ -6,31 +6,31 @@
<path
android:pathData="M11.173,1.882C11.085,1.801 11.04,1.8 11.033,1.8C11.025,1.8 10.98,1.801 10.893,1.882C10.803,1.964 10.696,2.106 10.58,2.326C10.35,2.765 10.129,3.428 9.939,4.283C9.725,5.245 9.556,6.421 9.449,7.738C9.964,7.987 10.494,8.258 11.033,8.549C11.571,8.258 12.101,7.987 12.616,7.737C12.51,6.421 12.341,5.245 12.127,4.283C11.936,3.428 11.716,2.765 11.485,2.326C11.37,2.106 11.262,1.964 11.173,1.882ZM10.582,8.795C10.186,8.585 9.796,8.386 9.415,8.2C9.386,8.623 9.363,9.06 9.348,9.508C9.549,9.387 9.754,9.266 9.962,9.147C10.169,9.027 10.376,8.91 10.582,8.795ZM9.034,7.54C9.343,3.93 10.121,1.369 11.033,1.369C11.944,1.369 12.723,3.93 13.032,7.54C16.314,6.002 18.923,5.396 19.378,6.185C19.834,6.973 18.004,8.928 15.031,11C18.004,13.072 19.834,15.027 19.378,15.816C18.923,16.604 16.314,15.998 13.032,14.46C12.723,18.07 11.944,20.631 11.033,20.631C10.121,20.631 9.343,18.07 9.034,14.46C5.751,15.998 3.143,16.604 2.687,15.816C2.231,15.027 4.061,13.072 7.034,11C4.061,8.928 2.231,6.973 2.687,6.185C3.143,5.396 5.751,6.002 9.034,7.54ZM7.414,10.739C6.326,9.989 5.392,9.254 4.665,8.588C4.019,7.996 3.555,7.473 3.29,7.054C3.157,6.845 3.087,6.681 3.061,6.562C3.035,6.446 3.056,6.406 3.06,6.4C3.063,6.394 3.087,6.355 3.2,6.32C3.317,6.283 3.494,6.262 3.742,6.272C4.238,6.291 4.923,6.432 5.759,6.695C6.7,6.991 7.803,7.433 8.997,7.998C8.956,8.569 8.926,9.163 8.908,9.774C8.387,10.095 7.887,10.418 7.414,10.739ZM7.414,11.261C6.326,12.011 5.392,12.746 4.665,13.412C4.019,14.004 3.555,14.527 3.29,14.946C3.157,15.156 3.087,15.319 3.061,15.438C3.035,15.554 3.056,15.594 3.06,15.6C3.063,15.606 3.087,15.645 3.2,15.68C3.317,15.717 3.494,15.738 3.742,15.728C4.238,15.709 4.923,15.568 5.759,15.305C6.7,15.009 7.803,14.567 8.997,14.002C8.956,13.431 8.926,12.837 8.908,12.226C8.387,11.905 7.887,11.582 7.414,11.261ZM8.897,11.712C8.517,11.475 8.15,11.237 7.797,11C8.15,10.763 8.517,10.525 8.897,10.288C8.893,10.523 8.891,10.76 8.891,11C8.891,11.24 8.893,11.477 8.897,11.712ZM9.333,11.981C9.326,11.658 9.322,11.331 9.322,11C9.322,10.669 9.326,10.342 9.333,10.019C9.608,9.852 9.89,9.685 10.177,9.519C10.464,9.354 10.75,9.193 11.033,9.039C11.316,9.193 11.601,9.354 11.888,9.519C12.175,9.685 12.457,9.852 12.733,10.019C12.74,10.342 12.744,10.669 12.744,11C12.744,11.331 12.74,11.658 12.733,11.981C12.457,12.148 12.175,12.315 11.888,12.481C11.601,12.646 11.316,12.807 11.033,12.962C10.75,12.807 10.464,12.646 10.177,12.481C9.89,12.315 9.608,12.148 9.333,11.981ZM9.348,12.492C9.363,12.94 9.386,13.377 9.415,13.8C9.796,13.614 10.186,13.415 10.582,13.205C10.376,13.09 10.169,12.973 9.962,12.854C9.754,12.734 9.549,12.613 9.348,12.492ZM11.033,13.451C10.494,13.742 9.964,14.013 9.449,14.262C9.556,15.579 9.725,16.755 9.939,17.717C10.129,18.572 10.35,19.235 10.58,19.674C10.696,19.894 10.803,20.036 10.893,20.118C10.98,20.199 11.025,20.201 11.033,20.201C11.04,20.201 11.085,20.199 11.173,20.118C11.262,20.036 11.37,19.894 11.485,19.674C11.716,19.235 11.936,18.572 12.127,17.717C12.341,16.755 12.51,15.579 12.616,14.263C12.101,14.013 11.571,13.742 11.033,13.451ZM13.068,14.002C14.262,14.568 15.366,15.009 16.306,15.305C17.142,15.568 17.827,15.709 18.323,15.728C18.571,15.738 18.748,15.717 18.865,15.68C18.978,15.645 19.002,15.606 19.006,15.6C19.009,15.594 19.031,15.554 19.005,15.438C18.978,15.319 18.908,15.156 18.775,14.946C18.511,14.527 18.046,14.004 17.4,13.412C16.674,12.746 15.739,12.011 14.651,11.261C14.178,11.582 13.679,11.905 13.157,12.226C13.14,12.837 13.11,13.431 13.068,14.002ZM14.651,10.739C15.739,9.989 16.674,9.254 17.4,8.588C18.046,7.996 18.511,7.473 18.775,7.054C18.908,6.845 18.978,6.681 19.005,6.562C19.031,6.446 19.009,6.406 19.006,6.4C19.002,6.394 18.978,6.355 18.865,6.32C18.748,6.283 18.571,6.262 18.323,6.272C17.827,6.291 17.142,6.432 16.306,6.695C15.366,6.991 14.262,7.433 13.068,7.998C13.11,8.569 13.14,9.163 13.157,9.774C13.679,10.095 14.178,10.418 14.651,10.739ZM13.168,10.288C13.549,10.525 13.916,10.763 14.268,11C13.916,11.237 13.549,11.475 13.168,11.712C13.172,11.477 13.174,11.24 13.174,11C13.174,10.76 13.172,10.523 13.168,10.288ZM12.718,9.508C12.702,9.06 12.68,8.623 12.651,8.199C12.269,8.386 11.879,8.585 11.483,8.795C11.689,8.91 11.896,9.027 12.103,9.147C12.311,9.266 12.516,9.387 12.718,9.508ZM12.651,13.801C12.269,13.614 11.879,13.415 11.483,13.205C11.689,13.09 11.896,12.973 12.103,12.854C12.311,12.734 12.516,12.613 12.718,12.492C12.702,12.94 12.68,13.377 12.651,13.801Z"
android:strokeWidth="0.3"
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:fillType="evenOdd"
android:strokeColor="#B0B0B0"/>
android:strokeColor="#000000" />
<path
android:pathData="M4.877,8.303C5.251,8.303 5.554,8.606 5.554,8.981C5.554,9.355 5.251,9.658 4.877,9.658C4.503,9.658 4.2,9.355 4.2,8.981C4.2,8.606 4.503,8.303 4.877,8.303Z"
android:strokeWidth="0.5"
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:fillType="evenOdd"
android:strokeColor="#B0B0B0"/>
android:strokeColor="#000000" />
<path
android:pathData="M15.641,5.983C16.015,5.983 16.318,6.286 16.318,6.66C16.318,7.034 16.015,7.338 15.641,7.338C15.267,7.338 14.963,7.034 14.963,6.66C14.963,6.286 15.267,5.983 15.641,5.983Z"
android:strokeWidth="0.5"
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:fillType="evenOdd"
android:strokeColor="#B0B0B0"/>
android:strokeColor="#000000" />
<path
android:pathData="M9.668,16.618C10.042,16.618 10.346,16.921 10.346,17.295C10.346,17.669 10.042,17.972 9.668,17.972C9.294,17.972 8.99,17.669 8.99,17.295C8.99,16.921 9.294,16.618 9.668,16.618Z"
android:strokeWidth="0.5"
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:fillType="evenOdd"
android:strokeColor="#B0B0B0"/>
android:strokeColor="#000000" />
<path
android:pathData="M10.995,9.848C11.629,9.848 12.142,10.361 12.142,10.995C12.142,11.629 11.629,12.142 10.995,12.142C10.361,12.142 9.848,11.629 9.848,10.995C9.848,10.361 10.361,9.848 10.995,9.848Z"
android:strokeWidth="0.5"
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:fillType="evenOdd"
android:strokeColor="#B0B0B0"/>
android:strokeColor="#000000" />
</vector>

View file

@ -4,15 +4,15 @@
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:pathData="M11.005,3.5L4.5,7.25V14.75L11.005,18.5L17.5,14.75V7.25L11.005,3.5ZM15.573,13.637L10.995,16.274L6.427,13.637V8.363L11.005,5.726L15.573,8.363V13.637Z" />
<path
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:pathData="M11.005,18.5L17.5,14.75V7.25L11.005,3.5V5.726L15.573,8.363V13.648L10.995,16.285V18.5H11.005Z" />
<path
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:pathData="M10.995,3.5L4.5,7.25V14.75L10.995,18.5V16.274L6.427,13.637V8.352L10.995,5.726V3.5Z" />
<path
android:fillColor="#B0B0B0"
android:fillColor="#000000"
android:pathData="M14.036,12.751L11.005,14.501L7.964,12.751V9.249L11.005,7.499L14.036,9.249L12.77,9.984L10.994,8.957L9.23,9.984V12.027L11.005,13.053L12.78,12.027L14.036,12.751Z" />
</vector>

View file

@ -8,9 +8,9 @@
android:pathData="M0,0h22v22h-22z"/>
<path
android:pathData="M12.97,4.846H7.66L7.22,7.304L12.012,7.311C14.372,7.311 15.07,8.168 15.05,9.588C15.038,10.317 14.724,11.549 14.588,11.948C14.224,13.012 13.477,14.227 10.677,14.223L6.02,14.221L5.578,16.682H10.875C12.743,16.682 13.538,16.464 14.379,16.076C16.246,15.215 17.356,13.373 17.8,10.97C18.462,7.392 17.636,4.846 12.97,4.846"
android:fillColor="#A1A1A4"/>
android:fillColor="#000000" />
<path
android:pathData="M5.369,9.53C3.979,9.53 3.779,10.436 3.649,10.984C3.476,11.701 3.42,11.991 3.42,11.991H8.856C10.247,11.991 10.446,11.085 10.576,10.537C10.749,9.819 10.805,9.53 10.805,9.53H5.369Z"
android:fillColor="#A1A1A4"/>
android:fillColor="#000000" />
</group>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="22dp"
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:fillColor="#000"
android:fillType="evenOdd"
android:pathData="M6.5,5H15.5V17H6.5V14.474H12.882V7.526H6.5V5ZM6.5,9.737H10.755V12.263H6.5V9.737Z" />
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:autoMirrored="true"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="#000000"
android:fillType="evenOdd"
android:pathData="M5.3545,12.185H8.1164H8.1165C8.1165,12.185 11.9999,12.5157 11.9999,8.0674C11.9999,3.7917 8.4536,3.8068 7.7682,3.8098C7.7521,3.8098 7.7376,3.8099 7.7248,3.8099H5.3544V7.5406H4.3809V8.4545H5.3545V12.185ZM6.9101,5.3579H7.9998C8.4074,5.3579 10.4578,5.5245 10.4611,8.0975C10.4643,10.6399 8.397,10.6373 8.0645,10.6368C8.0604,10.6368 8.0567,10.6368 8.0532,10.6368H6.9101V8.4544H8.6245V7.5405H6.9101V5.3579Z" />
</vector>

View file

@ -0,0 +1,25 @@
<vector android:autoMirrored="true" android:height="16dp"
android:viewportHeight="16" android:viewportWidth="16"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M8.0002,2.8001L8.0002,2.8001A3.2001,3.2001 0,0 1,11.2003 6.0001L11.2003,10.0003A3.2001,3.2001 0,0 1,8.0002 13.2003L8.0002,13.2003A3.2001,3.2001 0,0 1,4.8002 10.0003L4.8002,6.0001A3.2001,3.2001 0,0 1,8.0002 2.8001z" />
<path
android:fillColor="#000000"
android:pathData="M7.9994,2.8001L7.9296,3.037V9.9123L7.9994,9.9819L11.1986,8.0955L7.9994,2.8001Z" />
<path
android:fillColor="#000000"
android:pathData="M7.9993,2.8001L4.8002,8.0955L7.9993,9.9819V6.6449V2.8001Z" />
<path
android:fillColor="#000000"
android:pathData="M7.9992,10.5862L7.9598,10.6341V13.0832L7.9992,13.198L11.2003,8.7007L7.9992,10.5862Z" />
<path
android:fillColor="#000000"
android:pathData="M7.9993,13.1982V10.5864L4.8002,8.7009L7.9993,13.1982Z" />
<path
android:fillColor="#000000"
android:pathData="M7.9991,9.9821L11.1982,8.0956L7.9991,6.645V9.9821Z" />
<path
android:fillColor="#000000"
android:pathData="M4.8002,8.0956L7.9992,9.9821V6.645L4.8002,8.0956Z" />
</group>
</vector>

View file

@ -7,31 +7,31 @@
<group>
<clip-path android:pathData="M0,0h22v22h-22z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M10.75,3.737L10.75,5.243L7.014,10.538L6.243,10.081L10.75,3.737Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M7.445,10.793L10.75,6.111L10.749,12.751L7.445,10.793Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M11.25,5.243L11.25,3.739L15.757,10.081L15.057,10.495L11.25,5.243Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M11.25,6.095L11.249,12.752L14.625,10.752L11.25,6.095Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M10.749,15.656L10.749,17.039L9.272,14.739L10.749,15.656Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M11.249,17.035L11.249,15.657L12.701,14.756L11.249,17.035Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M13.681,14.148L14.915,13.382L11.25,18.88L11.25,17.966L13.681,14.148Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M10.75,18.879L10.75,17.965L8.29,14.135L8.284,14.126L7.085,13.382L10.75,18.879Z" />
<path
android:fillColor="#A1A1A4"
android:fillColor="#000000"
android:pathData="M15.559,12.393L13.718,11.78L11,13.479L8.282,11.78L6.44,12.394L10.072,14.647L11,15.177L11.927,14.648L15.559,12.393Z" />
</group>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#1E1E1E"
android:pathData="M2,5.27L3.28,4L20,20.72L18.73,22L15.65,18.92C14.5,19.3 13.28,19.5 12,19.5C7,19.5 2.73,16.39 1,12C1.69,10.24 2.79,8.69 4.19,7.46L2,5.27ZM12,9C12.796,9 13.559,9.316 14.121,9.879C14.684,10.441 15,11.204 15,12C15,12.35 14.94,12.69 14.83,13L11,9.17C11.31,9.06 11.65,9 12,9ZM12,4.5C17,4.5 21.27,7.61 23,12C22.18,14.08 20.79,15.88 19,17.19L17.58,15.76C18.94,14.82 20.06,13.54 20.82,12C19.17,8.64 15.76,6.5 12,6.5C10.91,6.5 9.84,6.68 8.84,7L7.3,5.47C8.74,4.85 10.33,4.5 12,4.5ZM3.18,12C4.83,15.36 8.24,17.5 12,17.5C12.69,17.5 13.37,17.43 14,17.29L11.72,15C10.29,14.85 9.15,13.71 9,12.28L5.6,8.87C4.61,9.72 3.78,10.78 3.18,12Z" />
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="22dp"
android:autoMirrored="true"
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:fillColor="#000000"
android:fillType="evenOdd"
android:pathData="M11.825,8.8687L14.3,7.425V10.3125L11.825,8.8687ZM14.3,15.0562L11,16.9813L7.7,15.0562V11.6875L11,13.6125L14.3,11.6875V15.0562ZM7.7,7.425L10.175,8.8687L7.7,10.3125V7.425ZM11.4125,9.5562L13.8875,11L11.4125,12.4438V9.5562ZM10.5875,12.4438L8.1125,11L10.5875,9.5562V12.4438ZM13.8875,6.7375L11,8.3875L8.1125,6.7375L11,5.0188L13.8875,6.7375ZM6.875,6.4625V15.4688L11,17.8062L15.125,15.4688V6.4625L11,4.125L6.875,6.4625Z" />
</vector>

View file

@ -5,14 +5,14 @@
android:viewportHeight="22">
<path
android:pathData="M7.703,12.11C8.163,12.11 8.612,11.957 8.974,11.677L6.058,8.761C5.356,9.669 5.524,10.978 6.432,11.68C6.799,11.957 7.243,12.11 7.703,12.11Z"
android:fillColor="#A1A1A4" />
android:fillColor="#000000" />
<path
android:pathData="M16.376,10.028C16.376,9.568 16.223,9.12 15.942,8.757L13.026,11.673C13.935,12.376 15.24,12.208 15.942,11.299C16.223,10.936 16.376,10.488 16.376,10.028Z"
android:fillColor="#A1A1A4" />
android:fillColor="#000000"
android:pathData="M16.376,10.028C16.376,9.568 16.223,9.12 15.942,8.757L13.026,11.673C13.935,12.376 15.24,12.208 15.942,11.299C16.223,10.936 16.376,10.488 16.376,10.028Z" />
<path
android:pathData="M17.845,6.854L16.555,8.144C17.594,9.389 17.43,11.243 16.185,12.282C15.094,13.194 13.508,13.194 12.417,12.282L11,13.699L9.587,12.286C8.342,13.325 6.488,13.161 5.449,11.916C4.536,10.824 4.536,9.239 5.449,8.148L4.787,7.486L4.159,6.854C3.4,8.103 3,9.538 3,11C3,15.419 6.581,19 11,19C15.419,19 19,15.419 19,11C19.004,9.538 18.6,8.103 17.845,6.854Z"
android:fillColor="#A1A1A4" />
android:fillColor="#000000"
android:pathData="M17.845,6.854L16.555,8.144C17.594,9.389 17.43,11.243 16.185,12.282C15.094,13.194 13.508,13.194 12.417,12.282L11,13.699L9.587,12.286C8.342,13.325 6.488,13.161 5.449,11.916C4.536,10.824 4.536,9.239 5.449,8.148L4.787,7.486L4.159,6.854C3.4,8.103 3,9.538 3,11C3,15.419 6.581,19 11,19C15.419,19 19,15.419 19,11C19.004,9.538 18.6,8.103 17.845,6.854Z" />
<path
android:pathData="M16.787,5.478C13.74,2.282 8.679,2.163 5.482,5.209C5.389,5.299 5.299,5.389 5.213,5.478C5.015,5.688 4.828,5.905 4.652,6.133L11,12.484L17.348,6.133C17.176,5.905 16.985,5.688 16.787,5.478ZM11,4.047C12.869,4.047 14.611,4.768 15.92,6.084L11,11.004L6.08,6.084C7.389,4.768 9.131,4.047 11,4.047Z"
android:fillColor="#A1A1A4" />
android:fillColor="#000000"
android:pathData="M16.787,5.478C13.74,2.282 8.679,2.163 5.482,5.209C5.389,5.299 5.299,5.389 5.213,5.478C5.015,5.688 4.828,5.905 4.652,6.133L11,12.484L17.348,6.133C17.176,5.905 16.985,5.688 16.787,5.478ZM11,4.047C12.869,4.047 14.611,4.768 15.92,6.084L11,11.004L6.08,6.084C7.389,4.768 9.131,4.047 11,4.047Z" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#1E1E1E"
android:pathData="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20ZM12,2C10.687,2 9.386,2.259 8.173,2.761C6.96,3.264 5.858,4 4.929,4.929C3.054,6.804 2,9.348 2,12C2,14.652 3.054,17.196 4.929,19.071C5.858,20 6.96,20.736 8.173,21.239C9.386,21.741 10.687,22 12,22C14.652,22 17.196,20.946 19.071,19.071C20.946,17.196 22,14.652 22,12C22,10.687 21.741,9.386 21.239,8.173C20.736,6.96 20,5.858 19.071,4.929C18.142,4 17.04,3.264 15.827,2.761C14.614,2.259 13.313,2 12,2ZM11,17H13V11H11V17Z" />
</vector>

View file

@ -4,6 +4,6 @@
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:fillColor="#C9C9C9"
android:fillColor="#000000"
android:pathData="M13.659,5.35L11.564,5.663L12.174,9.721L7.787,6.34L6.5,8.022L10.343,10.975L6.5,13.944L7.787,15.626L12.174,12.245L11.564,16.303L13.659,16.616L14.5,10.975L13.659,5.35Z" />
</vector>

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