Updated on 2026-08-14
This commit is contained in:
parent
d5cd5f1951
commit
2ee3bce46a
11 changed files with 22 additions and 357 deletions
|
|
@ -19,7 +19,7 @@ import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease
|
|||
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isVisa
|
||||
import com.tangem.domain.common.TwinsHelper
|
||||
import com.tangem.domain.card.common.TwinsHelper
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||
import com.tangem.domain.card.configs.CardConfig
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.common.card.EllipticCurve
|
|||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.domain.common.TwinsHelper
|
||||
import com.tangem.domain.card.common.TwinsHelper
|
||||
import com.tangem.operations.attestation.AttestationTask
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.CreateWalletTask
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.common.core.CardSession
|
|||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.domain.common.TwinsHelper
|
||||
import com.tangem.domain.card.common.TwinsHelper
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.CreateWalletTask
|
||||
import com.tangem.operations.wallet.PurgeWalletCommand
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.Message
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
||||
class TwinCardsManager(card: CardDTO) {
|
||||
|
||||
private val issuersConfigStorage by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
store.inject(DaggerGraphState::issuersConfigStorage)
|
||||
}
|
||||
|
||||
private val firstCardId: String = card.cardId
|
||||
private val publicKey: String = card.issuer.publicKey.toHexString()
|
||||
|
||||
private var currentCardPublicKey: String? = null
|
||||
private var secondCardPublicKey: String? = null
|
||||
|
||||
suspend fun createFirstWallet(message: Message): CompletionResult<CreateWalletResponse> {
|
||||
val response = tangemSdkManager.createFirstTwinWallet(cardId = firstCardId, initialMessage = message)
|
||||
|
||||
if (response is CompletionResult.Success) {
|
||||
currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun createSecondWallet(
|
||||
initialMessage: Message,
|
||||
preparingMessage: Message,
|
||||
creatingWalletMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
val response = tangemSdkManager.createSecondTwinWallet(
|
||||
firstPublicKey = requireNotNull(currentCardPublicKey),
|
||||
firstCardId = firstCardId,
|
||||
issuerKeys = getIssuerKeys(),
|
||||
preparingMessage = preparingMessage,
|
||||
creatingWalletMessage = creatingWalletMessage,
|
||||
initialMessage = initialMessage,
|
||||
)
|
||||
|
||||
if (response is CompletionResult.Success) {
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun complete(message: Message): CompletionResult<ScanResponse> {
|
||||
val response = tangemSdkManager.finalizeTwin(
|
||||
secondCardPublicKey = requireNotNull(secondCardPublicKey).hexToBytes(),
|
||||
issuerKeyPair = getIssuerKeys(),
|
||||
cardId = firstCardId,
|
||||
initialMessage = message,
|
||||
)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private suspend fun getIssuerKeys(): KeyPair {
|
||||
val issuer = issuersConfigStorage.getConfig().first { it.publicKey == publicKey }
|
||||
|
||||
return KeyPair(
|
||||
publicKey = issuer.publicKey.hexToBytes(),
|
||||
privateKey = issuer.privateKey.hexToBytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
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.domain.common.TwinCardNumber
|
||||
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapView
|
||||
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapViewState
|
||||
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TwinsCardWidget(
|
||||
val leapfrogWidget: LeapfrogWidget,
|
||||
private val deviceScaleFactor: Float = 1f,
|
||||
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: 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)),
|
||||
)
|
||||
animator.doOnEnd { onEnd() }
|
||||
leapfrogWidget.fold(animate) { animator.start() }
|
||||
}
|
||||
|
||||
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)),
|
||||
)
|
||||
animator.doOnEnd {
|
||||
leapfrogWidget.initViews()
|
||||
leapfrogWidget.unfold(animate, onEnd)
|
||||
}
|
||||
animator.start()
|
||||
}
|
||||
|
||||
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): AnimatorSet {
|
||||
return AnimatorSet().apply {
|
||||
duration = if (animate) animationDuration else 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAnimator(cardNumber: TwinCardNumber, properties: TwinsCardProperties): ObjectAnimator {
|
||||
val view = getLeapViewByCardNumber(cardNumber).view
|
||||
val animator = ObjectAnimator.ofPropertyValuesHolder(view, *properties.createValuesHolders().toTypedArray())
|
||||
view.elevation = properties.elevation
|
||||
return animator
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun createWelcomeProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
|
||||
return when (cardNumber) {
|
||||
TwinCardNumber.First -> {
|
||||
TwinsCardProperties(
|
||||
xTranslation = -120f * deviceScaleFactor,
|
||||
yTranslation = -220f * deviceScaleFactor,
|
||||
rotation = -3f,
|
||||
elevation = 1f,
|
||||
scale = deviceScaleFactor,
|
||||
)
|
||||
}
|
||||
TwinCardNumber.Second -> {
|
||||
TwinsCardProperties(
|
||||
xTranslation = 170f * deviceScaleFactor,
|
||||
yTranslation = 170f * deviceScaleFactor,
|
||||
rotation = -3f,
|
||||
elevation = 0f,
|
||||
scale = deviceScaleFactor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLeapfrogProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
|
||||
val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state)
|
||||
return twinProperties.copy(
|
||||
xTranslation = 0f,
|
||||
yTranslation = 0f,
|
||||
rotation = 0f,
|
||||
scale = 1f,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun createActivateProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
|
||||
val topOfAnchorView = getTopOfAnchorViewForActivateState()
|
||||
val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state)
|
||||
return twinProperties.copy(
|
||||
xTranslation = twinProperties.xTranslation,
|
||||
yTranslation = twinProperties.yTranslation - topOfAnchorView,
|
||||
rotation = 0f,
|
||||
scale = (twinProperties.scale - 0.5f) * deviceScaleFactor,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getLeapViewByCardNumber(cardNumber: TwinCardNumber): LeapView {
|
||||
return when (cardNumber) {
|
||||
TwinCardNumber.First -> leapfrogWidget.getViewByPosition(0)
|
||||
TwinCardNumber.Second -> leapfrogWidget.getViewByPosition(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class TwinCardsWidgetState {
|
||||
Welcome, Leapfrog, Activate
|
||||
}
|
||||
|
||||
private data class TwinsCardProperties(
|
||||
val xTranslation: Float = 0f,
|
||||
val yTranslation: Float = 0f,
|
||||
val rotation: Float = 0f,
|
||||
val elevation: Float = 0f,
|
||||
val scale: Float = 1.1f,
|
||||
) {
|
||||
|
||||
fun createValuesHolders(): List<PropertyValuesHolder> {
|
||||
return listOf(
|
||||
PropertyValuesHolder.ofFloat(View.TRANSLATION_X, xTranslation),
|
||||
PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, yTranslation),
|
||||
PropertyValuesHolder.ofFloat(View.ROTATION, rotation),
|
||||
PropertyValuesHolder.ofFloat(View.SCALE_X, scale),
|
||||
PropertyValuesHolder.ofFloat(View.SCALE_Y, scale),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(leapViewState: LeapViewState): TwinsCardProperties {
|
||||
val leapViewProperties = leapViewState.properties
|
||||
|
||||
return TwinsCardProperties(
|
||||
yTranslation = leapViewProperties.yTranslation,
|
||||
elevation = leapViewProperties.elevationEnd,
|
||||
scale = leapViewProperties.scale,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.domain.card.common.TwinCardNumber
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue