Updated on 2026-08-14

This commit is contained in:
Tangem 2021-10-27 23:21:34 +03:00
parent c02e5b93f6
commit 6dcce60fb4
80 changed files with 1946 additions and 1738 deletions

View file

@ -13,6 +13,8 @@ import com.tangem.tap.common.DialogManager
import com.tangem.tap.common.IntentHandler
import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.redux.NotificationsHandler
import com.tangem.tap.common.redux.global.AndroidResources
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.TangemSdkManager
@ -66,9 +68,19 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
tangemSdk = TangemSdk.init(this, TangemSdkManager.config)
tangemSdkManager = TangemSdkManager(tangemSdk, this)
store.dispatch(GlobalAction.SetResources(getAndroidResources()))
store.dispatch(WalletConnectAction.RestoreSessions)
}
private fun getAndroidResources(): AndroidResources {
return AndroidResources(
AndroidResources.RString(
R.string.copy_toast_msg,
R.string.details_notification_erase_wallet_not_possible
)
)
}
private fun systemActions() {
// makes the status bar text dark
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

View file

@ -7,8 +7,8 @@ import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.features.twins.ui.dialog.CreateWalletInterruptDialog
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.store
import com.tangem.wallet.R
@ -45,7 +45,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
dialog = when (state.dialog) {
is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is TwinCardsAction.Wallet.InterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context)
is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context)
is WalletConnectDialog.UnsupportedCard ->
SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,

View file

@ -16,12 +16,10 @@ import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
import com.tangem.tap.features.home.HomeFragment
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.OnboardingTwinsFragment
import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment
import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.tokens.ui.AddTokensFragment
import com.tangem.tap.features.twins.ui.CreateTwinWalletFragment
import com.tangem.tap.features.twins.ui.TwinWalletWarningFragment
import com.tangem.tap.features.wallet.ui.WalletDetailsFragment
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.wallet.R
@ -39,7 +37,7 @@ fun FragmentActivity.openFragment(
transaction.setReorderingAllowed(true)
shareElements.forEach { shareElement ->
shareElement.wView.get()?.let { view ->
transaction.addSharedElement(view, shareElement.name)
transaction.addSharedElement(view, shareElement.elementName)
}
}
}
@ -70,10 +68,11 @@ fun FragmentActivity.addOnBackPressedDispatcher(
private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
// AppScreen.Home -> TestLeapfrogFragment()
AppScreen.Home -> HomeFragment()
AppScreen.OnboardingNote -> OnboardingNoteFragment()
AppScreen.OnboardingWallet -> OnboardingWalletFragment()
AppScreen.OnboardingTwins -> OnboardingTwinsFragment()
AppScreen.OnboardingTwins -> TwinsCardsFragment()
AppScreen.OnboardingOther -> OnboardingOtherCardsFragment()
AppScreen.Wallet -> WalletFragment()
AppScreen.Send -> SendFragment()
@ -81,8 +80,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
AppScreen.DetailsSecurity -> DetailsSecurityFragment()
AppScreen.Disclaimer -> DisclaimerFragment()
AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment()
AppScreen.CreateTwinWallet -> CreateTwinWalletFragment()
AppScreen.AddTokens -> AddTokensFragment()
AppScreen.WalletDetails -> WalletDetailsFragment()
AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment()

View file

@ -6,6 +6,7 @@ import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.FrameLayout
import androidx.core.animation.doOnEnd
import androidx.core.view.children
import com.tangem.common.extensions.VoidCallback
import com.tangem.tangem_sdk_new.extensions.dpToPx
/**
@ -15,9 +16,9 @@ class LeapfrogWidget(
private val parentContainer: FrameLayout,
private val calculator: PropertyCalculator = PropertyCalculator(),
) {
private val foldAnimationDuration = 500L
private val leapAnimationDuration = 800L
private val leapBackAnimationDuration = 800L
private val foldAnimationDuration = 300L
private val leapAnimationDuration = 500L
private val leapBackAnimationDuration = 500L
private val leapViews = mutableListOf<LeapView>()
@ -25,9 +26,6 @@ class LeapfrogWidget(
private var leapProgress: Int = 100
private var leapBackProgress: Int = 100
var isFolded: Boolean = true
private set
init {
calculator.setTranslationConverter { parentContainer.dpToPx(it) }
if (viewIsFullFledged()) {
@ -43,12 +41,11 @@ class LeapfrogWidget(
* Apply initial properties to the views
*/
fun initViews() {
isFolded = true
leapViews.forEach { it.initView() }
}
fun fold(onEnd: () -> Unit = {}) {
val animator = foldAnimator()
fun fold(animate: Boolean = true, onEnd: VoidCallback = {}) {
val animator = foldAnimator(animate)
if (animator == null) {
onEnd()
} else {
@ -57,8 +54,8 @@ class LeapfrogWidget(
}
}
fun unfold(onEnd: () -> Unit = {}) {
val animator = unfoldAnimator()
fun unfold(animate: Boolean = true, onEnd: VoidCallback = {}) {
val animator = unfoldAnimator(animate)
if (animator == null) {
onEnd()
} else {
@ -67,33 +64,31 @@ class LeapfrogWidget(
}
}
fun foldAnimator(): Animator? {
if (!canFoldUnfold() || isFolded) return null
fun foldAnimator(animate: Boolean = true): Animator? {
if (!canFoldUnfold()) return null
return createFoldUnfoldAnimator(true, foldAnimationDuration)
return createFoldUnfoldAnimator(true, animate, foldAnimationDuration)
}
fun unfoldAnimator(): Animator? {
if (!canFoldUnfold() || !isFolded) return null
fun unfoldAnimator(animate: Boolean = true): Animator? {
if (!canFoldUnfold()) return null
return createFoldUnfoldAnimator(false, foldAnimationDuration)
return createFoldUnfoldAnimator(false, animate, foldAnimationDuration)
}
private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator {
private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, animate: Boolean, duration: Long): Animator {
val animatorsList = mutableListOf<Animator>()
val animateDuration = if (animate) duration else 0
leapViews.forEach {
if (isFoldAnimation) {
it.fold()
animatorsList.add(it.view.foldAnimation(duration, it.state.properties))
animatorsList.add(it.view.foldAnimation(animateDuration, it.state.properties))
} else {
it.unfold()
animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties))
animatorsList.add(it.view.unfoldAnimation(animateDuration, it.state.properties))
}
}
animatorsList.add(createProgressListener(duration) {
foldUnfoldProgress = it
if (it == 100) isFolded = isFoldAnimation
})
animatorsList.add(createProgressListener(animateDuration) { foldUnfoldProgress = it })
val animator = AnimatorSet()
animator.interpolator = AccelerateDecelerateInterpolator()
@ -101,13 +96,13 @@ class LeapfrogWidget(
return animator
}
fun leap(onEnd: () -> Unit = {}) {
fun leap(animate: Boolean = true, onEnd: VoidCallback = {}) {
if (!canLeap()) {
onEnd()
return
}
val duration = leapAnimationDuration
val duration = if (animate) leapAnimationDuration else 0
val animatorsList = mutableListOf<Animator>()
leapViews.forEach {
when (it.leap()) {
@ -128,13 +123,13 @@ class LeapfrogWidget(
animator.start()
}
fun leapBack(onEnd: () -> Unit = {}) {
fun leapBack(animate: Boolean = true, onEnd: VoidCallback = {}) {
if (!canLeapBack()) {
onEnd()
return
}
val duration = leapBackAnimationDuration
val duration = if (animate) leapBackAnimationDuration else 0
val animatorsList = mutableListOf<Animator>()
leapViews.forEach {
when (it.leapBack()) {
@ -160,12 +155,11 @@ class LeapfrogWidget(
fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position }
fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.state })
fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(leapViews.map { it.state })
fun applyState(state: LeapfrogWidgetState) {
isFolded = state.isFolded
state.leapViewStates.forEach { state ->
leapViews.firstOrNull { it.index == state.index }?.applyState(state)
state.leapViewStates.forEach { viewState ->
leapViews.firstOrNull { it.index == viewState.index }?.applyState(viewState)
}
}
@ -183,7 +177,6 @@ class LeapfrogWidget(
private fun canLeap(): Boolean {
return when {
!viewIsFullFledged() -> false
isFolded -> false
leapBackInProgress() -> false
leapProgress < 70 -> false
else -> true
@ -193,7 +186,6 @@ class LeapfrogWidget(
private fun canLeapBack(): Boolean {
return when {
!viewIsFullFledged() -> false
isFolded -> false
leapInProgress() -> false
leapBackProgress < 70 -> false
else -> true
@ -206,14 +198,13 @@ class LeapfrogWidget(
}
data class LeapfrogWidgetState(
val isFolded: Boolean,
val leapViewStates: List<LeapViewState>
)
class PropertyCalculator(
val elevationFactor: Float = 1f,
val decreaseScaleFactor: Float = 0.1f,
val yTranslationFactor: Float = 20f,
val decreaseScaleFactor: Float = 0.15f,
val yTranslationFactor: Float = 35f,
val decreaseOverLiftingFactor: Float = 0.85f,
) {
// 0 view it is view on the top of stack, but it is last in parent.children

View file

@ -8,10 +8,10 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer
import com.tangem.tap.features.home.redux.HomeReducer
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsReducer
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer
import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
import com.tangem.tap.features.tokens.redux.TokensReducer
import com.tangem.tap.features.twins.redux.TwinCardsReducer
import com.tangem.tap.features.wallet.redux.reducers.WalletReducer
import org.rekotlin.Action
@ -20,19 +20,19 @@ fun appReducer(action: Action, state: AppState?): AppState {
if (action is AppAction.RestoreState) return action.state
return AppState(
navigationState = NavigationReducer.reduce(action, state),
globalState = globalReducer(action, state),
homeState = HomeReducer.reduce(action, state),
onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
walletState = WalletReducer.reduce(action, state),
twinCardsState = TwinCardsReducer.reduce(action, state),
sendState = SendScreenReducer.reduce(action, state.sendState),
detailsState = DetailsReducer.reduce(action, state),
disclaimerState = DisclaimerReducer.reduce(action, state),
tokensState = TokensReducer.reduce(action, state),
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState)
navigationState = NavigationReducer.reduce(action, state),
globalState = globalReducer(action, state),
homeState = HomeReducer.reduce(action, state),
onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
walletState = WalletReducer.reduce(action, state),
twinCardsState = TwinCardsReducer.reduce(action, state),
sendState = SendScreenReducer.reduce(action, state.sendState),
detailsState = DetailsReducer.reduce(action, state),
disclaimerState = DisclaimerReducer.reduce(action, state),
tokensState = TokensReducer.reduce(action, state),
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState)
)
}

View file

@ -16,14 +16,14 @@ import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMidd
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsMiddleware
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
import com.tangem.tap.features.send.redux.middlewares.SendMiddleware
import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.tokens.redux.TokensMiddleware
import com.tangem.tap.features.tokens.redux.TokensState
import com.tangem.tap.features.twins.redux.TwinCardsMiddleware
import com.tangem.tap.features.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware
import org.rekotlin.Middleware

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.features.wallet.redux.AddressData
/**
@ -11,7 +12,7 @@ sealed class AppDialog : StateDialog {
object ScanFailsDialog : AppDialog()
data class AddressInfoDialog(
val addressData: AddressData,
val onCopyAddress: () -> Unit,
val onExploreAddress: () -> Unit
val onCopyAddress: VoidCallback,
val onExploreAddress: VoidCallback
) : AppDialog()
}

View file

@ -0,0 +1,35 @@
package com.tangem.tap.common.redux.navigation
import android.view.View
import androidx.transition.TransitionSet
import java.lang.ref.WeakReference
/**
[REDACTED_AUTHOR]
*/
data class FragmentShareTransition(
val shareElements: List<ShareElement>,
val enterTransitionSet: TransitionSet,
val exitTransitionSet: TransitionSet
)
/**
* For ease of use, the name is used as transitionName\name into the FragmentTransaction.addSharedElement
*/
class ShareElement(view: View, name: String? = null) {
val wView: WeakReference<View>
val elementName: String
init {
name?.let { view.transitionName = it }
elementName = view.transitionName
?: throw UnsupportedOperationException("ShareElement require the name")
wView = WeakReference(view)
}
companion object {
val imvFrontCard = "imv_front_card"
val imvBackCard = "imv_back_card"
}
}

View file

@ -1,8 +1,6 @@
package com.tangem.tap.common.redux.navigation
import android.view.View
import androidx.fragment.app.FragmentActivity
import androidx.transition.TransitionSet
import org.rekotlin.Action
import java.lang.ref.WeakReference
@ -19,12 +17,4 @@ sealed class NavigationAction : Action {
data class ActivityCreated(val activity: WeakReference<FragmentActivity>) : NavigationAction()
object ActivityDestroyed : NavigationAction()
}
data class FragmentShareTransition(
val shareElements: List<ShareElement>,
val enterTransitionSet: TransitionSet,
val exitTransitionSet: TransitionSet
)
data class ShareElement(val wView: WeakReference<View>, val name: String)
}

View file

@ -5,8 +5,8 @@ import org.rekotlin.StateType
import java.lang.ref.WeakReference
data class NavigationState(
val backStack: List<AppScreen> = listOf(AppScreen.Home),
val activity: WeakReference<FragmentActivity>? = null
val backStack: List<AppScreen> = listOf(AppScreen.Home),
val activity: WeakReference<FragmentActivity>? = null
) : StateType
enum class AppScreen {
@ -16,7 +16,6 @@ enum class AppScreen {
Wallet, WalletDetails,
Send,
Details, DetailsConfirm, DetailsSecurity,
CreateTwinWalletWarning, CreateTwinWallet,
AddTokens,
WalletConnectSessions,
QrScan

View file

@ -14,6 +14,7 @@ data class Config(
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
val isSendingToPayIdEnabled: Boolean = true,
val isTopUpEnabled: Boolean = false,
@Deprecated("Not relevant since version 3.23")
val isCreatingTwinCardsAllowed: Boolean = false
)
@ -50,10 +51,10 @@ class ConfigManager(
fun resetToDefault(name: String) {
when (name) {
isSendingToPayIdEnabled -> config =
config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
isCreatingTwinCardsAllowed -> config =
config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
}
}

View file

@ -1,124 +0,0 @@
package com.tangem.tap.domain.tasks
import com.tangem.*
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemError
import com.tangem.common.extensions.toHexString
import com.tangem.operations.ScanTask
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapSdkError
import com.tangem.tap.domain.TapWorkarounds.isExcluded
import com.tangem.tap.domain.TapWorkarounds.isTangemNote
import com.tangem.tap.domain.extensions.getSingleWallet
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.TwinsHelper
@Deprecated("Use ScanProductTask instead")
class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanResponse> {
override fun run(
session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
ScanTask().run(session) { result ->
when (result) {
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
is CompletionResult.Success -> {
val card = this.card ?: result.data
val error = getErrorIfExcludedCard(card)
if (error != null) {
callback(CompletionResult.Failure(error))
return@run
}
if (TwinsHelper.getTwinCardNumber(card.cardId) != null) {
dealWithTwinCard(card, session, callback)
} else if (!isTangemNote(card) && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) {
createMissingWalletsIfNeeded(card, session, callback)
} else {
callback(CompletionResult.Success(
ScanResponse(card, ProductType.Other, session.environment.walletData)))
}
}
}
}
}
private fun createMissingWalletsIfNeeded(
card: Card, session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
val walletData = session.environment.walletData
if (card.wallets.isEmpty()) {
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
return
}
val curvesPresent = card.wallets.map { it.curve }
// val curvesToCreate = EllipticCurve.values().subtract(curvesPresent)
val curvesToCreate = card.supportedCurves.subtract(curvesPresent)
if (curvesToCreate.isEmpty()) {
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
return
}
CreateWalletsTask(curvesToCreate.toList()).run(session) { result ->
when (result) {
is CompletionResult.Success ->
callback(CompletionResult.Success(ScanResponse(result.data, ProductType.Other, walletData)))
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
}
private fun dealWithTwinCard(
card: Card, session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
ReadIssuerDataCommand().run(session) { readDataResult ->
when (readDataResult) {
is CompletionResult.Success -> {
val publicKey = card.getSingleWallet()?.publicKey
if (publicKey == null) {
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
return@run
}
val verified = TwinsHelper.verifyTwinPublicKey(
readDataResult.data.issuerData, publicKey
)
if (verified) {
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
callback(CompletionResult.Success(
ScanResponse(
card = card,
ProductType.Other,
walletData = session.environment.walletData,
secondTwinPublicKey = twinPublicKey.toHexString())
))
return@run
} else {
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
}
}
is CompletionResult.Failure ->
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
}
}
}
private fun getErrorIfExcludedCard(card: Card): TangemError? {
if (card.isExcluded()) return TapSdkError.CardForDifferentApp
// Disable new multi-currency HD wallet cards on the old version of the app
// if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard()
return null
}
}

View file

@ -4,44 +4,45 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Types
import com.tangem.Message
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.CompletionResult
import com.tangem.common.KeyPair
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.network.createMoshi
import com.tangem.tap.tangemSdkManager
class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: AssetReader) {
class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
private val currentCardId: String = scanResponse.card.cardId
private val currentCardId: String = card.cardId
private var currentCardPublicKey: String? = null
private var secondCardPublicKey: String? = null
private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, scanResponse.card.issuer.publicKey.toHexString())
private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString())
suspend fun createFirstWallet(message: Message): SimpleResult {
suspend fun createFirstWallet(message: Message): CompletionResult<CreateWalletResponse> {
val response = tangemSdkManager.runTaskAsync(
CreateFirstTwinWalletTask(), currentCardId, message
)
when (response) {
is CompletionResult.Success -> {
currentCardPublicKey = response.data.wallet.publicKey.toHexString()
return SimpleResult.Success
return response
}
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
card = scanResponse.card
card = card
)
}
return SimpleResult.failure(response.error)
return response
}
}
@ -52,7 +53,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse
initialMessage: Message,
preparingMessage: Message,
creatingWalletMessage: Message,
): SimpleResult {
): CompletionResult<CreateWalletResponse> {
val task = CreateSecondTwinWalletTask(
firstPublicKey = currentCardPublicKey!!,
firstCardId = currentCardId,
@ -64,20 +65,18 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse
when (response) {
is CompletionResult.Success -> {
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
return SimpleResult.Success
}
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
card = scanResponse.card
card = card
)
}
return SimpleResult.failure(response.error)
}
}
return response
}
suspend fun complete(message: Message): Result<ScanResponse> {
@ -92,7 +91,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData,
card = scanResponse.card
card = card
)
}
Result.failure(response.error)

View file

@ -5,6 +5,7 @@ import android.animation.ObjectAnimator
import android.animation.PropertyValuesHolder
import android.view.View
import androidx.core.animation.doOnEnd
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.leapfrogWidget.LeapView
import com.tangem.tap.common.leapfrogWidget.LeapViewState
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
@ -17,47 +18,60 @@ class TwinsCardWidget(
val getTopOfAnchorViewForActivateState: () -> Float
) {
var state: TwinCardsWidgetState? = null
private set
private val animationDuration = 300L
init {
if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException()
}
fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) {
val animator = createAnimator(animate, onEnd)
fun toWelcome(animate: Boolean = true, onEnd: VoidCallback = {}) {
if (state == TwinCardsWidgetState.Welcome) return
state = TwinCardsWidgetState.Welcome
val animator = createAnimator(animate)
animator.playTogether(
createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)),
createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second))
)
leapfrogWidget.fold { animator.start() }
animator.doOnEnd { onEnd() }
leapfrogWidget.fold(animate) { animator.start() }
}
fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) {
val animator = createAnimator(animate, onEnd)
fun toLeapfrog(animate: Boolean = true, onEnd: VoidCallback = {}) {
if (state == TwinCardsWidgetState.Leapfrog) return
state = TwinCardsWidgetState.Leapfrog
val animator = createAnimator(animate)
animator.playTogether(
createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)),
createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second))
)
leapfrogWidget.fold {
animator.doOnEnd {
leapfrogWidget.initViews()
leapfrogWidget.unfold()
}
animator.start()
animator.doOnEnd {
leapfrogWidget.initViews()
leapfrogWidget.unfold(animate, onEnd)
}
animator.start()
}
fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) {
val animator = createAnimator(animate, onEnd)
fun toActivate(animate: Boolean = true, onEnd: VoidCallback = {}) {
if (state == TwinCardsWidgetState.Activate) return
state = TwinCardsWidgetState.Activate
val animator = createAnimator(animate)
animator.playTogether(
createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)),
createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second))
)
animator.doOnEnd { onEnd() }
animator.start()
}
private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet {
private fun createAnimator(animate: Boolean): AnimatorSet {
return AnimatorSet().apply {
duration = if (animate) 400 else 0
doOnEnd { onEnd() }
duration = if (animate) animationDuration else 0
}
}
@ -72,20 +86,20 @@ class TwinsCardWidget(
return when (cardNumber) {
TwinCardNumber.First -> {
TwinsCardProperties(
xTranslation = -180f,
yTranslation = -350f,
xTranslation = -120f,
yTranslation = -220f,
rotation = -3f,
elevation = 1f,
scale = 1.1f,
scale = 1f,
)
}
TwinCardNumber.Second -> {
TwinsCardProperties(
xTranslation = 180f,
yTranslation = 10f,
xTranslation = 170f,
yTranslation = 170f,
rotation = -3f,
elevation = 0f,
scale = 1.1f,
scale = 1f,
)
}
}
@ -108,7 +122,7 @@ class TwinsCardWidget(
xTranslation = twinProperties.xTranslation,
yTranslation = twinProperties.yTranslation - topOfAnchorView,
rotation = 0f,
scale = twinProperties.scale - 0.4f,
scale = twinProperties.scale - 0.5f,
)
}
@ -120,6 +134,10 @@ class TwinsCardWidget(
}
}
enum class TwinCardsWidgetState {
Welcome, Leapfrog, Activate
}
private data class TwinsCardProperties(
val xTranslation: Float = 0f,
val yTranslation: Float = 0f,

View file

@ -1,26 +1,26 @@
package com.tangem.tap.features.send
package com.tangem.tap.features
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_wallet.*
import org.rekotlin.StoreSubscriber
/**
[REDACTED_AUTHOR]
*/
abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
abstract fun subscribeToStore()
abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackPressedHandler {
protected lateinit var mainView: View
protected val storeSubscribersList = mutableListOf<StoreSubscriber<*>>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -40,26 +40,32 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar?.setNavigationOnClickListener { activity?.onBackPressed() }
addBackPressHandler(this)
}
override fun onStart() {
super.onStart()
subscribeToStore()
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo())
}
override fun onStop() {
storeSubscribersList.forEach { store.unsubscribe(it) }
storeSubscribersList.clear()
super.onStop()
}
fun showRetrySnackbar(message: String, action: () -> Unit) {
fun showRetrySnackbar(message: String, action: VoidCallback) {
val snackbar = Snackbar.make(mainView, message, Snackbar.LENGTH_INDEFINITE)
snackbar.setAction(getString(R.string.common_retry)) {
snackbar.dismiss()
action()
}.show()
}
}
interface FragmentOnBackPressedHandler {
fun handleOnBackPressed()
}
@SuppressLint("FragmentBackPressedCallback")
fun Fragment.addBackPressHandler(handler: FragmentOnBackPressedHandler) {
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
handler.handleOnBackPressed()
}
})
view?.findViewById<Toolbar>(R.id.toolbar)?.setNavigationOnClickListener { activity?.onBackPressed() }
}

View file

@ -0,0 +1,24 @@
package com.tangem.tap.features
import com.tangem.tap.store
import org.rekotlin.StoreSubscriber
/**
[REDACTED_AUTHOR]
*/
abstract class BaseStoreFragment(layoutId: Int) : BaseFragment(layoutId) {
abstract fun subscribeToStore()
protected val storeSubscribersList = mutableListOf<StoreSubscriber<*>>()
override fun onStart() {
super.onStart()
subscribeToStore()
}
override fun onStop() {
storeSubscribersList.forEach { store.unsubscribe(it) }
storeSubscribersList.clear()
super.onStop()
}
}

View file

@ -11,8 +11,8 @@ 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.features.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.onboarding.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService

View file

@ -6,7 +6,7 @@ import com.tangem.common.card.Card
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.features.onboarding.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.network.coinmarketcap.FiatCurrency
import com.tangem.tap.store
import org.rekotlin.StateType

View file

@ -6,7 +6,6 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
@ -16,8 +15,6 @@ import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.features.feedback.FeedbackEmail
import com.tangem.tap.features.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_details.*
@ -91,17 +88,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
}
if (state.isTangemTwins) {
if (state.twinCardsState.isCreatingTwinCardsAllowed) {
tv_erase_wallet.show()
tv_erase_wallet.text = getText(R.string.details_row_title_twins_recreate)
tv_erase_wallet.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.Create(
state.twinCardsState.cardNumber!!,
CreateTwinWalletMode.RecreateWallet
))
}
} else {
tv_erase_wallet.hide()
tv_erase_wallet.show()
tv_erase_wallet.text = getText(R.string.details_row_title_twins_recreate)
tv_erase_wallet.setOnClickListener {
store.dispatch(DetailsAction.ReCreateTwinsWallet(state.twinCardsState.cardNumber!!))
}
} else {
tv_erase_wallet.show()

View file

@ -16,15 +16,14 @@ import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.*
import kotlinx.android.synthetic.main.layout_onboarding_home.*
import java.lang.ref.WeakReference
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_home_top.*
class HomeFragment : BaseOnboardingFragment<HomeState>() {
private lateinit var btnScanCard: ViewStateWidget
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -38,7 +37,10 @@ class HomeFragment : BaseOnboardingFragment<HomeState>() {
toolbar.hide()
val shareTransition = FragmentShareTransition(
listOf(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)),
listOf(
ShareElement(imv_front_card, ShareElement.imvFrontCard),
ShareElement(imv_back_card, ShareElement.imvBackCard)
),
FrontCardEnterTransition(),
FrontCardExitTransition()
)
@ -62,6 +64,7 @@ class HomeFragment : BaseOnboardingFragment<HomeState>() {
oldState.homeState == newState.homeState
}.select { it.homeState }
}
storeSubscribersList.add(this)
}
override fun newState(state: HomeState) {

View file

@ -2,6 +2,8 @@ package com.tangem.tap.features.home.redux
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.post
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
@ -10,10 +12,10 @@ import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.domain.twins.getTwinCardNumber
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.send.redux.states.ButtonState
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
@ -58,21 +60,24 @@ private fun handleReadCard() {
} else {
changeButtonState(ButtonState.PROGRESS)
store.dispatch(GlobalAction.ScanCard({ scanResponse ->
if (scanResponse.isTangemTwins()) {
scanResponse.card.getTwinCardNumber()?.let {
val configManager = store.state.globalState.configManager
val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false
store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed))
}
}
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
store.dispatch(GlobalAction.Onboarding.Start(scanResponse))
navigateTo(navigateTo, store.state.homeState.shareTransition)
} else {
scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) }
navigateTo(AppScreen.Wallet, null)
scope.launch {
store.onCardScanned(scanResponse)
withMainContext {
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly))
navigateTo(AppScreen.OnboardingTwins, store.state.homeState.shareTransition)
} else {
navigateTo(AppScreen.Wallet, null)
}
}
}
}
}, {
changeButtonState(ButtonState.ENABLED)

View file

@ -5,7 +5,7 @@ import android.view.View
import com.tangem.tap.common.extensions.inflate
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.transitions.HomeToOnboardingTransition
import com.tangem.tap.features.send.BaseStoreFragment
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.view_confetti.*
@ -16,11 +16,6 @@ import org.rekotlin.StoreSubscriber
*/
abstract class BaseOnboardingFragment<T> : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber<T> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
configureTransitions()
}
override fun configureTransitions() {
enterTransition = HomeToOnboardingTransition()
exitTransition = HomeToOnboardingTransition()

View file

@ -16,18 +16,18 @@ import com.tangem.tangem_sdk_new.extensions.fadeOut
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteResources
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteStep
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.*
import kotlinx.android.synthetic.main.layout_onboarding_note.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_container_top.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
@ -36,23 +36,26 @@ import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
*/
class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None
private var previousStep: OnboardingNoteStep = OnboardingNoteStep.None
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imv_front_card.transitionName = ShareElement.imvFrontCard
startPostponedEnterTransition()
toolbar.setTitle(R.string.onboarding_title)
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
val resources = OnboardingNoteResources(
OnboardingNoteResources.Strings(
R.string.copy_toast_msg
)
)
store.dispatch(OnboardingNoteAction.SetResources(resources))
store.dispatch(OnboardingNoteAction.LoadCardArtwork)
store.dispatch(OnboardingNoteAction.DetermineStepOfScreen)
}
@ -81,7 +84,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
}
setBalance(state)
showConfetti(state.showConfetti)
currentStep = state.currentStep
previousStep = state.currentStep
}
private fun setBalance(state: OnboardingNoteState) {
@ -166,7 +169,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
}
private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) {
if (this.currentStep == currentStep) return
if (this.previousStep == currentStep) return
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)

View file

@ -10,7 +10,6 @@ import org.rekotlin.Action
*/
sealed class OnboardingNoteAction : Action {
// from user, ui
data class SetResources(val resources: OnboardingNoteResources) : OnboardingNoteAction()
object LoadCardArtwork : OnboardingNoteAction()
object CreateWallet : OnboardingNoteAction()
object TopUp : OnboardingNoteAction()

View file

@ -12,6 +12,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.scope
import com.tangem.tap.store
@ -107,6 +108,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading
val balanceIsLoading = noteState.walletBalance.copy(
currency = Currency.Blockchain(walletManager.wallet.blockchain),
state = ProgressState.Loading,
error = null,
criticalError = null
@ -128,7 +130,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
}
is OnboardingNoteAction.ShowAddressInfoDialog -> {
val addressData = noteState.walletManager?.getAddressData() ?: return
val addressWasCopied = noteState.resources.strings.addressWasCopied
val addressWasCopied = globalState.resources.strings.addressWasCopied
val appDialog = AppDialog.AddressInfoDialog(
addressData,
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
@ -143,7 +145,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
OnboardingNoteAction.Done -> {
store.dispatch(GlobalAction.Onboarding.Stop)
scope.launch {
globalState.tapWalletManager.onCardScanned(scanResponse)
store.onCardScanned(scanResponse)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
}

View file

@ -23,9 +23,6 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
is OnboardingNoteAction.SetWalletManager -> {
state = state.copy(walletManager = action.walletManager)
}
is OnboardingNoteAction.SetResources -> {
state = state.copy(resources = action.resources)
}
is OnboardingNoteAction.Balance.Set -> {
state = state.copy(walletBalance = action.balance)
}

View file

@ -10,7 +10,6 @@ import org.rekotlin.StateType
*/
data class OnboardingNoteState(
val walletManager: WalletManager? = null,
val resources: OnboardingNoteResources = OnboardingNoteResources(),
// UI
val cardArtwork: Artwork? = null,
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
@ -25,12 +24,4 @@ data class OnboardingNoteState(
enum class OnboardingNoteStep {
None, CreateWallet, TopUpWallet, Done
}
data class OnboardingNoteResources(
val strings: Strings = Strings()
) {
data class Strings(
val addressWasCopied: Int = -1
)
}

View file

@ -8,16 +8,18 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
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.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
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.*
import kotlinx.android.synthetic.main.layout_onboarding_note.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_container_top.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
/**
@ -25,11 +27,19 @@ import kotlinx.android.synthetic.main.view_onboarding_progress.*
*/
class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCardsState>() {
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imv_front_card.transitionName = ShareElement.imvFrontCard
startPostponedEnterTransition()
toolbar.setTitle(R.string.onboarding_title)
store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork)
store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen)

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.common.CompletionResult
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
@ -83,7 +84,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
OnboardingOtherCardsAction.Done -> {
store.dispatch(GlobalAction.Onboarding.Stop)
scope.launch {
globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse)
store.onCardScanned(onboardingManager.scanResponse)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
}

View file

@ -1,72 +0,0 @@
package com.tangem.tap.features.onboarding.products.twins
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.squareup.picasso.Picasso
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.withMainContext
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_twin_cards_welcome.*
import kotlinx.android.synthetic.main.layout_twin_cards.*
import kotlinx.coroutines.launch
class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards_welcome) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
})
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
store.dispatch(TwinCardsAction.SetOnboardingShown)
val secondTwinNumber = store.state.twinCardsState.cardNumber?.pairNumber()?.number ?: ""
val text = getString(R.string.twins_onboarding_description_format, secondTwinNumber)
tv_twin_cards_description_1.text = text
setOnClickListeners()
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
?.into(iv_twin_card_1)
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
?.into(iv_twin_card_2)
}
private fun setOnClickListeners() {
btn_continue.setOnClickListener {
scope.launch {
val global = store.state.globalState
val scanResponse = global.onboardingManager?.scanResponse ?: return@launch
global.tapWalletManager.onCardScanned(scanResponse)
withMainContext {
store.dispatchDebugErrorNotification("Делаем онбоардинг")
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
}
}
}
}

View file

@ -0,0 +1,64 @@
package com.tangem.tap.features.onboarding.products.twins.redux
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.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import org.rekotlin.Action
/**
[REDACTED_AUTHOR]
*/
sealed class TwinCardsAction : Action {
data class IfTwinsPrepareState(val scanResponse: ScanResponse) : TwinCardsAction()
object Init : TwinCardsAction()
data class SetMode(val mode: CreateTwinWalletMode) : TwinCardsAction()
data class SetStepOfScreen(val step: TwinCardsStep) : TwinCardsAction()
data class SetUserUnderstand(val isUnderstand: Boolean) : TwinCardsAction()
sealed class CardsManager {
data class Set(val manager: TwinCardsManager) : TwinCardsAction()
object Release : TwinCardsAction()
}
sealed class Wallet : TwinCardsAction() {
data class HandleOnBackPressed(
// this is necessary for the correct animation of the return of cards to the Home screen
val shouldResetTwinCardsWidget: (should: Boolean, popAction: VoidCallback) -> Unit
) : TwinCardsAction()
data class ShowInterruptDialog(val onOk: VoidCallback) : TwinCardsAction(), StateDialog
data class LaunchFirstStep(val initialMessage: Message, val reader: AssetReader) : TwinCardsAction()
data class LaunchSecondStep(
val initialMessage: Message,
val preparingMessage: Message,
val creatingWalletMessage: Message,
) : TwinCardsAction()
data class LaunchThirdStep(val message: Message) : TwinCardsAction()
}
// for the onboarding
data class SetPairCardId(val cardId: String) : TwinCardsAction()
object TopUp : TwinCardsAction()
object ShowAddressInfoDialog : TwinCardsAction()
data class SetWalletManager(val walletManager: WalletManager) : TwinCardsAction()
object Done : TwinCardsAction()
sealed class Balance {
object Update : TwinCardsAction()
data class Set(val balance: OnboardingWalletBalance) : TwinCardsAction()
}
sealed class Confetti {
object Show : TwinCardsAction()
object Hide : TwinCardsAction()
}
}

View file

@ -0,0 +1,275 @@
package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.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.makePrimaryWalletManager
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
class TwinCardsMiddleware {
companion object {
val handler = twinsWalletMiddleware
}
}
private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
handle(action, dispatch)
next(action)
}
}
}
private fun handle(action: Action, dispatch: DispatchFunction) {
val action = action as? TwinCardsAction ?: return
val globalState = store.state.globalState
val onboardingManager = globalState.onboardingManager
val twinCardsState = store.state.twinCardsState
fun getScanResponse(): ScanResponse {
return when (twinCardsState.mode) {
CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse
CreateTwinWalletMode.RecreateWallet -> globalState.scanResponse
} ?: throw NullPointerException("ScanResponse can't be NULL")
}
fun updateScanResponse(response: ScanResponse) {
when (twinCardsState.mode) {
CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse = response
CreateTwinWalletMode.RecreateWallet -> store.dispatch(GlobalAction.SaveScanNoteResponse(response))
}
}
fun startCardActivation(cardId: String) {
if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) {
onboardingManager?.activationStarted(cardId)
}
}
fun finishCardActivation() {
if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) {
onboardingManager?.activationFinished(getScanResponse().card.cardId)
twinCardsState.pairCardId?.let { onboardingManager?.activationFinished(it) }
}
}
when (action) {
is TwinCardsAction.Init -> {
if (twinCardsState.currentStep == TwinCardsStep.WelcomeOnly) return
when (twinCardsState.mode) {
CreateTwinWalletMode.CreateWallet -> {
if (preferencesStorage.wasTwinsOnboardingShown()) {
val step = when {
!getScanResponse().twinsIsTwinned() -> TwinCardsStep.CreateFirstWallet
twinCardsState.walletBalance.balanceIsToppedUp() -> TwinCardsStep.Done
else -> TwinCardsStep.TopUpWallet
}
store.dispatch((TwinCardsAction.SetStepOfScreen(step)))
} else {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Welcome))
}
}
CreateTwinWalletMode.RecreateWallet -> {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Warning))
}
}
}
is TwinCardsAction.Wallet.HandleOnBackPressed -> {
val shouldReturnCardBack = twinCardsState.mode == CreateTwinWalletMode.CreateWallet
&& twinCardsState.currentStep != TwinCardsStep.TopUpWallet
&& twinCardsState.currentStep != TwinCardsStep.Done
if (twinCardsState.showAlert) {
val onInterruptPrompt = {
store.dispatch(TwinCardsAction.CardsManager.Release)
action.shouldResetTwinCardsWidget(shouldReturnCardBack) {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
}
val stateDialog = TwinCardsAction.Wallet.ShowInterruptDialog(onInterruptPrompt)
store.dispatchDialogShow(stateDialog)
} else {
store.dispatch(TwinCardsAction.CardsManager.Release)
action.shouldResetTwinCardsWidget(shouldReturnCardBack) {
store.dispatch(NavigationAction.PopBackTo())
}
}
}
is TwinCardsAction.SetStepOfScreen -> {
when (action.step) {
TwinCardsStep.WelcomeOnly -> {
preferencesStorage.saveTwinsOnboardingShown()
}
TwinCardsStep.Welcome -> {
preferencesStorage.saveTwinsOnboardingShown()
}
TwinCardsStep.Warning -> {
}
TwinCardsStep.CreateFirstWallet -> {
}
TwinCardsStep.CreateSecondWallet -> {
}
TwinCardsStep.CreateThirdWallet -> {
}
TwinCardsStep.TopUpWallet -> {
store.dispatch(TwinCardsAction.Balance.Update)
}
TwinCardsStep.Done -> {
finishCardActivation()
postUi(500) { store.dispatch(TwinCardsAction.Confetti.Show) }
}
}
}
is TwinCardsAction.Wallet.LaunchFirstStep -> {
val manager = TwinCardsManager(getScanResponse().card, action.reader)
store.dispatch(TwinCardsAction.CardsManager.Set(manager))
scope.launch {
when (val result = manager.createFirstWallet(action.initialMessage)) {
is CompletionResult.Success -> {
startCardActivation(result.data.cardId)
delay(DELAY_SDK_DIALOG_CLOSE)
withMainContext {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateSecondWallet))
}
}
is CompletionResult.Failure -> {
}
}
}
}
is TwinCardsAction.Wallet.LaunchSecondStep -> {
val manager = twinCardsState.twinCardsManager ?: return
scope.launch {
val result = manager.createSecondWallet(
action.initialMessage,
action.preparingMessage,
action.creatingWalletMessage
)
when (result) {
is CompletionResult.Success -> {
startCardActivation(result.data.cardId)
delay(DELAY_SDK_DIALOG_CLOSE)
withMainContext {
store.dispatch(TwinCardsAction.SetPairCardId(result.data.cardId))
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateThirdWallet))
}
}
is CompletionResult.Failure -> {
}
}
}
}
is TwinCardsAction.Wallet.LaunchThirdStep -> {
val manager = twinCardsState.twinCardsManager ?: return
scope.launch {
when (val result = manager.complete(action.message)) {
is Result.Success -> {
updateScanResponse(result.data)
delay(DELAY_SDK_DIALOG_CLOSE)
withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.TopUpWallet)) }
}
is Result.Failure -> {
}
}
}
}
is TwinCardsAction.Balance.Update -> {
val walletManager = if (twinCardsState.walletManager != null) {
twinCardsState.walletManager
} else {
val wmFactory = globalState.tapWalletManager.walletManagerFactory
val walletManager = wmFactory.makePrimaryWalletManager(getScanResponse()).guard {
val message = "Loading cancelled. Cause: wallet manager didn't created"
val customError = TapError.CustomError(message)
store.dispatchErrorNotification(customError)
return
}
dispatch(TwinCardsAction.SetWalletManager(walletManager))
walletManager
}
val isLoadedBefore = twinCardsState.walletBalance.state != ProgressState.Loading
val balanceIsLoading = twinCardsState.walletBalance.copy(
currency = Currency.Blockchain(walletManager.wallet.blockchain),
state = ProgressState.Loading,
error = null,
criticalError = null
)
store.dispatch(TwinCardsAction.Balance.Set(balanceIsLoading))
scope.launch {
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)) }
}
}
is TwinCardsAction.Balance.Set -> {
if (action.balance.balanceIsToppedUp()) {
scope.launch {
withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) }
}
}
}
is TwinCardsAction.ShowAddressInfoDialog -> {
val addressData = twinCardsState.walletManager?.getAddressData() ?: return
val addressWasCopied = globalState.resources.strings.addressWasCopied
val appDialog = AppDialog.AddressInfoDialog(
addressData,
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
)
store.dispatchDialogShow(appDialog)
}
is TwinCardsAction.TopUp -> {
val topUpUrl = twinCardsState.walletManager?.getToUpUrl() ?: return
store.dispatchOpenUrl(topUpUrl)
}
TwinCardsAction.Done -> {
scope.launch {
val scanResponse = getScanResponse()
store.onCardScanned(scanResponse)
withMainContext {
when (twinCardsState.mode) {
CreateTwinWalletMode.CreateWallet -> {
store.dispatch(GlobalAction.Onboarding.Stop)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
CreateTwinWalletMode.RecreateWallet -> {
store.dispatch(NavigationAction.PopBackTo())
}
}
}
}
}
}
}

