Updated on 2026-08-14

This commit is contained in:
Tangem 2021-05-12 21:40:31 +03:00
parent 206212dead
commit 812831937c
9 changed files with 141 additions and 15 deletions

View file

@ -75,13 +75,9 @@ dependencies {
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// TODO: change to prod dependencies when they are available
// implementation 'com.tangem:blockchain:1.164.0'
// implementation 'com.tangem:core:1.109.0'
// implementation 'com.tangem:sdk:1.109.0'
implementation 'blockchain-sdk-kotlin:blockchain:tangem-20210416.151414-8'
implementation 'com.tangem:tangem-core:tangem-20210414.185224-2'
implementation 'com.tangem:tangem-sdk:tangem-20210414.185231-2'
implementation 'com.tangem:blockchain:develop-5'
implementation 'com.tangem:core:develop-10'
implementation 'com.tangem:sdk:develop-10'
// WebView
implementation "androidx.browser:browser:1.3.0"

View file

@ -24,7 +24,12 @@ sealed class GlobalAction : Action {
data class Success(val appCurrency: FiatCurrencyName) : GlobalAction()
}
data class UpdateWalletSignedHashes(val walletSignedHashes: Int?) : GlobalAction()
data class UpdateWalletSignedHashes(
val walletSignedHashes: Int?,
val remainingSignatures: Int?,
val walletPublicKey: ByteArray,
) : GlobalAction()
data class HideWarningMessage(val warning: WarningMessage) : GlobalAction()
data class UpdateSecurityOptions(val securityOption: SecurityOption) : GlobalAction()

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.redux.global
import com.tangem.commands.wallet.WalletIndex
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.details.redux.SecurityOption
import org.rekotlin.Action
@ -32,13 +33,13 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
is GlobalAction.UpdateSecurityOptions -> {
val card = when (action.securityOption) {
SecurityOption.LongTap -> globalState.scanNoteResponse?.card?.copy(
isPin1Default = true, isPin2Default = true
isPin1Default = true, isPin2Default = true
)
SecurityOption.PassCode -> globalState.scanNoteResponse?.card?.copy(
isPin1Default = true, isPin2Default = false
isPin1Default = true, isPin2Default = false
)
SecurityOption.AccessCode -> globalState.scanNoteResponse?.card?.copy(
isPin1Default = false, isPin2Default = true
isPin1Default = false, isPin2Default = true
)
}
if (card != null) {
@ -47,6 +48,22 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
globalState
}
}
is GlobalAction.UpdateWalletSignedHashes -> {
val wallet = globalState.scanNoteResponse?.card
?.wallet(WalletIndex.PublicKey(action.walletPublicKey))
?.copy(
signedHashes = action.walletSignedHashes,
remainingSignatures = action.remainingSignatures
)
val card = globalState.scanNoteResponse?.card
wallet?.let { globalState.scanNoteResponse.card.updateWallet(wallet) }
if (card != null) {
globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card))
} else {
globalState
}
}
is GlobalAction.SetFeedbackManager -> {
globalState.copy(feedbackManager = action.feedbackManager)
}

View file

