Updated on 2026-08-14

This commit is contained in:
Tangem 2021-10-28 21:35:23 +00:00
commit d21b90dc62
30 changed files with 179 additions and 71 deletions

View file

@ -29,7 +29,7 @@ suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) {
Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString()))
} else {
val message = exception.localizedMessage ?: "Unknown exception during WalletManager update"
val message = exception.localizedMessage ?: "An error has occurred. Try later"
Result.Failure(TapError.WalletManagerUpdate.InternalError(message))
}
}

View file

@ -21,6 +21,7 @@ class PicassoHelper {
.downloader(OkHttp3Downloader(getOkHttpForPicasso(application)))
.build()
picasso.isLoggingEnabled = BuildConfig.DEBUG
picasso.setIndicatorsEnabled(BuildConfig.DEBUG)
Picasso.setSingletonInstance(picasso)
}

View file

@ -46,6 +46,8 @@ sealed class TapError(
sealed class WalletManagerUpdate {
class NoAccountError(amountToCreateAccount: String): CustomError(amountToCreateAccount)
class InternalError(message: String): CustomError(message)
object BlockchainIsUnreachable: TapError(R.string.wallet_balance_blockchain_unreachable)
object BlockchainIsUnreachableTryLater: TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
}
data class ValidateTransactionErrors(

View file

@ -10,19 +10,14 @@ import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.extensions.isPositive
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.UrlBitmapLoader
import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.features.wallet.models.hasPendingTransactions
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.persistence.UsedCardsPrefStorage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.math.BigDecimal
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
/**
[REDACTED_AUTHOR]
@ -35,24 +30,31 @@ class OnboardingManager(
var cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null
private set
suspend fun loadArtwork(): Artwork {
cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo))
suspend fun loadArtworkUrl(): String {
val cardInfo = cardInfo
?: OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
this.cardInfo = cardInfo
return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)
}
suspend fun loadArtwork(url: String): Artwork {
return withContext(Dispatchers.Main) {
suspendCoroutine { continuation ->
UrlBitmapLoader().loadBitmap(url) {
val result = when (it) {
is Result.Success -> Artwork(url, it.data)
is Result.Failure -> Artwork(url, null)
}
continuation.resume(result)
}
}
}
}
// suspend fun loadArtwork(): Artwork {
// cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
// return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo))
// }
//
// suspend fun loadArtwork(url: String): Artwork {
// return withContext(Dispatchers.Main) {
// suspendCoroutine { continuation ->
// UrlBitmapLoader().loadBitmap(url) {
// val result = when (it) {
// is Result.Success -> Artwork(url, it.data)
// is Result.Failure -> Artwork(url, null)
// }
// continuation.resume(result)
// }
// }
// }
// }
suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance {
val balance = when (val result = walletManager.safeUpdate()) {
@ -76,7 +78,10 @@ class OnboardingManager(
when (error) {
is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error)
// NoInternetConnection, WalletManagerUpdate.InternalError
else -> OnboardingWalletBalance.criticalError(error)
else -> {
Timber.e(error.localizedMessage)
OnboardingWalletBalance.criticalError(TapError.WalletManagerUpdate.BlockchainIsUnreachableTryLater)
}
}
}
}

View file

@ -10,6 +10,7 @@ import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
import com.tangem.tangem_sdk_new.extensions.fadeIn
import com.tangem.tangem_sdk_new.extensions.fadeOut
@ -73,7 +74,12 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
override fun newState(state: OnboardingNoteState) {
if (activity == null) return
state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) }
Picasso.get()
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_front_card)
pb_state.max = state.steps.size - 1
pb_state.progress = state.progress
@ -91,9 +97,13 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
private fun setBalance(state: OnboardingNoteState) {
if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
val balanceValue = state.walletBalance.value.stripZeroPlainString()
val currency = state.walletBalance.currency.currencySymbol
tv_balance_value.text = "$balanceValue $currency"
if (state.balanceCriticalError == null) {
val balanceValue = state.walletBalance.value.stripZeroPlainString()
val currency = state.walletBalance.currency.currencySymbol
tv_balance_value.text = "$balanceValue $currency"
} else {
tv_balance_value.text = ""
}
}
private fun setupNoneState(state: OnboardingNoteState) {

View file

@ -3,7 +3,6 @@ package com.tangem.tap.features.onboarding.products.note.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.features.wallet.redux.Artwork
import org.rekotlin.Action
/**
@ -17,7 +16,7 @@ sealed class OnboardingNoteAction : Action {
object ShowAddressInfoDialog : OnboardingNoteAction()
// from redux
class SetArtwork(val artwork: Artwork) : OnboardingNoteAction()
class SetArtworkUrl(val artworkUrl: String) : OnboardingNoteAction()
data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction()
object DetermineStepOfScreen : OnboardingNoteAction()
object Done : OnboardingNoteAction()
@ -27,6 +26,7 @@ sealed class OnboardingNoteAction : Action {
sealed class Balance {
object Update : OnboardingNoteAction()
data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction()
data class SetCriticalError(val error: TapError?) : OnboardingNoteAction()
data class SetNonCriticalError(val error: TapError?) : OnboardingNoteAction()
}

View file

@ -9,6 +9,7 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
@ -50,8 +51,8 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
when (action) {
is OnboardingNoteAction.LoadCardArtwork -> {
scope.launch {
val artwork = onboardingManager.loadArtwork()
withMainContext { store.dispatch(OnboardingNoteAction.SetArtwork(artwork)) }
val artworkUrl = onboardingManager.loadArtworkUrl()
withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artworkUrl)) }
}
}
is OnboardingNoteAction.DetermineStepOfScreen -> {
@ -69,7 +70,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
}
OnboardingNoteStep.Done -> {
onboardingManager.activationFinished(card.cardId)
postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) }
postUi(DELAY_SDK_DIALOG_CLOSE) { store.dispatch(OnboardingNoteAction.Confetti.Show) }
}
}
}
@ -117,10 +118,11 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
scope.launch {
val loadedBalance = onboardingManager.updateBalance(walletManager)
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
delay(if (isLoadedBefore) 0 else 300)
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
withMainContext {
store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance))
store.dispatch(OnboardingNoteAction.Balance.SetCriticalError(loadedBalance.criticalError))
store.dispatch(OnboardingNoteAction.Balance.SetNonCriticalError(loadedBalance.error))
}
}
@ -148,7 +150,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
store.dispatch(GlobalAction.Onboarding.Stop)
scope.launch {
store.onCardScanned(scanResponse)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
withMainContext { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) }
}
}
}

View file

@ -17,8 +17,8 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
is GlobalAction.Onboarding.Start -> {
state = OnboardingNoteState()
}
is OnboardingNoteAction.SetArtwork -> {
state = state.copy(cardArtwork = action.artwork)
is OnboardingNoteAction.SetArtworkUrl -> {
state = state.copy(cardArtworkUrl = action.artworkUrl)
}
is OnboardingNoteAction.SetWalletManager -> {
state = state.copy(walletManager = action.walletManager)
@ -26,6 +26,9 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
is OnboardingNoteAction.Balance.Set -> {
state = state.copy(walletBalance = action.balance)
}
is OnboardingNoteAction.Balance.SetCriticalError -> {
state = state.copy(balanceCriticalError = action.error)
}
is OnboardingNoteAction.Balance.SetNonCriticalError -> {
state = state.copy(balanceNonCriticalError = action.error)
}

View file

@ -3,7 +3,6 @@ package com.tangem.tap.features.onboarding.products.note.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.features.wallet.redux.Artwork
import org.rekotlin.StateType
/**
@ -12,9 +11,10 @@ import org.rekotlin.StateType
data class OnboardingNoteState(
val walletManager: WalletManager? = null,
// UI
val cardArtwork: Artwork? = null,
val cardArtworkUrl: String? = null,
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
val balanceNonCriticalError: TapError? = null,
val balanceCriticalError: TapError? = null,
val currentStep: OnboardingNoteStep = OnboardingNoteStep.None,
val steps: List<OnboardingNoteStep> = OnboardingNoteStep.values().toList(),
val showConfetti: Boolean = false,

View file

@ -7,12 +7,12 @@ import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import com.squareup.picasso.Picasso
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep
@ -60,7 +60,12 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
if (activity == null) return
if (state.currentStep == OnboardingOtherCardsStep.None) return
state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) }
Picasso.get()
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_front_card)
pb_state.max = state.steps.size - 1
pb_state.progress = state.progress

View file

@ -1,6 +1,5 @@
package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.tap.features.wallet.redux.Artwork
import org.rekotlin.Action
/**
@ -12,7 +11,7 @@ sealed class OnboardingOtherCardsAction : Action {
object CreateWallet : OnboardingOtherCardsAction()
// from redux
class SetArtwork(val artwork: Artwork) : OnboardingOtherCardsAction()
class SetArtworkUrl(val artworkUrl: String) : OnboardingOtherCardsAction()
object DetermineStepOfScreen : OnboardingOtherCardsAction()
object Done : OnboardingOtherCardsAction()

View file

@ -44,8 +44,8 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
when (action) {
is OnboardingOtherCardsAction.LoadCardArtwork -> {
scope.launch {
val artwork = onboardingManager.loadArtwork()
withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtwork(artwork)) }
val artworkUrl = onboardingManager.loadArtworkUrl()
withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artworkUrl)) }
}
}
is OnboardingOtherCardsAction.DetermineStepOfScreen -> {
@ -58,7 +58,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
is OnboardingOtherCardsAction.SetStepOfScreen -> {
if (action.step == OnboardingOtherCardsStep.Done) {
onboardingManager.activationFinished(card.cardId)
postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) }
postUi(200) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) }
}
}
is OnboardingOtherCardsAction.CreateWallet -> {
@ -85,7 +85,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
store.dispatch(GlobalAction.Onboarding.Stop)
scope.launch {
store.onCardScanned(onboardingManager.scanResponse)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
withMainContext { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) }
}
}
}

View file

@ -17,8 +17,8 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingOtherC
is GlobalAction.Onboarding.Start -> {
state = OnboardingOtherCardsState()
}
is OnboardingOtherCardsAction.SetArtwork -> {
state = state.copy(cardArtwork = action.artwork)
is OnboardingOtherCardsAction.SetArtworkUrl -> {
state = state.copy(cardArtworkUrl = action.artworkUrl)
}
is OnboardingOtherCardsAction.SetStepOfScreen -> {
if (action.step != state.currentStep && state.steps.contains(action.step)) {

View file

@ -1,6 +1,5 @@
package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.tap.features.wallet.redux.Artwork
import org.rekotlin.StateType
/**
@ -8,7 +7,7 @@ import org.rekotlin.StateType
*/
data class OnboardingOtherCardsState(
// UI
val cardArtwork: Artwork? = null,
val cardArtworkUrl: String? = null,
val showConfetti: Boolean = false,
val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None,
val steps: List<OnboardingOtherCardsStep> = OnboardingOtherCardsStep.values().toList(),

View file

@ -4,6 +4,7 @@ import com.tangem.Message
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardsManager
@ -55,6 +56,8 @@ sealed class TwinCardsAction : Action {
sealed class Balance {
object Update : TwinCardsAction()
data class Set(val balance: OnboardingWalletBalance) : TwinCardsAction()
data class SetCriticalError(val error: TapError?) : TwinCardsAction()
data class SetNonCriticalError(val error: TapError?) : TwinCardsAction()
}
sealed class Confetti {

View file

@ -212,7 +212,11 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
val loadedBalance = onboardingManager?.updateBalance(walletManager) ?: return@launch
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
delay(if (isLoadedBefore) 0 else 300)
withMainContext { store.dispatch(TwinCardsAction.Balance.Set(loadedBalance)) }
withMainContext {
store.dispatch(TwinCardsAction.Balance.Set(loadedBalance))
store.dispatch(TwinCardsAction.Balance.SetCriticalError(loadedBalance.criticalError))
store.dispatch(TwinCardsAction.Balance.SetNonCriticalError(loadedBalance.error))
}
}
}

View file

@ -29,7 +29,10 @@ private fun internalReduce(action: Action, state: AppState): TwinCardsState {
}
}
is TwinCardsAction.SetMode -> {
state = state.copy(mode = action.mode)
state = state.copy(
mode = action.mode,
userWasUnderstandIfWalletRecreate = false,
)
}
is TwinCardsAction.SetStepOfScreen -> {
state = state.copy(currentStep = action.step)
@ -52,6 +55,12 @@ private fun internalReduce(action: Action, state: AppState): TwinCardsState {
is TwinCardsAction.Balance.Set -> {
state = state.copy(walletBalance = action.balance)
}
is TwinCardsAction.Balance.SetCriticalError -> {
state = state.copy(balanceCriticalError = action.error)
}
is TwinCardsAction.Balance.SetNonCriticalError -> {
state = state.copy(balanceNonCriticalError = action.error)
}
is TwinCardsAction.Confetti.Show -> {
state = state.copy(showConfetti = true)
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
@ -22,6 +23,8 @@ data class TwinCardsState(
val pairCardId: String? = null, // available after create\recreate wallets
val walletManager: WalletManager? = null,
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
val balanceNonCriticalError: TapError? = null,
val balanceCriticalError: TapError? = null,
val showConfetti: Boolean = false,
) : StateType {

View file

@ -77,18 +77,20 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
addBackPressHandler(this)
reconfigureLayoutForTwins()
twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 250f }
twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 285f }
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
toolbar.title = getText(R.string.twins_recreate_toolbar)
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_twin_front_card)
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.error(R.drawable.card_placeholder_white)
.placeholder(R.drawable.card_placeholder_white)
?.into(imv_twin_back_card)
}
@ -114,11 +116,6 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
override fun newState(state: TwinCardsState) {
if (activity == null) return
// toolbar.title = when (state.mode) {
// CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet)
// CreateTwinWalletMode.RecreateWallet -> getText(R.string.twins_recreate_toolbar)
// }
pb_state.max = state.steps.size - 1
pb_state.progress = state.progress
@ -141,9 +138,13 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
private fun setBalance(state: TwinCardsState) {
if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
val balanceValue = state.walletBalance.value.stripZeroPlainString()
val currency = state.walletBalance.currency.currencySymbol
tv_balance_value.text = "$balanceValue $currency"
if (state.balanceCriticalError == null) {
val balanceValue = state.walletBalance.value.stripZeroPlainString()
val currency = state.walletBalance.currency.currencySymbol
tv_balance_value.text = "$balanceValue $currency"
} else {
tv_balance_value.text = ""
}
}
private fun setupWelcomeOnlyState(state: TwinCardsState) {
@ -287,6 +288,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
store.dispatch(TwinCardsAction.TopUp)
}
btn_alternative_action.isVisible = true
btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address)
btn_alternative_action.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)

View file

@ -147,7 +147,7 @@ class WalletMiddleware {
WalletAction.LoadArtwork(action.card, response.data.artwork?.id),
GlobalAction.SetIfCardVerifiedOnline(response.data.passed)
)
actionList.forEach { store.dispatch(it) }
withMainContext { actionList.forEach { store.dispatch(it) } }
}
is Result.Failure -> {
store.dispatchOnMain(GlobalAction.SetIfCardVerifiedOnline(false))

View file

@ -0,0 +1,14 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="40dp"
android:height="40dp" />
<gradient
android:angle="270"
android:endColor="#47F8F8F8"
android:startColor="#F4F4F4"
android:type="linear" />
</shape>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="502dp">
@ -68,10 +69,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:fontFamily="sans-serif-medium"
android:maxWidth="170dp"
android:minWidth="120dp"
android:singleLine="true"
android:text="addlkasLNXDLKNnkjkjhnjkjnljnnlsenn" />
android:textSize="15sp"
tools:text="addlkasLNXDLKNnkjkjhnjkjnljnnlsenn" />
<View
android:layout_width="16dp"

View file

@ -36,7 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="64dp"
android:layout_marginTop="10dp"
android:layout_marginTop="14dp"
android:layout_marginEnd="64dp"
android:minLines="3"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"

View file

@ -3,7 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/onboarding_main_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:clipChildren="false">
<include layout="@layout/view_bg_home" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_back_card"

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:translationY="-50dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bg_circle_large"
android:layout_width="670dp"
android:layout_height="670dp"
android:src="@drawable/shape_circle_home_screen"
android:translationX="160dp"
android:translationY="-175dp" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bg_circle_medium"
android:layout_width="650dp"
android:layout_height="650dp"
android:src="@drawable/shape_circle"
android:tint="@color/backgroundWhite"
android:translationX="170dp"
android:translationY="-165dp" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bg_circle_small"
android:layout_width="590dp"
android:layout_height="590dp"
android:src="@drawable/shape_circle_home_screen"
android:translationX="192dp"
android:translationY="-132dp" />
</FrameLayout>

View file

@ -6,8 +6,8 @@
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:progressBackgroundTint="#401ACE80"
android:progressTint="@color/accent" />
</merge>

View file

@ -4,7 +4,6 @@
android:id="@+id/btn_refresh_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true">

View file

@ -14,15 +14,21 @@
android:layout_height="wrap_content"
android:elevation="0dp"
android:fontFamily="sans-serif-medium"
android:letterSpacing="0.036"
android:text="@string/onboarding_balance_title"
android:textAllCaps="true"
android:textColor="#ABABAB"
android:textSize="14sp" />
<View
android:layout_width="wrap_content"
android:layout_height="4dp" />
<TextView
android:id="@+id/tv_balance_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="0.036"
android:textSize="28sp"
android:textStyle="bold"
tools:text="0.000 BTC" />

View file

@ -14,7 +14,7 @@
<string name="twins_onboarding_title" translatable="false">Tangem Twin</string>
<string name="twins_onboarding_subtitle" translatable="false">One wallet. Two cards.</string>
<string name="twins_onboarding_description_format" translatable="false">This one that you are holding in your handsand the other one with number #%s.</string>
<string name="twins_onboarding_description_format" translatable="false">This one that you are holding in your hands and the other one with number #%s.</string>
<string name="wallet_twins_chip_format" translatable="false">Card %s of 2</string>
@ -194,5 +194,7 @@
<string name="onboarding_twins_interrupt_warning" translatable="false">If the process of re-creating the wallet gets interrupted in any way, youll have to start over.</string>
<string name="onboarding_error_create_primary_wallet" translatable="false">Internal error: can\'t create wallet manager</string>
<string name="wallet_balance_blockchain_unreachable_try_later" translatable="false">Blockchain is unreachable. Try later</string>
</resources>

View file

@ -90,12 +90,14 @@
<style name="TextViewOnboarding.Header">
<item name="android:textColor">#060606</item>
<item name="android:textSize">24sp</item>
<item name="android:lineSpacingExtra">4sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
</style>
<style name="TextViewOnboarding.Body">
<item name="android:textColor">#686868</item>
<item name="android:textSize">16sp</item>
<item name="android:lineSpacingExtra">4sp</item>
</style>
</resources>