View file

@ -0,0 +1,64 @@
package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.twins.getTwinCardNumber
import org.rekotlin.Action
class TwinCardsReducer {
companion object {
fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state)
}
}
private fun internalReduce(action: Action, state: AppState): TwinCardsState {
if (action !is TwinCardsAction) return state.twinCardsState
var state = state.twinCardsState
when (action) {
is TwinCardsAction.IfTwinsPrepareState -> {
state = if (action.scanResponse.isTangemTwins()) {
val cardNumber = action.scanResponse.card.getTwinCardNumber() ?: return state
TwinCardsState(
cardNumber = cardNumber,
mode = CreateTwinWalletMode.CreateWallet,
)
} else {
TwinCardsState()
}
}
is TwinCardsAction.SetMode -> {
state = state.copy(mode = action.mode)
}
is TwinCardsAction.SetStepOfScreen -> {
state = state.copy(currentStep = action.step)
}
is TwinCardsAction.SetUserUnderstand -> {
state = state.copy(userWasUnderstandIfWalletRecreate = action.isUnderstand)
}
is TwinCardsAction.CardsManager.Set -> {
state = state.copy(twinCardsManager = action.manager)
}
TwinCardsAction.CardsManager.Release -> {
state = state.copy(twinCardsManager = null)
}
is TwinCardsAction.SetPairCardId -> {
state = state.copy(pairCardId = action.cardId)
}
is TwinCardsAction.SetWalletManager -> {
state = state.copy(walletManager = action.walletManager)
}
is TwinCardsAction.Balance.Set -> {
state = state.copy(walletBalance = action.balance)
}
is TwinCardsAction.Confetti.Show -> {
state = state.copy(showConfetti = true)
}
is TwinCardsAction.Confetti.Hide -> {
state = state.copy(showConfetti = false)
}
}
return state
}

View file

@ -0,0 +1,61 @@
package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import org.rekotlin.StateType
/**
[REDACTED_AUTHOR]
*/
data class TwinCardsState(
// if cardNumber = null -> the scanned card is not Twin
val cardNumber: TwinCardNumber? = null,
val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet,
val currentStep: TwinCardsStep = TwinCardsStep.Welcome,
val userWasUnderstandIfWalletRecreate: Boolean = false,
// available only when creating or recreating wallets
val twinCardsManager: TwinCardsManager? = null,
// for the onboarding
val pairCardId: String? = null, // available after create\recreate wallets
val walletManager: WalletManager? = null,
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
val showConfetti: Boolean = false,
) : StateType {
val steps: List<TwinCardsStep>
get() = when (mode) {
CreateTwinWalletMode.CreateWallet -> listOf(
TwinCardsStep.None,
TwinCardsStep.CreateFirstWallet,
TwinCardsStep.CreateSecondWallet,
TwinCardsStep.CreateThirdWallet,
TwinCardsStep.TopUpWallet,
TwinCardsStep.Done
)
CreateTwinWalletMode.RecreateWallet -> listOf(
TwinCardsStep.None,
TwinCardsStep.CreateFirstWallet,
TwinCardsStep.CreateSecondWallet,
TwinCardsStep.CreateThirdWallet,
TwinCardsStep.Done
)
}
val progress: Int
get() = steps.indexOf(currentStep)
val showAlert: Boolean
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
}
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }
enum class TwinCardsStep {
None, WelcomeOnly, Welcome, Warning, CreateFirstWallet, CreateSecondWallet, CreateThirdWallet,
// for the onboarding
TopUpWallet, Done
}

View file

@ -0,0 +1,366 @@
package com.tangem.tap.features.onboarding.products.twins.ui
import android.os.Bundle
import android.view.View
import android.view.animation.OvershootInterpolator
import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionInflater
import androidx.transition.TransitionManager
import com.squareup.picasso.Picasso
import com.tangem.Message
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_container_top.*
import kotlinx.android.synthetic.main.view_bg_twins_welcome.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
import org.rekotlin.Action
class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
private var previousStep = TwinCardsStep.None
private lateinit var twinsWidget: TwinsCardWidget
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
private val assetReader: AssetReader by lazy {
object : AssetReader {
override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name)
}
}
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
store.dispatch(TwinCardsAction.Init)
}
override fun configureTransitions() {
when (store.state.twinCardsState.mode) {
CreateTwinWalletMode.CreateWallet -> super.configureTransitions()
CreateTwinWalletMode.RecreateWallet -> {
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
reconfigureLayoutForTwins()
twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 250f }
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
toolbar.title = getText(R.string.twins_recreate_toolbar)
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_twin_front_card)
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.placeholder(R.drawable.card_placeholder_white)
?.into(imv_twin_back_card)
}
private fun reconfigureLayoutForTwins() {
imv_front_card.hide()
imv_card_background.hide()
cards_container.show()
imv_twin_front_card.transitionName = ShareElement.imvFrontCard
imv_twin_back_card.transitionName = ShareElement.imvBackCard
}
override fun subscribeToStore() {
store.subscribe(this) { state ->
state.skipRepeats { oldState, newState ->
oldState.twinCardsState == newState.twinCardsState
}.select { it.twinCardsState }
}
storeSubscribersList.add(this)
}
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
when (state.currentStep) {
TwinCardsStep.WelcomeOnly -> setupWelcomeOnlyState(state)
TwinCardsStep.Welcome -> setupWelcomeState(state)
TwinCardsStep.Warning -> setupWarningState(state)
TwinCardsStep.CreateFirstWallet -> setupCreateFirstWalletState(state)
TwinCardsStep.CreateSecondWallet -> setupCreateSecondWalletState(state)
TwinCardsStep.CreateThirdWallet -> setupCreateThirdWalletState(state)
TwinCardsStep.TopUpWallet -> setupTopUpWalletState(state)
TwinCardsStep.Done -> setupDoneState(state)
}
setBalance(state)
showConfetti(state.showConfetti)
previousStep = state.currentStep
}
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"
}
private fun setupWelcomeOnlyState(state: TwinCardsState) {
setupWelcomeState(state, NavigationAction.NavigateTo(AppScreen.Wallet))
}
private fun setupWelcomeState(state: TwinCardsState) {
setupWelcomeState(state, TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet))
}
private fun setupWelcomeState(state: TwinCardsState, mainAction: Action) {
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
pb_state.hide()
tv_header.setText(R.string.twins_onboarding_subtitle)
tv_body.text = getString(R.string.twins_onboarding_description_format, state.cardNumber?.pairIndexNumber())
btn_main_action.setText(R.string.common_continue)
btn_main_action.setOnClickListener { store.dispatch(mainAction) }
}
private fun setupWarningState(state: TwinCardsState) {
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
pb_state.hide()
chb_understand.show()
tv_header.setText(R.string.common_warning)
tv_body.setText(R.string.twins_recreate_warning)
chb_understand.setOnCheckedChangeListener { buttonView, isChecked ->
store.dispatch(TwinCardsAction.SetUserUnderstand(isChecked))
}
btn_main_action.isEnabled = state.userWasUnderstandIfWalletRecreate
btn_main_action.setText(R.string.common_continue)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet))
}
}
private fun setupCreateFirstWalletState(state: TwinCardsState) {
bg_circle_large.hide()
bg_circle_medium.hide()
bg_circle_min.hide()
chb_understand.hide()
pb_state.show()
onboarding_main_container.beginDelayedTransition()
when (previousStep) {
TwinCardsStep.None -> {
twinsWidget.toLeapfrog(false) {
when (state.cardNumber) {
TwinCardNumber.First -> startPostponedEnterTransition()
TwinCardNumber.Second -> {
switchToCard(state.cardNumber, false) { startPostponedEnterTransition() }
}
}
}
}
TwinCardsStep.Welcome, TwinCardsStep.Warning -> {
twinsWidget.toLeapfrog(onEnd = { switchToCard(state.cardNumber) })
}
}
val twinIndexNumber = state.cardNumber?.indexNumber()
tv_header.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
tv_body.setText(R.string.onboarding_twins_interrupt_warning)
btn_main_action.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
assetReader
))
}
btn_alternative_action.isVisible = false
btn_alternative_action.isClickable = false
}
private fun setupCreateSecondWalletState(state: TwinCardsState) {
switchToCard(state.cardNumber?.pairNumber())
val twinPairIndexNumber = state.cardNumber?.pairIndexNumber()
tv_header.text = getString(R.string.twins_recreate_title_format, twinPairIndexNumber)
tv_body.setText(R.string.onboarding_twins_interrupt_warning)
btn_main_action.text = getString(R.string.twins_recreate_button_format, twinPairIndexNumber)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep(
Message(getString(R.string.twins_recreate_title_format, twinPairIndexNumber)),
Message(getString(R.string.twins_recreate_title_preparing)),
Message(getString(R.string.twins_recreate_title_creating_wallet)),
))
}
}
private fun setupCreateThirdWalletState(state: TwinCardsState) {
switchToCard(state.cardNumber)
val twinIndexNumber = state.cardNumber?.indexNumber()
tv_header.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
tv_body.setText(R.string.onboarding_twins_interrupt_warning)
btn_main_action.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber))
))
}
}
private fun setupTopUpWalletState(state: TwinCardsState) {
when (previousStep) {
TwinCardsStep.None -> {
when (state.cardNumber) {
TwinCardNumber.First -> {
twinsWidget.leapfrogWidget.unfold(false) {
twinsWidget.toActivate(false) {
startPostponedEnterTransition()
}
}
}
TwinCardNumber.Second -> {
switchToCard(state.cardNumber, false) {
twinsWidget.toActivate(false)
startPostponedEnterTransition()
}
}
}
}
TwinCardsStep.CreateThirdWallet -> {
twinsWidget.toActivate()
}
}
btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.TopUp)
}
btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address)
btn_alternative_action.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
}
tv_header.setText(R.string.onboarding_top_up_header)
tv_body.setText(R.string.onboarding_top_up_body)
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
if (btnRefreshBalanceWidget.isShowing != true) {
postUi(300) {
btnRefreshBalanceWidget.mainView.setOnClickListener {
store.dispatch(TwinCardsAction.Balance.Update)
}
}
}
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet_twins)
}
private fun setupDoneState(state: TwinCardsState) {
btn_main_action.setText(R.string.onboarding_done_button_continue)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Confetti.Hide)
store.dispatch(TwinCardsAction.Done)
}
btn_alternative_action.isVisible = false
btn_alternative_action.isClickable = false
tv_header.setText(R.string.onboarding_done_header)
tv_body.setText(R.string.onboarding_done_body)
val layout = when (state.mode) {
CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins
CreateTwinWalletMode.RecreateWallet -> R.layout.lp_onboarding_done
}
updateConstraints(state.currentStep, layout)
}
private fun updateConstraints(currentStep: TwinCardsStep, @LayoutRes layoutId: Int) {
if (this.previousStep == currentStep) return
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboarding_main_container)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
}
private fun switchToCard(cardNumber: TwinCardNumber?, animate: Boolean = true, onEnd: VoidCallback = {}) {
val position = cardNumber?.number ?: return
if (twinsWidget.leapfrogWidget.getViewPositionByIndex(1) != position - 1) {
twinsWidget.leapfrogWidget.leap(animate, onEnd)
} else {
onEnd()
}
}
override fun handleOnBackPressed() {
store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed { should, popAction ->
store.dispatch(TwinCardsAction.Confetti.Hide)
showConfetti(false)
if (should) switchToCard(TwinCardNumber.First, true, popAction)
else popAction()
})
}
}
private fun TwinCardNumber?.indexNumber(): String {
return this?.number?.toString() ?: ""
}
private fun TwinCardNumber?.pairIndexNumber(): String {
return this?.pairNumber()?.number?.toString() ?: ""
}

View file

@ -1,10 +1,10 @@
package com.tangem.tap.features.twins.ui.dialog
package com.tangem.tap.features.onboarding.products.twins.ui.dialog
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.store
import com.tangem.wallet.R
@ -13,10 +13,10 @@ import com.tangem.wallet.R
*/
class CreateWalletInterruptDialog {
companion object {
fun create(state: TwinCardsAction.Wallet.InterruptDialog, context: Context): AlertDialog {
fun create(state: TwinCardsAction.Wallet.ShowInterruptDialog, context: Context): AlertDialog {
return MaterialAlertDialogBuilder(context)
.setMessage(R.string.details_twins_recreate_alert)
.setPositiveButton(R.string.common_ok) { _, _ -> state.interrupt() }
.setMessage(R.string.twins_recreate_alert)
.setPositiveButton(R.string.common_ok) { _, _ -> state.onOk() }
.setNegativeButton(R.string.common_cancel) { _, _ -> }
.setOnDismissListener { store.dispatchDialogHide() }
.create()

View file

@ -3,8 +3,8 @@ package com.tangem.tap.features.onboarding.products.wallet
import android.os.Bundle
import android.view.View
import androidx.transition.TransitionInflater
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
import com.tangem.tap.features.send.BaseStoreFragment
import com.tangem.tap.store
import com.tangem.wallet.R
import org.rekotlin.StoreSubscriber

View file

@ -7,7 +7,6 @@ import android.text.method.DigitsKeyListener
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.activity.OnBackPressedCallback
import androidx.core.view.postDelayed
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
@ -25,7 +24,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.snackBar.MaxAmountSnackbar
import com.tangem.tap.common.text.truncateMiddleWith
import com.tangem.tap.common.toggleWidget.*
import com.tangem.tap.features.send.BaseStoreFragment
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.*
import com.tangem.tap.features.send.redux.AmountActionUi.*
@ -63,23 +62,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
private val sendSubscriber = SendStateSubscriber(this)
private lateinit var keyboardObserver: KeyboardObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val externalTransactionData = store.state.sendState.externalTransactionData
if (externalTransactionData == null) {
store.dispatch(NavigationAction.PopBackTo())
} else {
store.dispatch(
WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)
)
}
}
})
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -97,7 +79,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
private fun initSendButtonStates() {
btnSend.setOnClickListener {
store.dispatch(SendActionUi.SendAmountToRecipient(
Message(getString(R.string.initial_message_sign_header))
Message(getString(R.string.initial_message_sign_header))
))
}
sendBtn = IndeterminateProgressButtonWidget(btnSend, progress)
@ -124,8 +106,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
imvQrCode.setOnClickListener {
startActivityForResult(
Intent(requireContext(), ScanQrCodeActivity::class.java),
ScanQrCodeActivity.SCAN_QR_REQUEST_CODE
Intent(requireContext(), ScanQrCodeActivity::class.java),
ScanQrCodeActivity.SCAN_QR_REQUEST_CODE
)
}
}
@ -281,6 +263,17 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
sp.edit().putString("mainCurrency", type.name).apply()
}
override fun handleOnBackPressed() {
val externalTransactionData = store.state.sendState.externalTransactionData
if (externalTransactionData == null) {
store.dispatch(NavigationAction.PopBackTo())
} else {
store.dispatch(
WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)
)
}
}
override fun onDestroy() {
keyboardObserver.unregisterListener()
store.dispatch(ReleaseSendState)

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.send.ui.stateSubscribers
import com.tangem.tap.features.send.BaseStoreFragment
import com.tangem.tap.features.BaseStoreFragment
import org.rekotlin.StateType
import org.rekotlin.StoreSubscriber
import java.lang.ref.WeakReference

View file

@ -12,7 +12,7 @@ import com.tangem.tap.common.redux.getMessageString
import com.tangem.tap.common.text.DecimalDigitsInputFilter
import com.tangem.tap.domain.MultiMessageError
import com.tangem.tap.domain.assembleErrors
import com.tangem.tap.features.send.BaseStoreFragment
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
import com.tangem.tap.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.SendAction
@ -188,15 +188,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
(fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type)
val balanceText = fg.getString(R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol,
state.viewBalanceValue)
state.mainCurrency.currencySymbol,
state.viewBalanceValue)
fg.tvBalance.update(balanceText)
fg.tilAmountToSend.isEnabled = state.inputIsEnabled
val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0
fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0)
val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray
val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray
fg.tvAmountCurrency.setTextColor(fg.getColor(textColor))
}
@ -245,7 +245,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
val willSent = getString(R.string.send_total_subtitle_format,
receipt.willSentCrypto, receipt.symbols.crypto)
receipt.willSentCrypto, receipt.symbols.crypto)
totalLayout.tvWillBeSentValue.update(willSent)
}
@ -277,9 +277,9 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
val willSent = getString(
R.string.send_total_subtitle_asset_format,
receipt.symbols.token ?: "", receipt.willSentToken,
receipt.symbols.crypto, receipt.willSentFeeCoin
R.string.send_total_subtitle_asset_format,
receipt.symbols.token ?: "", receipt.willSentToken,
receipt.symbols.crypto, receipt.willSentFeeCoin
)
totalLayout.tvWillBeSentValue.update(willSent)
}