@ -0,0 +1,65 @@
package com.tangem.tap.domain
import com.tangem.Message
import com.tangem.TangemSdk
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.commands.SignCommand
import com.tangem.commands.SignResponse
import com.tangem.commands.wallet.WalletIndex
import com.tangem.common.CompletionResult
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class TangemSigner(
private val tangemSdk: TangemSdk,
private val initialMessage: Message,
private val signerCallback: (SignResponse) -> Unit
) : TransactionSigner {
override suspend fun sign(
hash: ByteArray,
cardId: String,
walletPublicKey: ByteArray
): CompletionResult<ByteArray> =
suspendCoroutine { continuation ->
val command = SignCommand(arrayOf(hash), WalletIndex.PublicKey(walletPublicKey))
tangemSdk.startSessionWithRunnable(
runnable = command,
cardId = cardId,
initialMessage = initialMessage,
) { result ->
when (result) {
is CompletionResult.Success -> {
signerCallback(result.data)
continuation.resume(CompletionResult.Success(result.data.signatures.first()))
}
is CompletionResult.Failure ->
continuation.resume(CompletionResult.Failure(result.error))
}
}
}
override suspend fun sign(
hashes: List<ByteArray>,
cardId: String,
walletPublicKey: ByteArray
): CompletionResult<List<ByteArray>> =
suspendCoroutine { continuation ->
val command = SignCommand(hashes.toTypedArray(), WalletIndex.PublicKey(walletPublicKey))
tangemSdk.startSessionWithRunnable(
runnable = command,
cardId = cardId,
initialMessage = initialMessage,
) { result ->
when (result) {
is CompletionResult.Success -> {
signerCallback(result.data)
continuation.resume(CompletionResult.Success(result.data.signatures))
}
is CompletionResult.Failure ->
continuation.resume(CompletionResult.Failure(result.error))
}
}
}
}

View file

@ -5,6 +5,7 @@ import com.tangem.commands.common.card.Card
import com.tangem.commands.common.card.masks.Settings
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapWorkarounds
import com.tangem.tap.domain.extensions.signedHashesCount
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.domain.twins.isTwinCard
@ -203,5 +204,5 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet<SecurityOption> {
private fun Card.toCardInfo(): CardInfo? {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
val issuer = this.cardData?.issuerName ?: return null
return CardInfo(cardId, issuer)
}
val signedHashes = this.signedHashesCount()
return CardInfo(cardId, issuer, signedHashes)}

View file

@ -26,6 +26,7 @@ data class DetailsState(
data class CardInfo(
val cardId: String,
val issuer: String,
val signedHashes: Int,
)
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }

View file

@ -74,8 +74,12 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
}
tv_card_id.text = cardId
tv_issuer.text = state.cardInfo.issuer
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
}
tv_signed_hashes.show(state.card?.isTwinCard() != true)
tv_signed_hashes_title.show(state.card?.isTwinCard() != true)
tv_disclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
tv_card_tou.show(state.cardTermsOfUseUrl != null)

View file

@ -6,12 +6,15 @@ import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.Signer
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.commands.SignResponse
import com.tangem.commands.common.card.Card
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.TapWorkarounds
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
@ -110,7 +113,17 @@ private fun sendTransaction(
if (TapWorkarounds.isStart2Coin) {
tangemSdk.config.linkedTerminal = false
}
val signer = Signer(tangemSdk, action.messageForSigner)
val signer = TangemSigner(
tangemSdk = tangemSdk, initialMessage = action.messageForSigner
) { signResponse ->
store.dispatch(
GlobalAction.UpdateWalletSignedHashes(
walletSignedHashes = signResponse.walletSignedHashes,
remainingSignatures = signResponse.walletRemainingSignatures,
walletPublicKey = walletManager.wallet.publicKey
)
)
}
val result = (walletManager as TransactionSender).send(txData, signer)
withContext(Dispatchers.Main) {
when (result) {

View file

@ -90,6 +90,30 @@
app:layout_constraintTop_toBottomOf="@id/tv_card_id"
tools:text="Tangem" />
<TextView
android:id="@+id/tv_signed_hashes_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="7dp"
android:paddingBottom="7dp"
android:text="@string/details_row_title_signed_hashes"
android:textColor="@color/darkGray6"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_issuer_title" />
<TextView
android:id="@+id/tv_signed_hashes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/darkGray1"
android:textSize="16sp"
android:paddingTop="7dp"
android:paddingBottom="7dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_issuer"
tools:text="48 hashes" />
<TextView
android:id="@+id/tv_disclaimer"
android:layout_width="match_parent"
@ -102,7 +126,7 @@
android:textSize="16sp"
app:drawableEndCompat="@drawable/ic_angle_bracket_right"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_issuer_title" />
app:layout_constraintTop_toBottomOf="@id/tv_signed_hashes_title" />
<TextView
android:id="@+id/tv_card_tou"