View file

@ -1,62 +0,0 @@
package com.tangem.tap.features.twins.redux
import com.tangem.Message
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinCardsManager
import org.rekotlin.Action
/**
[REDACTED_AUTHOR]
*/
sealed class TwinCardsAction : Action {
data class SetResources(val resources: TwinCardsResources) : TwinCardsAction()
data class SetTwinCard(
val number: TwinCardNumber,
val secondCardId: String?,
val isCreatingTwinCardsAllowed: Boolean,
) : TwinCardsAction()
sealed class CardsManager {
data class Set(val manager: TwinCardsManager) : TwinCardsAction()
object Release : TwinCardsAction()
}
object ShowOnboarding : TwinCardsAction()
object SetOnboardingShown : TwinCardsAction()
object ProceedToCreateWallet : TwinCardsAction()
sealed class Wallet : TwinCardsAction() {
object HandleOnBackPressed : TwinCardsAction()
data class Create(
val number: TwinCardNumber,
val createTwinWalletMode: CreateTwinWalletMode
) : TwinCardsAction()
data class InterruptDialog(val interrupt: () -> Unit) : TwinCardsAction(), StateDialog
data class LaunchFirstStep(
val initialMessage: Message,
val reader: AssetReader
) : TwinCardsAction() {
object Success : TwinCardsAction()
}
data class LaunchSecondStep(
val initialMessage: Message,
val preparingMessage: Message,
val creatingWalletMessage: Message,
) : TwinCardsAction() {
object Success : TwinCardsAction()
}
data class LaunchThirdStep(val message: Message) : TwinCardsAction() {
data class Success(val scanResponse: ScanResponse) : TwinCardsAction()
}
}
}

View file

@ -1,139 +0,0 @@
package com.tangem.tap.features.twins.redux
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchNotification
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
class TwinCardsMiddleware {
companion object {
val handler = twinsWalletMiddleware
}
}
private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
handle(action, dispatch)
next(action)
}
}
}
private fun handle(action: Action, dispatch: DispatchFunction) {
val action = action as? TwinCardsAction ?: return
val twinCardsState = store.state.twinCardsState
when (action) {
is TwinCardsAction.SetTwinCard -> {
val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
if (showOnboarding) store.dispatch(TwinCardsAction.ShowOnboarding)
}
TwinCardsAction.SetOnboardingShown -> {
preferencesStorage.saveTwinsOnboardingShown()
}
is TwinCardsAction.ProceedToCreateWallet -> {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet))
}
is TwinCardsAction.Wallet.HandleOnBackPressed -> {
if (twinCardsState.createWalletState?.showAlert == true) {
val stateDialog = TwinCardsAction.Wallet.InterruptDialog {
store.dispatch(TwinCardsAction.CardsManager.Release)
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
store.dispatchDialogShow(stateDialog)
} else {
store.dispatch(TwinCardsAction.CardsManager.Release)
store.dispatch(NavigationAction.PopBackTo())
}
}
is TwinCardsAction.Wallet.Create -> {
val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
if (wallet == null) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
return
}
val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty()
if (notEmpty) {
store.dispatchNotification(twinCardsState.resources.strings.walletIsNotEmpty)
} else {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
}
}
is TwinCardsAction.Wallet.LaunchFirstStep -> {
val scanResponse = store.state.globalState.scanResponse ?: return
val manager = TwinCardsManager(scanResponse, action.reader)
store.dispatch(TwinCardsAction.CardsManager.Set(manager))
scope.launch {
val result = manager.createFirstWallet(action.initialMessage)
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success -> {
store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep.Success)
}
is SimpleResult.Failure -> {
}
}
}
}
}
is TwinCardsAction.Wallet.LaunchSecondStep -> {
val manager = twinCardsState.twinCardsManager ?: return
scope.launch {
val result = manager.createSecondWallet(action.initialMessage,
action.preparingMessage, action.creatingWalletMessage)
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success -> {
store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep.Success)
}
is SimpleResult.Failure -> {
}
}
}
}
}
is TwinCardsAction.Wallet.LaunchThirdStep -> {
val manager = twinCardsState.twinCardsManager ?: return
scope.launch {
val result = manager.complete(action.message)
withContext(Dispatchers.Main) {
when (result) {
is Result.Success -> {
store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep.Success(result.data))
}
is Result.Failure -> {
}
}
}
}
}
is TwinCardsAction.Wallet.LaunchThirdStep.Success -> {
scope.launch {
store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse)
}
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
}
}

View file

@ -1,60 +0,0 @@
package com.tangem.tap.features.twins.redux
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Action
class TwinCardsReducer {
companion object {
fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state)
}
}
private fun internalReduce(action: Action, state: AppState): TwinCardsState {
if (action !is TwinCardsAction) return state.twinCardsState
val state = state.twinCardsState
return when (action) {
is TwinCardsAction.SetResources -> {
state.copy(resources = action.resources)
}
is TwinCardsAction.SetTwinCard -> {
state.copy(
cardNumber = action.number,
secondCardId = action.secondCardId,
showTwinOnboarding = state.showTwinOnboarding,
isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed,
)
}
is TwinCardsAction.CardsManager.Set -> {
state.copy(twinCardsManager = action.manager)
}
TwinCardsAction.CardsManager.Release -> {
state.copy(twinCardsManager = null)
}
is TwinCardsAction.ShowOnboarding -> {
state.copy(showTwinOnboarding = true)
}
is TwinCardsAction.SetOnboardingShown -> {
state.copy(showTwinOnboarding = false)
}
is TwinCardsAction.Wallet.Create -> {
val prevState = state.createWalletState
state.copy(createWalletState = CreateTwinWalletState(
prevState?.scanResponse,
prevState?.number ?: action.number,
prevState?.mode ?: action.createTwinWalletMode,
))
}
TwinCardsAction.Wallet.LaunchFirstStep.Success -> {
state.copy(createWalletState = state.createWalletState?.copy(
step = CreateTwinWalletStep.SecondStep
))
}
TwinCardsAction.Wallet.LaunchSecondStep.Success ->
state.copy(createWalletState = state.createWalletState?.copy(
step = CreateTwinWalletStep.ThirdStep
))
else -> state
}
}

View file

@ -1,43 +0,0 @@
package com.tangem.tap.features.twins.redux
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinCardsManager
import org.rekotlin.StateType
/**
[REDACTED_AUTHOR]
*/
data class TwinCardsState(
val twinCardsManager: TwinCardsManager? = null,
// if cardNumber is not set -> the scanned card is not Twin
val cardNumber: TwinCardNumber? = null,
val secondCardId: String? = null,
val showTwinOnboarding: Boolean = false,
val isCreatingTwinCardsAllowed: Boolean = false,
val createWalletState: CreateTwinWalletState? = null,
val resources: TwinCardsResources = TwinCardsResources(),
) : StateType
data class CreateTwinWalletState(
val scanResponse: ScanResponse?,
val number: TwinCardNumber = TwinCardNumber.First,
val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet,
val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep,
) {
val showAlert: Boolean
get() = step != CreateTwinWalletStep.FirstStep
}
enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep }
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }
data class TwinCardsResources(
val strings: Strings = Strings()
) {
//TODO: create global structure for StringRes. Set it from the MainActivity.
data class Strings(
val walletIsNotEmpty: Int = -1
)
}

View file

@ -1,162 +0,0 @@
package com.tangem.tap.features.twins.ui
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.core.content.ContextCompat.getColor
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.squareup.picasso.Picasso
import com.tangem.Message
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.twins.redux.*
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_twin_cards_create.*
import org.rekotlin.StoreSubscriber
class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create),
StoreSubscriber<TwinCardsState> {
private val assetReader: AssetReader by lazy {
object : AssetReader {
override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed)
}
})
initResources()
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
private fun initResources() {
val resources = TwinCardsResources(
TwinCardsResources.Strings(
R.string.details_notification_erase_wallet_not_possible
)
)
store.dispatch(TwinCardsAction.SetResources(resources))
}
override fun onStart() {
super.onStart()
store.subscribe(this) { state ->
state.skipRepeats { oldState, newState ->
oldState.twinCardsState == newState.twinCardsState
}.select { it.twinCardsState }
}
}
override fun onStop() {
super.onStop()
store.unsubscribe(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener { store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) }
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
?.into(iv_card_1)
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
?.into(iv_card_2)
}
override fun newState(state: TwinCardsState) {
if (activity == null) return
toolbar.title = when (state.createWalletState?.mode) {
CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet)
CreateTwinWalletMode.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar)
}
val selectedColor = getColor(requireContext(), R.color.colorSecondary)
val defaultColor = getColor(requireContext(), R.color.blue_pale)
val twinCardNumber = state.createWalletState?.number ?: TwinCardNumber.First
val cardNumber = when (state.createWalletState?.step) {
CreateTwinWalletStep.FirstStep -> {
val twinCardNumberString = twinCardNumber.number.toString()
tv_step_number.text =
getString(R.string.details_twins_recreate_step_format, "1")
v_step_1.setBackgroundColor(selectedColor)
v_step_2.setBackgroundColor(defaultColor)
v_step_3.setBackgroundColor(defaultColor)
btn_tap.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(
R.string.details_twins_recreate_title_format,
twinCardNumberString
)),
assetReader
))
}
btn_tap.text = getString(R.string.details_twins_recreate_button_format,
twinCardNumber.number.toString())
twinCardNumberString
}
CreateTwinWalletStep.SecondStep -> {
val twinCardNumberString = twinCardNumber.pairNumber().number.toString()
tv_step_number.text =
getString(R.string.details_twins_recreate_step_format, "2")
v_step_1.setBackgroundColor(selectedColor)
v_step_2.setBackgroundColor(selectedColor)
v_step_3.setBackgroundColor(defaultColor)
btn_tap.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep(
Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)),
Message(getString(R.string.details_twins_recreate_title_preparing)),
Message(getString(R.string.details_twins_recreate_title_creating_wallet)),
))
}
btn_tap.text = getString(R.string.details_twins_recreate_button_format, "2")
twinCardNumberString
}
CreateTwinWalletStep.ThirdStep -> {
val twinCardNumberString = twinCardNumber.number.toString()
tv_step_number.text =
getString(R.string.details_twins_recreate_step_format, "3")
v_step_1.setBackgroundColor(selectedColor)
v_step_2.setBackgroundColor(selectedColor)
v_step_3.setBackgroundColor(selectedColor)
btn_tap.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep(
Message(getString(
R.string.details_twins_recreate_title_format, twinCardNumberString)
)
))
}
btn_tap.text = getString(R.string.details_twins_recreate_button_format, twinCardNumberString)
twinCardNumberString
}
else -> null
}
btn_tap.text = getString(R.string.details_twins_recreate_button_format, cardNumber)
tv_twin_title.text = getString(R.string.details_twins_recreate_title_format, cardNumber)
}
}

View file

@ -1,57 +0,0 @@
package com.tangem.tap.features.twins.ui
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.squareup.picasso.Picasso
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_twin_cards_warning.*
import kotlinx.android.synthetic.main.layout_twin_cards_orange.*
class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo())
}
})
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val createTwinWallet = store.state.twinCardsState.createWalletState?.mode
if (createTwinWallet == CreateTwinWalletMode.CreateWallet) {
tv_twin_cards_description.text = getText(R.string.details_twins_recreate_subtitle)
} else if (createTwinWallet == CreateTwinWalletMode.RecreateWallet) {
tv_twin_cards_description.text = getText(R.string.details_twins_recreate_warning)
}
btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) }
btn_start.setOnClickListener { store.dispatch(TwinCardsAction.ProceedToCreateWallet) }
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
?.into(iv_twin_card_1)
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
?.into(iv_twin_card_2)
}
}

View file

@ -5,12 +5,13 @@ import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.Wallet
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.domain.extensions.toSendableAmounts
data class PendingTransaction(
val address: String?,
val amount: String?,
val currency: String,
val type: PendingTransactionType
val address: String?,
val amount: String?,
val currency: String,
val type: PendingTransactionType
)
enum class PendingTransactionType { Incoming, Outgoing, Unknown }
@ -36,10 +37,10 @@ fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransact
}
return PendingTransaction(
if (address == "unknown") null else address,
this.amount.value?.toFormattedString(amount.decimals),
this.amount.currencySymbol,
type
if (address == "unknown") null else address,
this.amount.value?.toFormattedString(amount.decimals),
this.amount.currencySymbol,
type
)
}
@ -66,4 +67,8 @@ fun Wallet.getPendingTransactions(): List<PendingTransaction> {
fun Wallet.hasPendingTransactions(): Boolean {
return getPendingTransactions().isNotEmpty()
}
fun Wallet.hasSendableAmountsOrPendingTransactions(): Boolean {
return hasPendingTransactions() || amounts.toSendableAmounts().isNotEmpty()
}

View file

@ -12,7 +12,7 @@ import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.topup.TradeCryptoHelper
import com.tangem.tap.features.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken

View file

@ -19,8 +19,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.network.NetworkStateChanged
import kotlinx.coroutines.launch
@ -96,31 +94,22 @@ class WalletMiddleware {
}
}
is WalletAction.CreateWallet -> {
if (walletState.isTangemTwins) {
store.dispatch(TwinCardsAction.Wallet.Create(
walletState.twinCardsState.cardNumber!!,
CreateTwinWalletMode.CreateWallet
))
} else {
scope.launch {
val result = tangemSdkManager.createWallet(
globalState.scanResponse?.card?.cardId
)
when (result) {
is CompletionResult.Success -> {
val scanNoteResponse = globalState.scanResponse?.copy(card = result.data)
scanNoteResponse?.let {
globalState.tapWalletManager.onCardScanned(scanNoteResponse)
}
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
card = store.state.detailsState.card
)
}
scope.launch {
val result = tangemSdkManager.createWallet(
globalState.scanResponse?.card?.cardId
)
when (result) {
is CompletionResult.Success -> {
val scanNoteResponse = globalState.scanResponse?.copy(card = result.data)
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) }
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
card = store.state.detailsState.card
)
}
}
}

View file

@ -38,13 +38,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
is WalletAction.ResetState -> newState = WalletState()
is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet)
is WalletAction.EmptyWallet -> {
val creatingWalletAllowed = state.twinCardsState.isCreatingTwinCardsAllowed
newState = newState.copy(
state = ProgressState.Done,
wallets = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed),
mainButton = WalletMainButton.CreateWalletButton(true),
)
)
)

View file

@ -150,8 +150,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
private fun setupCardImage(cardImage: Artwork?) {
Picasso.get()
.load(cardImage?.artworkId)
.placeholder(R.drawable.card_placeholder)
?.error(R.drawable.card_placeholder)
.placeholder(R.drawable.card_placeholder_black)
?.error(R.drawable.card_placeholder_black)
?.into(iv_card)
}

View file

@ -7,10 +7,8 @@ import android.widget.Button
import androidx.recyclerview.widget.LinearLayoutManager
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.BalanceWidget
@ -93,7 +91,7 @@ class SingleWalletView : WalletView {
}
private fun setupTwinCards(
twinCardsState: TwinCardsState?, fragment: WalletFragment
twinCardsState: TwinCardsState?, fragment: WalletFragment
) = with(fragment) {
twinCardsState?.cardNumber?.let { cardNumber ->
tv_twin_card_number.show()
@ -107,11 +105,6 @@ class SingleWalletView : WalletView {
if (twinCardsState?.cardNumber == null) {
tv_twin_card_number.hide()
}
if (twinCardsState?.showTwinOnboarding == true) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
}
}
private fun setupButtons(

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="12dp" />
<size
android:width="328dp"
android:height="174dp" />
<gradient
android:endColor="#FFFFFF"
android:startColor="#FFFFFF" />
</shape>
</item>
<item
android:drawable="@drawable/ic_tangem_pale"
android:gravity="center">
<size
android:width="85dp"
android:height="23dp" />
</item>
</layer-list>

View file

@ -0,0 +1,126 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="249dp"
android:height="150dp"
android:viewportWidth="249"
android:viewportHeight="150">
<path
android:fillAlpha="0.03"
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z" />
<path
android:fillAlpha="0.5"
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="7.404067"
android:centerY="8.65384"
android:gradientRadius="267.36932"
android:type="radial">
<item
android:color="#FFFFFFFF"
android:offset="0" />
<item
android:color="#19FFFFFF"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#1B1D1C"
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z" />
<path
android:fillColor="#00000000"
android:pathData="M10,0.5L238,0.5A9.499,9.501 134.998,0 1,247.501 10L247.517,140A9.499,9.501 134.998,0 1,238.019 149.5L10.019,149.5A9.499,9.501 134.998,0 1,0.517 140L0.501,10A9.499,9.501 134.998,0 1,10 0.5z"
android:strokeWidth="1">
<aapt:attr name="android:strokeColor">
<gradient
android:endX="246.96196"
android:endY="144.774"
android:startX="2.844399"
android:startY="3.38345"
android:type="linear">
<item
android:color="#FF1A1A1A"
android:offset="0" />
<item
android:color="#FF565656"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#A5A5A5"
android:pathData="M28.359,121.942L26.389,120.734L26.472,123H25.41L25.498,120.734L23.505,121.95L23.002,121.003L25.044,119.992L23.009,118.974L23.511,118.058L25.497,119.25L25.415,117H26.468L26.389,119.258L28.365,118.058L28.884,118.974L26.826,120L28.885,121.011L28.359,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M38.855,121.942L36.885,120.734L36.967,123H35.906L35.994,120.734L34.001,121.95L33.498,121.003L35.54,119.992L33.505,118.974L34.007,118.058L35.993,119.25L35.911,117H36.964L36.884,119.258L38.861,118.058L39.38,118.974L37.322,120L39.381,121.011L38.855,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M49.351,121.942L47.381,120.734L47.463,123H46.402L46.49,120.734L44.497,121.95L43.994,121.003L46.035,119.992L44.001,118.974L44.503,118.058L46.489,119.25L46.407,117H47.46L47.38,119.258L49.357,118.058L49.876,118.974L47.818,120L49.877,121.011L49.351,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M59.847,121.942L57.877,120.734L57.959,123H56.897L56.985,120.734L54.993,121.95L54.49,121.003L56.531,119.992L54.497,118.974L54.999,118.058L56.985,119.25L56.902,117H57.956L57.876,119.258L59.853,118.058L60.372,118.974L58.314,120L60.373,121.011L59.847,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M76.235,121.942L74.266,120.734L74.348,123H73.286L73.374,120.734L71.381,121.95L70.879,121.003L72.92,119.992L70.885,118.974L71.387,118.058L73.373,119.25L73.291,117H74.345L74.265,119.258L76.242,118.058L76.761,118.974L74.703,120L76.762,121.011L76.235,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M86.731,121.942L84.761,120.734L84.844,123H83.782L83.87,120.734L81.877,121.95L81.374,121.003L83.416,119.992L81.381,118.974L81.883,118.058L83.869,119.25L83.787,117H84.841L84.761,119.258L86.737,118.058L87.257,118.974L85.199,120L87.258,121.011L86.731,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M97.227,121.942L95.257,120.734L95.34,123H94.278L94.366,120.734L92.373,121.95L91.87,121.003L93.912,119.992L91.877,118.974L92.379,118.058L94.365,119.25L94.283,117H95.337L95.257,119.258L97.233,118.058L97.752,118.974L95.695,120L97.753,121.011L97.227,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M107.723,121.942L105.753,120.734L105.835,123H104.774L104.862,120.734L102.869,121.95L102.366,121.003L104.408,119.992L102.373,118.974L102.875,118.058L104.861,119.25L104.779,117H105.832L105.752,119.258L107.729,118.058L108.248,118.974L106.19,120L108.249,121.011L107.723,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M124.112,121.942L122.142,120.734L122.224,123H121.163L121.251,120.734L119.258,121.95L118.755,121.003L120.796,119.992L118.762,118.974L119.264,118.058L121.25,119.25L121.168,117H122.221L122.141,119.258L124.118,118.058L124.637,118.974L122.579,120L124.638,121.011L124.112,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M134.608,121.942L132.638,120.734L132.72,123H131.659L131.746,120.734L129.754,121.95L129.251,121.003L131.292,119.992L129.258,118.974L129.76,118.058L131.746,119.25L131.664,117H132.717L132.637,119.258L134.614,118.058L135.133,118.974L133.075,120L135.134,121.011L134.608,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M145.104,121.942L143.134,120.734L143.216,123H142.154L142.242,120.734L140.249,121.95L139.747,121.003L141.788,119.992L139.754,118.974L140.256,118.058L142.242,119.25L142.159,117H143.213L143.133,119.258L145.11,118.058L145.629,118.974L143.571,120L145.63,121.011L145.104,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M155.599,121.942L153.63,120.734L153.712,123H152.65L152.738,120.734L150.745,121.95L150.242,121.003L152.284,119.992L150.249,118.974L150.751,118.058L152.737,119.25L152.655,117H153.709L153.629,119.258L155.606,118.058L156.125,118.974L154.067,120L156.126,121.011L155.599,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M171.988,121.942L170.018,120.734L170.101,123H169.039L169.127,120.734L167.134,121.95L166.631,121.003L168.673,119.992L166.638,118.974L167.14,118.058L169.126,119.25L169.044,117H170.098L170.018,119.258L171.994,118.058L172.513,118.974L170.456,120L172.515,121.011L171.988,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M182.484,121.942L180.514,120.734L180.597,123H179.535L179.623,120.734L177.63,121.95L177.127,121.003L179.169,119.992L177.134,118.974L177.636,118.058L179.622,119.25L179.54,117H180.593L180.514,119.258L182.49,118.058L183.009,118.974L180.952,120L183.01,121.011L182.484,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M192.98,121.942L191.01,120.734L191.092,123H190.031L190.119,120.734L188.126,121.95L187.623,121.003L189.665,119.992L187.63,118.974L188.132,118.058L190.118,119.25L190.036,117H191.089L191.009,119.258L192.986,118.058L193.505,118.974L191.447,120L193.506,121.011L192.98,121.942Z" />
<path
android:fillColor="#A5A5A5"
android:pathData="M203.476,121.942L201.506,120.734L201.588,123H200.527L200.615,120.734L198.622,121.95L198.119,121.003L200.16,119.992L198.126,118.974L198.628,118.058L200.614,119.25L200.532,117H201.585L201.505,119.258L203.482,118.058L204.001,118.974L201.943,120L204.002,121.011L203.476,121.942Z" />
<path
android:fillColor="#ffffff"
android:pathData="M26.483,30.4L26.483,36.64L29.382,36.64C29.494,36.639 29.602,36.596 29.681,36.522C29.761,36.447 29.806,36.346 29.807,36.241L29.807,30.4L26.483,30.4Z" />
<path
android:fillColor="#ffffff"
android:pathData="M20,36.241C20.001,36.346 20.046,36.447 20.126,36.522C20.205,36.596 20.313,36.639 20.425,36.64L23.324,36.64L23.324,30.4L20,30.4L20,36.241Z" />
<path
android:fillColor="#ffffff"
android:pathData="M29.807,27.2L29.807,24.409C29.807,24.301 29.763,24.197 29.685,24.12C29.606,24.043 29.5,24 29.389,24L20.418,24C20.307,24 20.201,24.043 20.123,24.12C20.044,24.197 20,24.301 20,24.409L20,27.2L29.807,27.2Z" />
<path
android:fillColor="#ffffff"
android:pathData="M45.32,28.163C44.719,27.921 44.068,27.812 43.416,27.846C42.555,27.844 41.696,27.934 40.856,28.113L40.856,29.532C41.526,29.455 42.199,29.412 42.874,29.4C43.236,29.385 43.598,29.418 43.949,29.5C44.062,29.532 44.164,29.59 44.247,29.669C44.33,29.748 44.392,29.846 44.425,29.953C44.531,30.334 44.576,30.728 44.558,31.122L44.558,31.539C43.764,31.498 43.131,31.476 42.698,31.476C42.236,31.454 41.775,31.539 41.356,31.725C41.025,31.903 40.779,32.195 40.666,32.54C40.509,33.044 40.438,33.569 40.456,34.095C40.398,34.771 40.564,35.448 40.932,36.029C41.127,36.243 41.372,36.409 41.648,36.513C41.924,36.618 42.222,36.659 42.517,36.632C42.864,36.632 43.945,36.605 44.421,36.007C44.499,35.912 44.566,35.808 44.62,35.699L44.673,36.496L46.424,36.496L46.424,31.222C46.45,30.539 46.371,29.857 46.191,29.196C46.127,28.977 46.017,28.772 45.867,28.594C45.717,28.417 45.531,28.27 45.32,28.163ZM44.54,33.256C44.589,33.736 44.503,34.219 44.292,34.656C44.189,34.783 44.048,34.876 43.887,34.924C43.706,34.972 43.519,34.995 43.331,34.992C43.037,35.028 42.74,34.953 42.503,34.783C42.343,34.523 42.277,34.221 42.312,33.922C42.302,33.681 42.327,33.44 42.388,33.206C42.407,33.139 42.44,33.076 42.486,33.021C42.532,32.966 42.59,32.921 42.655,32.889C42.834,32.821 43.028,32.791 43.221,32.803L44.54,32.803L44.54,33.256Z" />
<path
android:fillColor="#ffffff"
android:pathData="M60.474,28.645C60.438,28.584 60.398,28.526 60.354,28.471C59.777,27.736 58.401,27.846 58.401,27.846C57.946,27.832 57.499,27.964 57.126,28.222C56.75,28.573 56.507,29.042 56.438,29.55C56.168,31.328 56.168,33.136 56.438,34.915C56.504,35.417 56.74,35.882 57.108,36.233C57.509,36.52 58,36.659 58.493,36.623C58.493,36.623 58.521,36.623 58.539,36.623C58.539,36.623 59.782,36.72 60.386,36.104C60.398,36.607 60.364,37.11 60.285,37.606C60.264,37.738 60.212,37.864 60.131,37.971C60.051,38.078 59.945,38.164 59.823,38.222C59.439,38.351 59.033,38.406 58.627,38.382L56.72,38.35L56.72,39.788C57.071,39.853 57.426,39.9 57.782,39.93C58.221,39.972 58.636,39.99 59.034,39.99C59.773,40.044 60.512,39.884 61.162,39.53C61.39,39.374 61.583,39.174 61.732,38.943C61.88,38.71 61.98,38.451 62.026,38.18C62.174,37.297 62.237,36.402 62.215,35.507L62.215,27.979L60.516,27.979L60.474,28.645ZM60.262,34.221C60.233,34.452 60.118,34.664 59.938,34.813C59.729,34.939 59.486,34.999 59.241,34.984C58.968,35.006 58.694,34.95 58.452,34.823C58.263,34.636 58.153,34.384 58.147,34.12C58.042,32.876 58.042,31.625 58.147,30.381C58.153,30.125 58.259,29.882 58.442,29.701C58.69,29.579 58.966,29.526 59.241,29.55C59.502,29.529 59.761,29.599 59.975,29.747C60.166,29.947 60.28,30.207 60.299,30.482C60.369,31.072 60.396,31.666 60.382,32.26C60.405,32.916 60.364,33.573 60.262,34.221Z" />
<path
android:fillColor="#ffffff"
android:pathData="M69.304,28.124C68.702,27.787 68.01,27.636 67.319,27.691C66.601,27.642 65.884,27.802 65.259,28.152C64.804,28.473 64.497,28.959 64.408,29.502C64.259,30.388 64.196,31.286 64.219,32.184C64.195,33.083 64.261,33.983 64.417,34.87C64.465,35.139 64.568,35.395 64.72,35.623C64.872,35.851 65.07,36.046 65.301,36.197C65.961,36.531 66.701,36.681 67.441,36.631C67.913,36.63 68.384,36.603 68.852,36.552C69.241,36.517 69.626,36.453 70.005,36.359L70.005,34.916C69.139,35.004 68.429,35.05 67.874,35.05C67.464,35.074 67.053,35.025 66.66,34.907C66.54,34.857 66.433,34.778 66.351,34.677C66.269,34.577 66.214,34.458 66.19,34.331C66.106,33.822 66.071,33.307 66.087,32.792L70.193,32.792L70.193,32.165C70.215,31.241 70.167,30.317 70.047,29.4C69.98,28.896 69.713,28.438 69.304,28.124ZM68.41,31.428L66.087,31.428C66.086,30.95 66.119,30.473 66.186,30C66.195,29.887 66.228,29.777 66.283,29.678C66.338,29.579 66.414,29.492 66.505,29.424C66.988,29.233 67.527,29.233 68.01,29.424C68.1,29.494 68.173,29.583 68.227,29.682C68.28,29.782 68.312,29.892 68.321,30.004C68.394,30.475 68.424,30.952 68.41,31.428Z" />
<path
android:fillColor="#ffffff"
android:pathData="M81.818,29.12C81.739,28.745 81.527,28.406 81.218,28.161C80.849,27.928 80.409,27.817 79.966,27.844C79.966,27.844 79.924,27.844 79.895,27.844C79.895,27.844 78.467,27.767 77.877,28.464C77.813,28.542 77.755,28.625 77.705,28.713C77.61,28.478 77.445,28.275 77.229,28.129C76.858,27.924 76.431,27.826 76.001,27.848C76.001,27.848 74.573,27.767 73.983,28.468C73.917,28.545 73.86,28.629 73.812,28.717L73.764,27.984L72.027,27.984L72.027,36.48L73.931,36.48L73.931,32.051C73.921,31.52 73.942,30.988 73.993,30.459C74.005,30.198 74.107,29.949 74.283,29.749C74.493,29.584 74.764,29.506 75.035,29.531C75.276,29.513 75.518,29.557 75.735,29.658C75.815,29.731 75.879,29.819 75.923,29.916C75.967,30.013 75.991,30.118 75.992,30.224C76.042,30.747 76.063,31.272 76.054,31.798L76.054,36.48L77.958,36.48L77.958,32.051C77.958,31.314 77.958,30.785 78.015,30.459C78.028,30.197 78.133,29.946 78.315,29.749C78.53,29.586 78.802,29.508 79.076,29.531C79.319,29.513 79.562,29.557 79.781,29.658C79.86,29.732 79.923,29.82 79.966,29.917C80.01,30.014 80.033,30.118 80.033,30.224C80.086,30.747 80.106,31.272 80.095,31.798L80.095,36.48L81.999,36.48L81.999,30.884C82.009,30.291 81.949,29.699 81.818,29.12Z" />
<path
android:fillColor="#ffffff"
android:pathData="M53.781,28.171C53.383,27.928 52.913,27.814 52.44,27.845C52.44,27.845 52.402,27.845 52.382,27.845C52.382,27.845 50.936,27.773 50.343,28.465C50.301,28.514 50.262,28.565 50.227,28.619L50.184,27.981L48.424,27.981L48.424,36.48L50.353,36.48L50.353,32.049C50.342,31.521 50.363,30.992 50.415,30.465C50.429,30.203 50.538,29.952 50.724,29.755C50.957,29.584 51.25,29.503 51.543,29.529C51.8,29.511 52.057,29.555 52.291,29.655C52.378,29.728 52.448,29.816 52.497,29.915C52.546,30.014 52.572,30.121 52.575,30.23C52.63,30.75 52.653,31.273 52.643,31.796L52.643,36.48L54.571,36.48L54.571,30.963C54.587,30.356 54.533,29.75 54.412,29.153C54.333,28.765 54.109,28.417 53.781,28.171Z" />
<path
android:fillColor="#ffffff"
android:pathData="M38.128,25.44L36.175,25.44L36.175,27.877L34.96,27.877L34.96,29.434L36.175,29.434L36.175,36.48L38.128,36.48L38.128,29.434L39.614,29.434L39.614,27.877L38.128,27.877L38.128,25.44Z" />
</vector>

View file

@ -0,0 +1,108 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="214dp"
android:height="130dp"
android:viewportWidth="214"
android:viewportHeight="130">
<path
android:fillColor="#E4E4E4"
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z" />
<path
android:fillColor="#00000000"
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z"
android:strokeWidth="0.5">
<aapt:attr name="android:strokeColor">
<gradient
android:endX="216.304"
android:endY="122.149"
android:startX="-1.98014"
android:startY="-1.3567"
android:type="linear">
<item
android:color="#FFDCDCDC"
android:offset="0" />
<item
android:color="#FFDEDEDE"
android:offset="0.9999" />
<item
android:color="#00000000"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#707070"
android:pathData="M25.274,26.4L25.274,32.64L28.079,32.64C28.188,32.639 28.292,32.596 28.369,32.522C28.446,32.447 28.49,32.346 28.491,32.241L28.491,26.4L25.274,26.4Z" />
<path
android:fillColor="#707070"
android:pathData="M19,32.241C19.001,32.346 19.045,32.447 19.122,32.522C19.199,32.596 19.303,32.639 19.412,32.64L22.217,32.64L22.217,26.4L19,26.4L19,32.241Z" />
<path
android:fillColor="#707070"
android:pathData="M28.491,23.2L28.491,20.409C28.491,20.301 28.448,20.197 28.372,20.12C28.296,20.043 28.194,20 28.086,20L19.405,20C19.297,20 19.195,20.043 19.119,20.12C19.043,20.197 19,20.301 19,20.409L19,23.2L28.491,23.2Z" />
<path
android:fillColor="#707070"
android:pathData="M43.503,24.163C42.922,23.921 42.292,23.812 41.661,23.846C40.827,23.844 39.996,23.934 39.183,24.113L39.183,25.532C39.832,25.455 40.483,25.412 41.136,25.4C41.486,25.385 41.836,25.418 42.177,25.5C42.286,25.532 42.385,25.59 42.465,25.669C42.546,25.748 42.605,25.846 42.637,25.953C42.74,26.334 42.783,26.728 42.766,27.122L42.766,27.539C41.997,27.498 41.385,27.476 40.965,27.476C40.519,27.454 40.073,27.539 39.667,27.725C39.347,27.903 39.109,28.195 38.999,28.54C38.847,29.044 38.779,29.569 38.796,30.094C38.74,30.771 38.901,31.448 39.257,32.029C39.445,32.243 39.683,32.409 39.95,32.513C40.216,32.618 40.505,32.659 40.791,32.632C41.127,32.632 42.172,32.605 42.633,32.007C42.709,31.912 42.774,31.808 42.826,31.698L42.877,32.496L44.572,32.496L44.572,27.222C44.596,26.539 44.52,25.857 44.346,25.196C44.284,24.977 44.178,24.772 44.033,24.594C43.888,24.417 43.708,24.27 43.503,24.163ZM42.748,29.256C42.796,29.736 42.713,30.219 42.508,30.656C42.409,30.782 42.272,30.876 42.117,30.924C41.942,30.972 41.76,30.995 41.578,30.992C41.294,31.028 41.006,30.953 40.777,30.783C40.623,30.523 40.558,30.221 40.592,29.922C40.582,29.681 40.607,29.44 40.666,29.206C40.684,29.139 40.716,29.075 40.761,29.021C40.805,28.966 40.861,28.921 40.924,28.889C41.098,28.821 41.285,28.791 41.472,28.803L42.748,28.803L42.748,29.256Z" />
<path
android:fillColor="#707070"
android:pathData="M58.169,24.645C58.133,24.584 58.095,24.526 58.053,24.471C57.494,23.736 56.162,23.846 56.162,23.846C55.722,23.831 55.29,23.964 54.929,24.222C54.564,24.573 54.329,25.042 54.263,25.55C54.001,27.328 54.001,29.136 54.263,30.914C54.326,31.417 54.555,31.882 54.911,32.233C55.299,32.52 55.774,32.659 56.251,32.623C56.251,32.623 56.278,32.623 56.296,32.623C56.296,32.623 57.498,32.72 58.084,32.104C58.095,32.607 58.062,33.11 57.985,33.606C57.966,33.738 57.915,33.864 57.837,33.971C57.759,34.078 57.656,34.164 57.539,34.222C57.166,34.351 56.773,34.406 56.381,34.382L54.535,34.35L54.535,35.788C54.876,35.853 55.219,35.9 55.563,35.93C55.988,35.972 56.39,35.99 56.774,35.99C57.49,36.044 58.205,35.884 58.834,35.53C59.055,35.374 59.242,35.174 59.386,34.943C59.529,34.71 59.626,34.451 59.67,34.18C59.813,33.297 59.875,32.402 59.853,31.507L59.853,23.979L58.209,23.979L58.169,24.645ZM57.963,30.221C57.936,30.452 57.824,30.664 57.65,30.813C57.447,30.94 57.212,30.999 56.975,30.983C56.711,31.006 56.446,30.95 56.211,30.823C56.028,30.636 55.923,30.385 55.916,30.12C55.815,28.876 55.815,27.625 55.916,26.381C55.923,26.125 56.025,25.882 56.202,25.701C56.442,25.579 56.709,25.526 56.975,25.55C57.227,25.529 57.479,25.599 57.686,25.747C57.87,25.947 57.981,26.207 57.999,26.482C58.066,27.072 58.093,27.666 58.079,28.26C58.101,28.916 58.062,29.573 57.963,30.221Z" />
<path
android:fillColor="#707070"
android:pathData="M66.713,24.124C66.131,23.787 65.461,23.636 64.793,23.691C64.098,23.642 63.404,23.802 62.799,24.152C62.358,24.473 62.062,24.959 61.975,25.502C61.831,26.388 61.77,27.286 61.793,28.184C61.769,29.083 61.833,29.983 61.984,30.87C62.031,31.139 62.131,31.395 62.278,31.623C62.425,31.851 62.616,32.046 62.84,32.197C63.478,32.531 64.194,32.681 64.911,32.631C65.367,32.63 65.823,32.603 66.276,32.552C66.653,32.517 67.025,32.453 67.392,32.359L67.392,30.916C66.554,31.004 65.867,31.05 65.33,31.05C64.933,31.074 64.535,31.025 64.155,30.907C64.038,30.857 63.936,30.778 63.856,30.677C63.777,30.577 63.723,30.458 63.7,30.331C63.618,29.823 63.585,29.307 63.6,28.792L67.574,28.792L67.574,28.165C67.596,27.241 67.548,26.317 67.433,25.4C67.368,24.896 67.109,24.438 66.713,24.124ZM65.849,27.428L63.6,27.428C63.6,26.95 63.632,26.473 63.696,26C63.704,25.887 63.736,25.777 63.79,25.678C63.843,25.579 63.917,25.492 64.005,25.424C64.473,25.233 64.994,25.233 65.462,25.424C65.548,25.494 65.619,25.583 65.671,25.683C65.723,25.782 65.754,25.892 65.762,26.004C65.833,26.475 65.862,26.952 65.849,27.428Z" />
<path
android:fillColor="#707070"
android:pathData="M78.824,25.12C78.747,24.744 78.542,24.406 78.243,24.161C77.886,23.928 77.46,23.817 77.032,23.844C77.032,23.844 76.991,23.844 76.963,23.844C76.963,23.844 75.581,23.767 75.01,24.464C74.948,24.542 74.892,24.625 74.844,24.712C74.752,24.478 74.592,24.275 74.383,24.129C74.024,23.923 73.61,23.826 73.195,23.848C73.195,23.848 71.813,23.767 71.242,24.468C71.178,24.545 71.122,24.629 71.076,24.717L71.03,23.984L69.349,23.984L69.349,32.48L71.191,32.48L71.191,28.051C71.182,27.52 71.201,26.988 71.251,26.459C71.263,26.198 71.362,25.949 71.532,25.749C71.736,25.584 71.997,25.506 72.26,25.531C72.493,25.513 72.727,25.557 72.937,25.658C73.015,25.731 73.076,25.819 73.119,25.916C73.162,26.013 73.185,26.118 73.186,26.223C73.234,26.747 73.254,27.272 73.245,27.798L73.245,32.48L75.088,32.48L75.088,28.051C75.088,27.314 75.088,26.785 75.143,26.459C75.156,26.197 75.258,25.946 75.434,25.749C75.642,25.586 75.906,25.508 76.171,25.531C76.405,25.513 76.641,25.557 76.852,25.658C76.929,25.732 76.99,25.82 77.032,25.917C77.074,26.014 77.096,26.118 77.096,26.223C77.147,26.747 77.167,27.272 77.156,27.798L77.156,32.48L78.999,32.48L78.999,26.884C79.009,26.291 78.951,25.699 78.824,25.12Z" />
<path
android:fillColor="#707070"
android:pathData="M51.691,24.171C51.307,23.928 50.851,23.814 50.394,23.845C50.394,23.845 50.356,23.845 50.338,23.845C50.338,23.845 48.938,23.773 48.364,24.465C48.323,24.514 48.286,24.565 48.252,24.619L48.21,23.981L46.507,23.981L46.507,32.48L48.373,32.48L48.373,28.049C48.363,27.521 48.384,26.992 48.434,26.465C48.448,26.203 48.553,25.952 48.733,25.755C48.958,25.584 49.242,25.503 49.526,25.528C49.774,25.511 50.023,25.555 50.249,25.655C50.333,25.728 50.401,25.816 50.448,25.915C50.496,26.014 50.521,26.121 50.524,26.23C50.578,26.75 50.6,27.273 50.59,27.796L50.59,32.48L52.456,32.48L52.456,26.963C52.471,26.356 52.419,25.749 52.302,25.153C52.226,24.765 52.009,24.417 51.691,24.171Z" />
<path
android:fillColor="#707070"
android:pathData="M36.543,21.44L34.653,21.44L34.653,23.877L33.477,23.877L33.477,25.434L34.653,25.434L34.653,32.48L36.543,32.48L36.543,25.434L37.981,25.434L37.981,23.877L36.543,23.877L36.543,21.44Z" />
<path
android:fillColor="#707070"
android:pathData="M23.463,105.118L21.826,104.112L21.904,106H21.024L21.087,104.112L19.441,105.125L19.02,104.335L20.707,103.493L19.016,102.645L19.428,101.882L21.08,102.875L21.001,101H21.874L21.818,102.882L23.451,101.882L23.885,102.645L22.184,103.5L23.895,104.342L23.463,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M32.161,105.118L30.524,104.112L30.602,106H29.722L29.785,104.112L28.139,105.125L27.718,104.335L29.405,103.493L27.715,102.645L28.126,101.882L29.778,102.875L29.699,101H30.573L30.517,102.882L32.149,101.882L32.583,102.645L30.883,103.5L32.593,104.342L32.161,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M40.86,105.118L39.222,104.112L39.3,106H38.42L38.483,104.112L36.837,105.125L36.416,104.335L38.104,103.493L36.413,102.645L36.825,101.882L38.476,102.875L38.398,101H39.271L39.215,102.882L40.847,101.882L41.282,102.645L39.581,103.5L41.292,104.342L40.86,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M49.558,105.118L47.92,104.112L47.998,106H47.119L47.181,104.112L45.535,105.125L45.114,104.335L46.802,103.493L45.111,102.645L45.523,101.882L47.174,102.875L47.096,101H47.969L47.913,102.882L49.546,101.882L49.98,102.645L48.279,103.5L49.99,104.342L49.558,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M63.14,105.118L61.502,104.112L61.58,106H60.701L60.763,104.112L59.117,105.125L58.696,104.335L60.384,103.493L58.693,102.645L59.105,101.882L60.756,102.875L60.678,101H61.551L61.495,102.882L63.128,101.882L63.562,102.645L61.861,103.5L63.572,104.342L63.14,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M71.838,105.118L70.2,104.112L70.279,106H69.399L69.462,104.112L67.815,105.125L67.394,104.335L69.082,103.493L67.391,102.645L67.803,101.882L69.454,102.875L69.376,101H70.249L70.193,102.882L71.826,101.882L72.26,102.645L70.559,103.5L72.27,104.342L71.838,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M80.536,105.118L78.898,104.112L78.977,106H78.097L78.16,104.112L76.514,105.125L76.093,104.335L77.78,103.493L76.089,102.645L76.501,101.882L78.152,102.875L78.074,101H78.947L78.891,102.882L80.524,101.882L80.958,102.645L79.257,103.5L80.968,104.342L80.536,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M89.234,105.118L87.597,104.112L87.675,106H86.795L86.858,104.112L85.212,105.125L84.791,104.335L86.478,103.493L84.788,102.645L85.2,101.882L86.851,102.875L86.772,101H87.646L87.59,102.882L89.222,101.882L89.657,102.645L87.956,103.5L89.666,104.342L89.234,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M102.816,105.118L101.179,104.112L101.257,106H100.377L100.44,104.112L98.794,105.125L98.373,104.335L100.06,103.493L98.369,102.645L98.781,101.882L100.433,102.875L100.354,101H101.227L101.171,102.882L102.804,101.882L103.238,102.645L101.538,103.5L103.248,104.342L102.816,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M111.515,105.118L109.877,104.112L109.955,106H109.075L109.138,104.112L107.492,105.125L107.071,104.335L108.758,103.493L107.068,102.645L107.48,101.882L109.131,102.875L109.053,101H109.926L109.87,102.882L111.502,101.882L111.937,102.645L110.236,103.5L111.947,104.342L111.515,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M120.213,105.118L118.575,104.112L118.653,106H117.774L117.836,104.112L116.19,105.125L115.769,104.335L117.457,103.493L115.766,102.645L116.178,101.882L117.829,102.875L117.751,101H118.624L118.568,102.882L120.201,101.882L120.635,102.645L118.934,103.5L120.645,104.342L120.213,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M128.911,105.118L127.273,104.112L127.352,106H126.472L126.535,104.112L124.888,105.125L124.467,104.335L126.155,103.493L124.464,102.645L124.876,101.882L126.527,102.875L126.449,101H127.322L127.266,102.882L128.899,101.882L129.333,102.645L127.632,103.5L129.343,104.342L128.911,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M142.493,105.118L140.855,104.112L140.933,106H140.054L140.116,104.112L138.47,105.125L138.049,104.335L139.737,103.493L138.046,102.645L138.458,101.882L140.109,102.875L140.031,101H140.904L140.848,102.882L142.481,101.882L142.915,102.645L141.214,103.5L142.925,104.342L142.493,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M151.191,105.118L149.553,104.112L149.632,106H148.752L148.815,104.112L147.169,105.125L146.748,104.335L148.435,103.493L146.744,102.645L147.156,101.882L148.807,102.875L148.729,101H149.602L149.546,102.882L151.179,101.882L151.613,102.645L149.912,103.5L151.623,104.342L151.191,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M159.889,105.118L158.252,104.112L158.33,106H157.45L157.513,104.112L155.867,105.125L155.446,104.335L157.133,103.493L155.443,102.645L155.854,101.882L157.506,102.875L157.427,101H158.3L158.244,102.882L159.877,101.882L160.311,102.645L158.611,103.5L160.321,104.342L159.889,105.118Z" />
<path
android:fillColor="#707070"
android:pathData="M168.588,105.118L166.95,104.112L167.028,106H166.148L166.211,104.112L164.565,105.125L164.144,104.335L165.831,103.493L164.141,102.645L164.553,101.882L166.204,102.875L166.126,101H166.999L166.943,102.882L168.575,101.882L169.01,102.645L167.309,103.5L169.02,104.342L168.588,105.118Z" />
</vector>

View file

@ -1,102 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="214dp"
android:height="130dp"
android:viewportWidth="214"
android:viewportHeight="130">
<path
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z"
android:fillColor="#E4E4E4"/>
<path
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z"
android:strokeWidth="0.5"
android:fillColor="#00000000">
<aapt:attr name="android:strokeColor">
<gradient
android:startY="-1.3567"
android:startX="-1.98014"
android:endY="122.149"
android:endX="216.304"
android:type="linear">
<item android:offset="0" android:color="#FFDCDCDC"/>
<item android:offset="0.9999" android:color="#FFDEDEDE"/>
<item android:offset="1" android:color="#00000000"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M25.274,26.4L25.274,32.64L28.079,32.64C28.188,32.639 28.292,32.596 28.369,32.522C28.446,32.447 28.49,32.346 28.491,32.241L28.491,26.4L25.274,26.4Z"
android:fillColor="#707070"/>
<path
android:pathData="M19,32.241C19.001,32.346 19.045,32.447 19.122,32.522C19.199,32.596 19.303,32.639 19.412,32.64L22.217,32.64L22.217,26.4L19,26.4L19,32.241Z"
android:fillColor="#707070"/>
<path
android:pathData="M28.491,23.2L28.491,20.409C28.491,20.301 28.448,20.197 28.372,20.12C28.296,20.043 28.194,20 28.086,20L19.405,20C19.297,20 19.195,20.043 19.119,20.12C19.043,20.197 19,20.301 19,20.409L19,23.2L28.491,23.2Z"
android:fillColor="#707070"/>
<path
android:pathData="M43.503,24.163C42.922,23.921 42.292,23.812 41.661,23.846C40.827,23.844 39.996,23.934 39.183,24.113L39.183,25.532C39.832,25.455 40.483,25.412 41.136,25.4C41.486,25.385 41.836,25.418 42.177,25.5C42.286,25.532 42.385,25.59 42.465,25.669C42.546,25.748 42.605,25.846 42.637,25.953C42.74,26.334 42.783,26.728 42.766,27.122L42.766,27.539C41.997,27.498 41.385,27.476 40.965,27.476C40.519,27.454 40.073,27.539 39.667,27.725C39.347,27.903 39.109,28.195 38.999,28.54C38.847,29.044 38.779,29.569 38.796,30.094C38.74,30.771 38.901,31.448 39.257,32.029C39.445,32.243 39.683,32.409 39.95,32.513C40.216,32.618 40.505,32.659 40.791,32.632C41.127,32.632 42.172,32.605 42.633,32.007C42.709,31.912 42.774,31.808 42.826,31.698L42.877,32.496L44.572,32.496L44.572,27.222C44.596,26.539 44.52,25.857 44.346,25.196C44.284,24.977 44.178,24.772 44.033,24.594C43.888,24.417 43.708,24.27 43.503,24.163ZM42.748,29.256C42.796,29.736 42.713,30.219 42.508,30.656C42.409,30.782 42.272,30.876 42.117,30.924C41.942,30.972 41.76,30.995 41.578,30.992C41.294,31.028 41.006,30.953 40.777,30.783C40.623,30.523 40.558,30.221 40.592,29.922C40.582,29.681 40.607,29.44 40.666,29.206C40.684,29.139 40.716,29.075 40.761,29.021C40.805,28.966 40.861,28.921 40.924,28.889C41.098,28.821 41.285,28.791 41.472,28.803L42.748,28.803L42.748,29.256Z"
android:fillColor="#707070"/>
<path
android:pathData="M58.169,24.645C58.133,24.584 58.095,24.526 58.053,24.471C57.494,23.736 56.162,23.846 56.162,23.846C55.722,23.831 55.29,23.964 54.929,24.222C54.564,24.573 54.329,25.042 54.263,25.55C54.001,27.328 54.001,29.136 54.263,30.914C54.326,31.417 54.555,31.882 54.911,32.233C55.299,32.52 55.774,32.659 56.251,32.623C56.251,32.623 56.278,32.623 56.296,32.623C56.296,32.623 57.498,32.72 58.084,32.104C58.095,32.607 58.062,33.11 57.985,33.606C57.966,33.738 57.915,33.864 57.837,33.971C57.759,34.078 57.656,34.164 57.539,34.222C57.166,34.351 56.773,34.406 56.381,34.382L54.535,34.35L54.535,35.788C54.876,35.853 55.219,35.9 55.563,35.93C55.988,35.972 56.39,35.99 56.774,35.99C57.49,36.044 58.205,35.884 58.834,35.53C59.055,35.374 59.242,35.174 59.386,34.943C59.529,34.71 59.626,34.451 59.67,34.18C59.813,33.297 59.875,32.402 59.853,31.507L59.853,23.979L58.209,23.979L58.169,24.645ZM57.963,30.221C57.936,30.452 57.824,30.664 57.65,30.813C57.447,30.94 57.212,30.999 56.975,30.983C56.711,31.006 56.446,30.95 56.211,30.823C56.028,30.636 55.923,30.385 55.916,30.12C55.815,28.876 55.815,27.625 55.916,26.381C55.923,26.125 56.025,25.882 56.202,25.701C56.442,25.579 56.709,25.526 56.975,25.55C57.227,25.529 57.479,25.599 57.686,25.747C57.87,25.947 57.981,26.207 57.999,26.482C58.066,27.072 58.093,27.666 58.079,28.26C58.101,28.916 58.062,29.573 57.963,30.221Z"
android:fillColor="#707070"/>
<path
android:pathData="M66.713,24.124C66.131,23.787 65.461,23.636 64.793,23.691C64.098,23.642 63.404,23.802 62.799,24.152C62.358,24.473 62.062,24.959 61.975,25.502C61.831,26.388 61.77,27.286 61.793,28.184C61.769,29.083 61.833,29.983 61.984,30.87C62.031,31.139 62.131,31.395 62.278,31.623C62.425,31.851 62.616,32.046 62.84,32.197C63.478,32.531 64.194,32.681 64.911,32.631C65.367,32.63 65.823,32.603 66.276,32.552C66.653,32.517 67.025,32.453 67.392,32.359L67.392,30.916C66.554,31.004 65.867,31.05 65.33,31.05C64.933,31.074 64.535,31.025 64.155,30.907C64.038,30.857 63.936,30.778 63.856,30.677C63.777,30.577 63.723,30.458 63.7,30.331C63.618,29.823 63.585,29.307 63.6,28.792L67.574,28.792L67.574,28.165C67.596,27.241 67.548,26.317 67.433,25.4C67.368,24.896 67.109,24.438 66.713,24.124ZM65.849,27.428L63.6,27.428C63.6,26.95 63.632,26.473 63.696,26C63.704,25.887 63.736,25.777 63.79,25.678C63.843,25.579 63.917,25.492 64.005,25.424C64.473,25.233 64.994,25.233 65.462,25.424C65.548,25.494 65.619,25.583 65.671,25.683C65.723,25.782 65.754,25.892 65.762,26.004C65.833,26.475 65.862,26.952 65.849,27.428Z"
android:fillColor="#707070"/>
<path
android:pathData="M78.824,25.12C78.747,24.744 78.542,24.406 78.243,24.161C77.886,23.928 77.46,23.817 77.032,23.844C77.032,23.844 76.991,23.844 76.963,23.844C76.963,23.844 75.581,23.767 75.01,24.464C74.948,24.542 74.892,24.625 74.844,24.712C74.752,24.478 74.592,24.275 74.383,24.129C74.024,23.923 73.61,23.826 73.195,23.848C73.195,23.848 71.813,23.767 71.242,24.468C71.178,24.545 71.122,24.629 71.076,24.717L71.03,23.984L69.349,23.984L69.349,32.48L71.191,32.48L71.191,28.051C71.182,27.52 71.201,26.988 71.251,26.459C71.263,26.198 71.362,25.949 71.532,25.749C71.736,25.584 71.997,25.506 72.26,25.531C72.493,25.513 72.727,25.557 72.937,25.658C73.015,25.731 73.076,25.819 73.119,25.916C73.162,26.013 73.185,26.118 73.186,26.223C73.234,26.747 73.254,27.272 73.245,27.798L73.245,32.48L75.088,32.48L75.088,28.051C75.088,27.314 75.088,26.785 75.143,26.459C75.156,26.197 75.258,25.946 75.434,25.749C75.642,25.586 75.906,25.508 76.171,25.531C76.405,25.513 76.641,25.557 76.852,25.658C76.929,25.732 76.99,25.82 77.032,25.917C77.074,26.014 77.096,26.118 77.096,26.223C77.147,26.747 77.167,27.272 77.156,27.798L77.156,32.48L78.999,32.48L78.999,26.884C79.009,26.291 78.951,25.699 78.824,25.12Z"
android:fillColor="#707070"/>
<path
android:pathData="M51.691,24.171C51.307,23.928 50.851,23.814 50.394,23.845C50.394,23.845 50.356,23.845 50.338,23.845C50.338,23.845 48.938,23.773 48.364,24.465C48.323,24.514 48.286,24.565 48.252,24.619L48.21,23.981L46.507,23.981L46.507,32.48L48.373,32.48L48.373,28.049C48.363,27.521 48.384,26.992 48.434,26.465C48.448,26.203 48.553,25.952 48.733,25.755C48.958,25.584 49.242,25.503 49.526,25.528C49.774,25.511 50.023,25.555 50.249,25.655C50.333,25.728 50.401,25.816 50.448,25.915C50.496,26.014 50.521,26.121 50.524,26.23C50.578,26.75 50.6,27.273 50.59,27.796L50.59,32.48L52.456,32.48L52.456,26.963C52.471,26.356 52.419,25.749 52.302,25.153C52.226,24.765 52.009,24.417 51.691,24.171Z"
android:fillColor="#707070"/>
<path
android:pathData="M36.543,21.44L34.653,21.44L34.653,23.877L33.477,23.877L33.477,25.434L34.653,25.434L34.653,32.48L36.543,32.48L36.543,25.434L37.981,25.434L37.981,23.877L36.543,23.877L36.543,21.44Z"
android:fillColor="#707070"/>
<path
android:pathData="M23.463,105.118L21.826,104.112L21.904,106H21.024L21.087,104.112L19.441,105.125L19.02,104.335L20.707,103.493L19.016,102.645L19.428,101.882L21.08,102.875L21.001,101H21.874L21.818,102.882L23.451,101.882L23.885,102.645L22.184,103.5L23.895,104.342L23.463,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M32.161,105.118L30.524,104.112L30.602,106H29.722L29.785,104.112L28.139,105.125L27.718,104.335L29.405,103.493L27.715,102.645L28.126,101.882L29.778,102.875L29.699,101H30.573L30.517,102.882L32.149,101.882L32.583,102.645L30.883,103.5L32.593,104.342L32.161,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M40.86,105.118L39.222,104.112L39.3,106H38.42L38.483,104.112L36.837,105.125L36.416,104.335L38.104,103.493L36.413,102.645L36.825,101.882L38.476,102.875L38.398,101H39.271L39.215,102.882L40.847,101.882L41.282,102.645L39.581,103.5L41.292,104.342L40.86,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M49.558,105.118L47.92,104.112L47.998,106H47.119L47.181,104.112L45.535,105.125L45.114,104.335L46.802,103.493L45.111,102.645L45.523,101.882L47.174,102.875L47.096,101H47.969L47.913,102.882L49.546,101.882L49.98,102.645L48.279,103.5L49.99,104.342L49.558,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M63.14,105.118L61.502,104.112L61.58,106H60.701L60.763,104.112L59.117,105.125L58.696,104.335L60.384,103.493L58.693,102.645L59.105,101.882L60.756,102.875L60.678,101H61.551L61.495,102.882L63.128,101.882L63.562,102.645L61.861,103.5L63.572,104.342L63.14,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M71.838,105.118L70.2,104.112L70.279,106H69.399L69.462,104.112L67.815,105.125L67.394,104.335L69.082,103.493L67.391,102.645L67.803,101.882L69.454,102.875L69.376,101H70.249L70.193,102.882L71.826,101.882L72.26,102.645L70.559,103.5L72.27,104.342L71.838,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M80.536,105.118L78.898,104.112L78.977,106H78.097L78.16,104.112L76.514,105.125L76.093,104.335L77.78,103.493L76.089,102.645L76.501,101.882L78.152,102.875L78.074,101H78.947L78.891,102.882L80.524,101.882L80.958,102.645L79.257,103.5L80.968,104.342L80.536,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M89.234,105.118L87.597,104.112L87.675,106H86.795L86.858,104.112L85.212,105.125L84.791,104.335L86.478,103.493L84.788,102.645L85.2,101.882L86.851,102.875L86.772,101H87.646L87.59,102.882L89.222,101.882L89.657,102.645L87.956,103.5L89.666,104.342L89.234,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M102.816,105.118L101.179,104.112L101.257,106H100.377L100.44,104.112L98.794,105.125L98.373,104.335L100.06,103.493L98.369,102.645L98.781,101.882L100.433,102.875L100.354,101H101.227L101.171,102.882L102.804,101.882L103.238,102.645L101.538,103.5L103.248,104.342L102.816,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M111.515,105.118L109.877,104.112L109.955,106H109.075L109.138,104.112L107.492,105.125L107.071,104.335L108.758,103.493L107.068,102.645L107.48,101.882L109.131,102.875L109.053,101H109.926L109.87,102.882L111.502,101.882L111.937,102.645L110.236,103.5L111.947,104.342L111.515,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M120.213,105.118L118.575,104.112L118.653,106H117.774L117.836,104.112L116.19,105.125L115.769,104.335L117.457,103.493L115.766,102.645L116.178,101.882L117.829,102.875L117.751,101H118.624L118.568,102.882L120.201,101.882L120.635,102.645L118.934,103.5L120.645,104.342L120.213,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M128.911,105.118L127.273,104.112L127.352,106H126.472L126.535,104.112L124.888,105.125L124.467,104.335L126.155,103.493L124.464,102.645L124.876,101.882L126.527,102.875L126.449,101H127.322L127.266,102.882L128.899,101.882L129.333,102.645L127.632,103.5L129.343,104.342L128.911,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M142.493,105.118L140.855,104.112L140.933,106H140.054L140.116,104.112L138.47,105.125L138.049,104.335L139.737,103.493L138.046,102.645L138.458,101.882L140.109,102.875L140.031,101H140.904L140.848,102.882L142.481,101.882L142.915,102.645L141.214,103.5L142.925,104.342L142.493,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M151.191,105.118L149.553,104.112L149.632,106H148.752L148.815,104.112L147.169,105.125L146.748,104.335L148.435,103.493L146.744,102.645L147.156,101.882L148.807,102.875L148.729,101H149.602L149.546,102.882L151.179,101.882L151.613,102.645L149.912,103.5L151.623,104.342L151.191,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M159.889,105.118L158.252,104.112L158.33,106H157.45L157.513,104.112L155.867,105.125L155.446,104.335L157.133,103.493L155.443,102.645L155.854,101.882L157.506,102.875L157.427,101H158.3L158.244,102.882L159.877,101.882L160.311,102.645L158.611,103.5L160.321,104.342L159.889,105.118Z"
android:fillColor="#707070"/>
<path
android:pathData="M168.588,105.118L166.95,104.112L167.028,106H166.148L166.211,104.112L164.565,105.125L164.144,104.335L165.831,103.493L164.141,102.645L164.553,101.882L166.204,102.875L166.126,101H166.999L166.943,102.882L168.575,101.882L169.01,102.645L167.309,103.5L169.02,104.342L168.588,105.118Z"
android:fillColor="#707070"/>
</vector>

View file

@ -1,118 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="249dp"
android:height="150dp"
android:viewportWidth="249"
android:viewportHeight="150">
<path
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z"
android:fillAlpha="0.03"/>
<path
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z"
android:fillAlpha="0.5">
<aapt:attr name="android:fillColor">
<gradient
android:gradientRadius="267.36932"
android:centerX="7.404067"
android:centerY="8.65384"
android:type="radial">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="1" android:color="#19FFFFFF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z"
android:fillColor="#1B1D1C"/>
<path
android:strokeWidth="1"
android:pathData="M10,0.5L238,0.5A9.499,9.501 134.998,0 1,247.501 10L247.517,140A9.499,9.501 134.998,0 1,238.019 149.5L10.019,149.5A9.499,9.501 134.998,0 1,0.517 140L0.501,10A9.499,9.501 134.998,0 1,10 0.5z"
android:fillColor="#00000000">
<aapt:attr name="android:strokeColor">
<gradient
android:startY="3.38345"
android:startX="2.844399"
android:endY="144.774"
android:endX="246.96196"
android:type="linear">
<item android:offset="0" android:color="#FF1A1A1A"/>
<item android:offset="1" android:color="#FF565656"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M28.359,121.942L26.389,120.734L26.472,123H25.41L25.498,120.734L23.505,121.95L23.002,121.003L25.044,119.992L23.009,118.974L23.511,118.058L25.497,119.25L25.415,117H26.468L26.389,119.258L28.365,118.058L28.884,118.974L26.826,120L28.885,121.011L28.359,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M38.855,121.942L36.885,120.734L36.967,123H35.906L35.994,120.734L34.001,121.95L33.498,121.003L35.54,119.992L33.505,118.974L34.007,118.058L35.993,119.25L35.911,117H36.964L36.884,119.258L38.861,118.058L39.38,118.974L37.322,120L39.381,121.011L38.855,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M49.351,121.942L47.381,120.734L47.463,123H46.402L46.49,120.734L44.497,121.95L43.994,121.003L46.035,119.992L44.001,118.974L44.503,118.058L46.489,119.25L46.407,117H47.46L47.38,119.258L49.357,118.058L49.876,118.974L47.818,120L49.877,121.011L49.351,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M59.847,121.942L57.877,120.734L57.959,123H56.897L56.985,120.734L54.993,121.95L54.49,121.003L56.531,119.992L54.497,118.974L54.999,118.058L56.985,119.25L56.902,117H57.956L57.876,119.258L59.853,118.058L60.372,118.974L58.314,120L60.373,121.011L59.847,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M76.235,121.942L74.266,120.734L74.348,123H73.286L73.374,120.734L71.381,121.95L70.879,121.003L72.92,119.992L70.885,118.974L71.387,118.058L73.373,119.25L73.291,117H74.345L74.265,119.258L76.242,118.058L76.761,118.974L74.703,120L76.762,121.011L76.235,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M86.731,121.942L84.761,120.734L84.844,123H83.782L83.87,120.734L81.877,121.95L81.374,121.003L83.416,119.992L81.381,118.974L81.883,118.058L83.869,119.25L83.787,117H84.841L84.761,119.258L86.737,118.058L87.257,118.974L85.199,120L87.258,121.011L86.731,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M97.227,121.942L95.257,120.734L95.34,123H94.278L94.366,120.734L92.373,121.95L91.87,121.003L93.912,119.992L91.877,118.974L92.379,118.058L94.365,119.25L94.283,117H95.337L95.257,119.258L97.233,118.058L97.752,118.974L95.695,120L97.753,121.011L97.227,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M107.723,121.942L105.753,120.734L105.835,123H104.774L104.862,120.734L102.869,121.95L102.366,121.003L104.408,119.992L102.373,118.974L102.875,118.058L104.861,119.25L104.779,117H105.832L105.752,119.258L107.729,118.058L108.248,118.974L106.19,120L108.249,121.011L107.723,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M124.112,121.942L122.142,120.734L122.224,123H121.163L121.251,120.734L119.258,121.95L118.755,121.003L120.796,119.992L118.762,118.974L119.264,118.058L121.25,119.25L121.168,117H122.221L122.141,119.258L124.118,118.058L124.637,118.974L122.579,120L124.638,121.011L124.112,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M134.608,121.942L132.638,120.734L132.72,123H131.659L131.746,120.734L129.754,121.95L129.251,121.003L131.292,119.992L129.258,118.974L129.76,118.058L131.746,119.25L131.664,117H132.717L132.637,119.258L134.614,118.058L135.133,118.974L133.075,120L135.134,121.011L134.608,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M145.104,121.942L143.134,120.734L143.216,123H142.154L142.242,120.734L140.249,121.95L139.747,121.003L141.788,119.992L139.754,118.974L140.256,118.058L142.242,119.25L142.159,117H143.213L143.133,119.258L145.11,118.058L145.629,118.974L143.571,120L145.63,121.011L145.104,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M155.599,121.942L153.63,120.734L153.712,123H152.65L152.738,120.734L150.745,121.95L150.242,121.003L152.284,119.992L150.249,118.974L150.751,118.058L152.737,119.25L152.655,117H153.709L153.629,119.258L155.606,118.058L156.125,118.974L154.067,120L156.126,121.011L155.599,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M171.988,121.942L170.018,120.734L170.101,123H169.039L169.127,120.734L167.134,121.95L166.631,121.003L168.673,119.992L166.638,118.974L167.14,118.058L169.126,119.25L169.044,117H170.098L170.018,119.258L171.994,118.058L172.513,118.974L170.456,120L172.515,121.011L171.988,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M182.484,121.942L180.514,120.734L180.597,123H179.535L179.623,120.734L177.63,121.95L177.127,121.003L179.169,119.992L177.134,118.974L177.636,118.058L179.622,119.25L179.54,117H180.593L180.514,119.258L182.49,118.058L183.009,118.974L180.952,120L183.01,121.011L182.484,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M192.98,121.942L191.01,120.734L191.092,123H190.031L190.119,120.734L188.126,121.95L187.623,121.003L189.665,119.992L187.63,118.974L188.132,118.058L190.118,119.25L190.036,117H191.089L191.009,119.258L192.986,118.058L193.505,118.974L191.447,120L193.506,121.011L192.98,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M203.476,121.942L201.506,120.734L201.588,123H200.527L200.615,120.734L198.622,121.95L198.119,121.003L200.16,119.992L198.126,118.974L198.628,118.058L200.614,119.25L200.532,117H201.585L201.505,119.258L203.482,118.058L204.001,118.974L201.943,120L204.002,121.011L203.476,121.942Z"
android:fillColor="#A5A5A5"/>
<path
android:pathData="M26.483,30.4L26.483,36.64L29.382,36.64C29.494,36.639 29.602,36.596 29.681,36.522C29.761,36.447 29.806,36.346 29.807,36.241L29.807,30.4L26.483,30.4Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M20,36.241C20.001,36.346 20.046,36.447 20.126,36.522C20.205,36.596 20.313,36.639 20.425,36.64L23.324,36.64L23.324,30.4L20,30.4L20,36.241Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M29.807,27.2L29.807,24.409C29.807,24.301 29.763,24.197 29.685,24.12C29.606,24.043 29.5,24 29.389,24L20.418,24C20.307,24 20.201,24.043 20.123,24.12C20.044,24.197 20,24.301 20,24.409L20,27.2L29.807,27.2Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M45.32,28.163C44.719,27.921 44.068,27.812 43.416,27.846C42.555,27.844 41.696,27.934 40.856,28.113L40.856,29.532C41.526,29.455 42.199,29.412 42.874,29.4C43.236,29.385 43.598,29.418 43.949,29.5C44.062,29.532 44.164,29.59 44.247,29.669C44.33,29.748 44.392,29.846 44.425,29.953C44.531,30.334 44.576,30.728 44.558,31.122L44.558,31.539C43.764,31.498 43.131,31.476 42.698,31.476C42.236,31.454 41.775,31.539 41.356,31.725C41.025,31.903 40.779,32.195 40.666,32.54C40.509,33.044 40.438,33.569 40.456,34.095C40.398,34.771 40.564,35.448 40.932,36.029C41.127,36.243 41.372,36.409 41.648,36.513C41.924,36.618 42.222,36.659 42.517,36.632C42.864,36.632 43.945,36.605 44.421,36.007C44.499,35.912 44.566,35.808 44.62,35.699L44.673,36.496L46.424,36.496L46.424,31.222C46.45,30.539 46.371,29.857 46.191,29.196C46.127,28.977 46.017,28.772 45.867,28.594C45.717,28.417 45.531,28.27 45.32,28.163ZM44.54,33.256C44.589,33.736 44.503,34.219 44.292,34.656C44.189,34.783 44.048,34.876 43.887,34.924C43.706,34.972 43.519,34.995 43.331,34.992C43.037,35.028 42.74,34.953 42.503,34.783C42.343,34.523 42.277,34.221 42.312,33.922C42.302,33.681 42.327,33.44 42.388,33.206C42.407,33.139 42.44,33.076 42.486,33.021C42.532,32.966 42.59,32.921 42.655,32.889C42.834,32.821 43.028,32.791 43.221,32.803L44.54,32.803L44.54,33.256Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M60.474,28.645C60.438,28.584 60.398,28.526 60.354,28.471C59.777,27.736 58.401,27.846 58.401,27.846C57.946,27.832 57.499,27.964 57.126,28.222C56.75,28.573 56.507,29.042 56.438,29.55C56.168,31.328 56.168,33.136 56.438,34.915C56.504,35.417 56.74,35.882 57.108,36.233C57.509,36.52 58,36.659 58.493,36.623C58.493,36.623 58.521,36.623 58.539,36.623C58.539,36.623 59.782,36.72 60.386,36.104C60.398,36.607 60.364,37.11 60.285,37.606C60.264,37.738 60.212,37.864 60.131,37.971C60.051,38.078 59.945,38.164 59.823,38.222C59.439,38.351 59.033,38.406 58.627,38.382L56.72,38.35L56.72,39.788C57.071,39.853 57.426,39.9 57.782,39.93C58.221,39.972 58.636,39.99 59.034,39.99C59.773,40.044 60.512,39.884 61.162,39.53C61.39,39.374 61.583,39.174 61.732,38.943C61.88,38.71 61.98,38.451 62.026,38.18C62.174,37.297 62.237,36.402 62.215,35.507L62.215,27.979L60.516,27.979L60.474,28.645ZM60.262,34.221C60.233,34.452 60.118,34.664 59.938,34.813C59.729,34.939 59.486,34.999 59.241,34.984C58.968,35.006 58.694,34.95 58.452,34.823C58.263,34.636 58.153,34.384 58.147,34.12C58.042,32.876 58.042,31.625 58.147,30.381C58.153,30.125 58.259,29.882 58.442,29.701C58.69,29.579 58.966,29.526 59.241,29.55C59.502,29.529 59.761,29.599 59.975,29.747C60.166,29.947 60.28,30.207 60.299,30.482C60.369,31.072 60.396,31.666 60.382,32.26C60.405,32.916 60.364,33.573 60.262,34.221Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M69.304,28.124C68.702,27.787 68.01,27.636 67.319,27.691C66.601,27.642 65.884,27.802 65.259,28.152C64.804,28.473 64.497,28.959 64.408,29.502C64.259,30.388 64.196,31.286 64.219,32.184C64.195,33.083 64.261,33.983 64.417,34.87C64.465,35.139 64.568,35.395 64.72,35.623C64.872,35.851 65.07,36.046 65.301,36.197C65.961,36.531 66.701,36.681 67.441,36.631C67.913,36.63 68.384,36.603 68.852,36.552C69.241,36.517 69.626,36.453 70.005,36.359L70.005,34.916C69.139,35.004 68.429,35.05 67.874,35.05C67.464,35.074 67.053,35.025 66.66,34.907C66.54,34.857 66.433,34.778 66.351,34.677C66.269,34.577 66.214,34.458 66.19,34.331C66.106,33.822 66.071,33.307 66.087,32.792L70.193,32.792L70.193,32.165C70.215,31.241 70.167,30.317 70.047,29.4C69.98,28.896 69.713,28.438 69.304,28.124ZM68.41,31.428L66.087,31.428C66.086,30.95 66.119,30.473 66.186,30C66.195,29.887 66.228,29.777 66.283,29.678C66.338,29.579 66.414,29.492 66.505,29.424C66.988,29.233 67.527,29.233 68.01,29.424C68.1,29.494 68.173,29.583 68.227,29.682C68.28,29.782 68.312,29.892 68.321,30.004C68.394,30.475 68.424,30.952 68.41,31.428Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M81.818,29.12C81.739,28.745 81.527,28.406 81.218,28.161C80.849,27.928 80.409,27.817 79.966,27.844C79.966,27.844 79.924,27.844 79.895,27.844C79.895,27.844 78.467,27.767 77.877,28.464C77.813,28.542 77.755,28.625 77.705,28.713C77.61,28.478 77.445,28.275 77.229,28.129C76.858,27.924 76.431,27.826 76.001,27.848C76.001,27.848 74.573,27.767 73.983,28.468C73.917,28.545 73.86,28.629 73.812,28.717L73.764,27.984L72.027,27.984L72.027,36.48L73.931,36.48L73.931,32.051C73.921,31.52 73.942,30.988 73.993,30.459C74.005,30.198 74.107,29.949 74.283,29.749C74.493,29.584 74.764,29.506 75.035,29.531C75.276,29.513 75.518,29.557 75.735,29.658C75.815,29.731 75.879,29.819 75.923,29.916C75.967,30.013 75.991,30.118 75.992,30.224C76.042,30.747 76.063,31.272 76.054,31.798L76.054,36.48L77.958,36.48L77.958,32.051C77.958,31.314 77.958,30.785 78.015,30.459C78.028,30.197 78.133,29.946 78.315,29.749C78.53,29.586 78.802,29.508 79.076,29.531C79.319,29.513 79.562,29.557 79.781,29.658C79.86,29.732 79.923,29.82 79.966,29.917C80.01,30.014 80.033,30.118 80.033,30.224C80.086,30.747 80.106,31.272 80.095,31.798L80.095,36.48L81.999,36.48L81.999,30.884C82.009,30.291 81.949,29.699 81.818,29.12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M53.781,28.171C53.383,27.928 52.913,27.814 52.44,27.845C52.44,27.845 52.402,27.845 52.382,27.845C52.382,27.845 50.936,27.773 50.343,28.465C50.301,28.514 50.262,28.565 50.227,28.619L50.184,27.981L48.424,27.981L48.424,36.48L50.353,36.48L50.353,32.049C50.342,31.521 50.363,30.992 50.415,30.465C50.429,30.203 50.538,29.952 50.724,29.755C50.957,29.584 51.25,29.503 51.543,29.529C51.8,29.511 52.057,29.555 52.291,29.655C52.378,29.728 52.448,29.816 52.497,29.915C52.546,30.014 52.572,30.121 52.575,30.23C52.63,30.75 52.653,31.273 52.643,31.796L52.643,36.48L54.571,36.48L54.571,30.963C54.587,30.356 54.533,29.75 54.412,29.153C54.333,28.765 54.109,28.417 53.781,28.171Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M38.128,25.44L36.175,25.44L36.175,27.877L34.96,27.877L34.96,29.434L36.175,29.434L36.175,36.48L38.128,36.48L38.128,29.434L39.614,29.434L39.614,27.877L38.128,27.877L38.128,25.44Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -7,6 +7,7 @@
android:clipChildren="false">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/linearLayoutCompat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
@ -37,6 +38,7 @@
android:layout_marginStart="64dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="64dp"
android:minLines="3"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -45,6 +47,20 @@
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/chb_understand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alert_button_i_understand"
android:textStyle="bold"
android:visibility="gone"
app:buttonTint="@color/accent"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayoutCompat"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/fl_scan_card_container"
android:layout_width="wrap_content"

View file

@ -41,7 +41,7 @@
<include
android:id="@+id/onboarding_action_container"
layout="@layout/layout_onboarding_bottom_action_views"
layout="@layout/layout_onboarding_container_bottom"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />

View file

@ -1,160 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_details_confirm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLightGray"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
style="@style/Widget.MaterialComponents.Toolbar.Surface"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/backgroundLightGray"
android:fitsSystemWindows="true"
app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
app:title="Re-create wallet" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_details_confirm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:layout_marginBottom="33dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:id="@+id/tv_step_number"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="start"
android:text="@string/details_twins_recreate_step_format"
android:textColor="@color/colorSecondary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/v_step_1"
app:layout_constraintStart_toStartOf="@id/v_step_1"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/v_step_1"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginStart="24dp"
android:layout_marginTop="14dp"
android:layout_marginEnd="4dp"
android:background="@color/colorSecondary"
app:layout_constraintEnd_toStartOf="@+id/v_step_2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_step_number" />
<View
android:id="@+id/v_step_2"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:background="@color/colorSecondary"
app:layout_constraintEnd_toStartOf="@+id/v_step_3"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/v_step_1"
app:layout_constraintTop_toTopOf="@id/v_step_1" />
<View
android:id="@+id/v_step_3"
android:layout_width="0dp"
android:layout_height="3dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="24dp"
android:background="@color/colorSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/v_step_2"
app:layout_constraintTop_toTopOf="@id/v_step_1" />
<TextView
android:id="@+id/tv_twin_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginBottom="8dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:text="@string/details_twins_recreate_title_format"
android:textColor="@color/darkGray6"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/v_step_1" />
<TextView
android:id="@+id/tv_twin_cards_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:text="@string/details_twins_recreate_subtitle"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_twin_title" />
<ImageView
android:id="@+id/iv_card_1"
android:layout_width="130dp"
android:layout_height="70dp"
android:layout_marginStart="47dp"
android:layout_marginTop="16dp"
android:src="@drawable/card_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_twin_cards_description" />
<ImageView
android:id="@+id/iv_card_2"
android:layout_width="130dp"
android:layout_height="70dp"
android:layout_marginStart="24dp"
android:layout_marginTop="20dp"
android:src="@drawable/card_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_card_1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.37" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tap"
style="@style/TapBlackButton"
android:layout_width="0dp"
android:layout_marginStart="7dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
app:icon="@drawable/ic_scan"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline"
tools:text="@string/details_twins_recreate_button_format" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,103 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_details_confirm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLightGray"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_details_confirm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="33dp">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha="0.55"
android:background="@color/warning"
app:layout_constraintBottom_toTopOf="@id/l_twin_cards"
app:layout_constraintTop_toTopOf="parent" />
<include
android:id="@+id/l_twin_cards"
layout="@layout/layout_twin_cards_orange"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-40dp"
android:layout_marginBottom="24dp"
app:layout_constraintBottom_toTopOf="@id/tv_twin_title"
app:layout_constraintVertical_bias="0" />
<TextView
android:id="@+id/tv_twin_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:drawablePadding="4dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:text="@string/common_warning"
android:textColor="@color/darkGray6"
android:textSize="20sp"
android:textStyle="bold"
app:drawableStartCompat="@drawable/ic_warning"
app:drawableTint="@color/darkGray6"
app:layout_constraintBottom_toTopOf="@id/tv_twin_cards_description"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_twin_cards_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:text="@string/details_twins_recreate_warning"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/btn_start"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_cancel"
style="@style/TapBlackButton"
android:layout_width="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="30dp"
android:gravity="center"
android:text="@string/common_back"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_bias="1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.37" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_start"
style="@style/TapButtonWithIcon"
android:layout_width="0dp"
android:layout_marginStart="7dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
android:text="@string/common_start"
app:icon="@drawable/ic_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,97 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLightGray"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_details_confirm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="33dp">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha="0.22"
android:background="@color/darkGray6"
app:layout_constraintBottom_toTopOf="@id/l_twin_cards"
app:layout_constraintTop_toTopOf="parent" />
<include
android:id="@+id/l_twin_cards"
layout="@layout/layout_twin_cards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-40dp"
android:layout_marginBottom="24dp"
android:gravity="top"
app:layout_constraintBottom_toTopOf="@id/tv_twin_title"
app:layout_constraintVertical_bias="0" />
<TextView
android:id="@+id/tv_twin_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24sp"
android:layout_marginBottom="8dp"
android:text="@string/twins_onboarding_title"
android:textColor="@color/darkGray6"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/tv_twin_subtitle"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_twin_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginBottom="16dp"
android:text="@string/twins_onboarding_subtitle"
android:textColor="@color/darkGray6"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/tv_twin_cards_description_1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_twin_cards_description_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:text="@string/twins_onboarding_description_format"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/btn_continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.37" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_continue"
style="@style/TapBlackButton"
android:layout_width="0dp"
android:layout_marginStart="7dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
android:text="@string/common_continue"
app:icon="@drawable/ic_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -58,7 +58,7 @@
android:elevation="3dp"
android:maxHeight="215dp"
android:scaleType="fitCenter"
android:src="@drawable/card_placeholder"
android:src="@drawable/card_placeholder_black"
app:layout_constraintTop_toTopOf="parent" />
<TextView

View file

@ -7,9 +7,10 @@
android:clipChildren="false">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/linearLayoutCompat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginBottom="8dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
@ -37,6 +38,7 @@
android:layout_marginStart="64dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="64dp"
android:minLines="3"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -45,6 +47,20 @@
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/chb_understand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alert_button_i_understand"
android:textStyle="bold"
android:visibility="gone"
app:buttonTint="@color/accent"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayoutCompat"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/fl_scan_card_container"
android:layout_width="wrap_content"

View file

@ -1,11 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/onboarding_main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone"
tools:visibility="visible" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
@ -38,12 +46,43 @@
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:src="@drawable/ic_home_front_card"
android:transitionName="imv_front_card"
android:src="@drawable/card_placeholder_black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:clipChildren="false"
android:src="@drawable/card_placeholder_black"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"

View file

@ -13,7 +13,7 @@
android:elevation="0dp"
android:rotation="-21"
android:scaleType="fitXY"
android:src="@drawable/ic_home_back_card"
android:src="@drawable/card_placeholder_white"
android:translationY="@dimen/home_back_card_translationY"
app:layout_constraintEnd_toEndOf="@+id/imv_front_card"
app:layout_constraintStart_toStartOf="@+id/imv_front_card"
@ -26,8 +26,7 @@
android:elevation="1dp"
android:rotation="-3"
android:scaleType="fitXY"
android:src="@drawable/ic_home_front_card"
android:transitionName="imv_front_card"
android:src="@drawable/card_placeholder_black"
android:translationY="@dimen/home_front_card_translationY"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View file

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-40dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="60dp"
android:scaleType="fitXY"
android:src="@drawable/ic_group_1258"
app:layout_constraintBottom_toBottomOf="@id/iv_twin_card_2"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_twin_card_1"
android:layout_width="wrap_content"
android:layout_height="170dp"
android:layout_marginTop="50dp"
android:rotation="-19.5"
android:src="@drawable/card_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_twin_card_2"
android:layout_width="wrap_content"
android:layout_height="170dp"
android:layout_marginTop="20dp"
android:rotation="-19.5"
android:src="@drawable/card_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_twin_card_1" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="-40dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="60dp"
android:scaleType="fitXY"
android:src="@drawable/ic_twin_cards_background_warning"
app:layout_constraintBottom_toBottomOf="@id/iv_twin_card_2"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_twin_card_1"
android:layout_width="wrap_content"
android:layout_height="170dp"
android:layout_marginTop="50dp"
android:rotation="-19.5"
android:src="@drawable/card_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_twin_card_2"
android:layout_width="wrap_content"
android:layout_height="170dp"
android:layout_marginTop="20dp"
android:rotation="-19.5"
android:src="@drawable/card_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_twin_card_1" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -6,6 +6,11 @@
android:layout_height="match_parent"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
@ -38,12 +43,43 @@
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:src="@drawable/ic_home_front_card"
android:transitionName="imv_front_card"
android:src="@drawable/card_placeholder_black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:clipChildren="false"
android:src="@drawable/card_placeholder_black"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"

View file

@ -6,6 +6,11 @@
android:layout_height="match_parent"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
@ -38,14 +43,45 @@
android:layout_width="140dp"
android:layout_height="88dp"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder"
android:transitionName="imv_front_card"
android:src="@drawable/card_placeholder_black"
android:translationY="-30dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:clipChildren="false"
android:src="@drawable/card_placeholder_black"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"

View file

@ -6,6 +6,11 @@
android:layout_height="match_parent"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
@ -37,14 +42,45 @@
android:layout_width="140dp"
android:layout_height="88dp"
android:scaleType="fitXY"
android:src="@drawable/ic_home_front_card"
android:transitionName="imv_front_card"
android:src="@drawable/card_placeholder_black"
android:translationY="-30dp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:clipChildren="false"
android:layout_marginEnd="32dp"
android:src="@drawable/card_placeholder_black"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"

View file

@ -0,0 +1,116 @@
<?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"
android:id="@+id/onboarding_main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_progress_margin_top"
android:layout_marginEnd="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_card_background"
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_square_background_margin_top"
android:layout_marginEnd="32dp"
android:background="@drawable/shape_rectangle_rounded_8"
android:backgroundTint="@color/lightGray0"
android:elevation="0dp"
android:scaleType="fitCenter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pb_state" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_front_card"
android:layout_width="140dp"
android:layout_height="88dp"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black"
android:translationY="-30dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:clipChildren="false"
android:src="@drawable/card_placeholder_black"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<include
android:id="@+id/btn_refresh_balance"
layout="@layout/view_onboarding_refresh_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
app:layout_constraintTop_toBottomOf="@+id/imv_card_background" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_activation_success"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_activation_success"
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
app:layout_constraintTop_toBottomOf="@+id/imv_card_background" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -6,6 +6,11 @@
android:layout_height="match_parent"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
@ -20,7 +25,7 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_card_background"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_height="230dp"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_square_background_margin_top"
android:layout_marginEnd="32dp"
@ -37,14 +42,45 @@
android:layout_width="140dp"
android:layout_height="88dp"
android:scaleType="fitXY"
android:src="@drawable/ic_home_front_card"
android:transitionName="imv_front_card"
android:src="@drawable/card_placeholder_black"
android:translationY="-30dp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:clipChildren="false"
android:src="@drawable/card_placeholder_black"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"

View file

@ -0,0 +1,116 @@
<?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"
android:id="@+id/onboarding_main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<include
android:id="@+id/onboarding_twins_welcome_bg"
layout="@layout/view_bg_twins_welcome"
android:visibility="gone" />
<include
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_progress_margin_top"
android:layout_marginEnd="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_card_background"
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_square_background_margin_top"
android:layout_marginEnd="32dp"
android:background="@drawable/shape_rectangle_rounded_8"
android:backgroundTint="@color/lightGray0"
android:elevation="0dp"
android:scaleType="fitCenter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pb_state" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_front_card"
android:layout_width="140dp"
android:layout_height="88dp"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black"
android:translationY="-30dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<FrameLayout
android:id="@+id/cards_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="@dimen/onboarding_card_margin_top"
android:layout_marginEnd="32dp"
android:clipChildren="false"
android:src="@drawable/card_placeholder_black"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_back_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_white" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_twin_front_card"
android:layout_width="328dp"
android:layout_height="174dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/card_placeholder_black" />
</FrameLayout>
<include
android:id="@+id/onboarding_tv_balance"
layout="@layout/view_onboarding_tv_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
<include
android:id="@+id/btn_refresh_balance"
layout="@layout/view_onboarding_refresh_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
app:layout_constraintTop_toBottomOf="@+id/imv_card_background" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imv_activation_success"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_activation_success"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
app:layout_constraintTop_toBottomOf="@+id/imv_card_background" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -11,13 +11,12 @@
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tint="@color/colorPrimary"
android:src="@drawable/card_placeholder" />
android:src="@drawable/card_placeholder_black"
android:tint="@color/colorPrimary" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tint="@color/accent"
android:src="@drawable/card_placeholder" />
android:src="@drawable/card_placeholder_black" />
</FrameLayout>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bg_circle_large"
android:layout_width="736dp"
android:layout_height="609dp"
android:alpha="0.4"
android:scaleType="fitXY"
android:src="@drawable/shape_circle"
android:tint="#DEDEE0"
android:translationX="-272dp"
android:translationY="-200dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bg_circle_medium"
android:layout_width="651dp"
android:layout_height="569dp"
android:alpha="0.4"
android:scaleType="fitXY"
android:src="@drawable/shape_circle"
android:tint="#DCDCDC"
android:translationX="-254dp"
android:translationY="-269dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bg_circle_min"
android:layout_width="712dp"
android:layout_height="591dp"
android:alpha="0.4"
android:scaleType="fitXY"
android:src="@drawable/shape_circle"
android:tint="#D9D9D9"
android:translationX="-394dp"
android:translationY="-411dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</FrameLayout>

View file

@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pb_state"
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:progressTint="@color/accent" />
<merge>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pb_state"
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:progressTint="@color/accent" />
</merge>

View file

@ -12,6 +12,7 @@
android:id="@+id/tv_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="0dp"
android:fontFamily="sans-serif-medium"
android:text="@string/onboarding_balance_title"
android:textAllCaps="true"

View file

@ -14,25 +14,22 @@
<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 hands
and the other one with number %s.\n\nBoth cards can be used to extract funds from
this wallet.
</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="wallet_twins_chip_format" translatable="false">Card %s of 2</string>
<string name="details_row_title_twins_recreate" translatable="false">Re-create twin wallet</string>
<string name="details_twins_recreate_warning" translatable="false">This action is irreversible. You will not have access to the old wallet.\nThe wallet re-creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning.</string>
<string name="details_twins_recreate_toolbar" translatable="false">Re-create wallet</string>
<string name="details_twins_recreate_step_format" translatable="false">Step %s</string>
<string name="details_twins_recreate_title_format" translatable="false">Tap the #%s twin card</string>
<string name="details_twins_recreate_title_preparing" translatable="false">Preparing card</string>
<string name="details_twins_recreate_title_creating_wallet" translatable="false">Creating wallet</string>
<string name="details_twins_recreate_subtitle" translatable="false">The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning.</string>
<string name="details_twins_recreate_button_format" translatable="false">Tap the card #%s</string>
<string name="details_twins_recreate_alert" translatable="false">You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet.</string>
<string name="details_row_title_card_tou" translatable="false">Card terms of use</string>
<string name="twins_recreate_toolbar" translatable="false">Tangem Twin</string>
<string name="twins_recreate_warning" translatable="false">This action is irreversible. You will not have access to the old wallet.</string>
<string name="twins_recreate_title_format" translatable="false">Tap the #%s twin card</string>
<string name="twins_recreate_title_preparing" translatable="false">Preparing card</string>
<string name="twins_recreate_title_creating_wallet" translatable="false">Creating wallet</string>
<string name="twins_recreate_subtitle" translatable="false">The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning.</string>
<string name="twins_recreate_button_format" translatable="false">Tap the card #%s</string>
<string name="twins_recreate_alert" translatable="false">You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet.</string>
<string name="notification_twins_recreate_success" translatable="false">The twin address was successfully created</string>
//Common
@ -188,11 +185,13 @@ this wallet.
<string name="onboarding_top_up_button_show_wallet_address" translatable="false">Show the wallets address</string>
<string name="onboarding_dialog_wallet_address" translatable="false">Scan the address to top up your wallet</string>
<string name="onboarding_done_header" translatable="false">Done!</string>
<string name="onboarding_done_header" translatable="false">Success!</string>
<string name="onboarding_done_body" translatable="false">Your crypto card is activated and ready to be used</string>
<string name="onboarding_done_button_continue" translatable="false">Continue</string>
<string name="onboarding_balance_title" translatable="false">Balance</string>
<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>
</resources>

View file

@ -5,8 +5,7 @@
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorSecondary">@color/colorSecondary</item>
<item name="android:textColor">@color/darkGray6</item>
<item name="android:statusBarColor">@color/backgroundLightGray</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowContentOverlay">@null</item>