From 834f410d4afae2f08f69ae86225167a820cb8e35 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 30 Jun 2020 15:47:01 +0300 Subject: [PATCH] Updated on 2026-08-14 --- build.gradle | 3 +- tangem-core/build.gradle | 10 +- .../src/main/java/com/tangem/CardSession.kt | 30 +++++- .../src/main/java/com/tangem/Config.kt | 10 +- .../java/com/tangem/SessionEnvironment.kt | 93 +++++++++++++----- .../java/com/tangem/SessionViewDelegate.kt | 2 +- .../src/main/java/com/tangem/TangemSdk.kt | 38 +++++--- .../com/tangem/commands/CheckWalletCommand.kt | 2 +- .../main/java/com/tangem/commands/Command.kt | 95 ++++++++++--------- .../tangem/commands/CreateWalletCommand.kt | 4 +- .../com/tangem/commands/PurgeWalletCommand.kt | 4 +- .../java/com/tangem/commands/ReadCommand.kt | 2 +- .../tangem/commands/ReadIssuerDataCommand.kt | 2 +- .../commands/ReadIssuerExtraDataCommand.kt | 2 +- .../tangem/commands/ReadUserDataCommand.kt | 2 +- .../java/com/tangem/commands/SetPinCommand.kt | 35 ++++--- .../java/com/tangem/commands/SignCommand.kt | 4 +- .../tangem/commands/WriteIssuerDataCommand.kt | 2 +- .../commands/WriteIssuerExtraDataCommand.kt | 2 +- .../tangem/commands/WriteUserDataCommand.kt | 2 +- .../commands/verifycard/VerifyCardCommand.kt | 2 +- .../com/tangem/common/CardValuesService.kt | 46 +++++++++ .../main/java/com/tangem/common/PinCode.kt | 10 ++ .../main/java/com/tangem/tasks/ScanTask.kt | 48 +++++++--- .../sqldelight/com/tangem/CardValuesEntity.sq | 34 +++++++ .../com/tangem/common/apdu/CommandApduTest.kt | 13 +-- .../com/tangem/devkit/TestUserDataActivity.kt | 11 +-- tangem-sdk/build.gradle | 1 + .../DefaultSessionViewDelegate.kt | 13 ++- .../SessionViewDelegateState.kt | 2 +- .../tangem_sdk_new/extensions/TangemSdk.kt | 8 +- .../tangem_sdk_new/ui/NfcSessionDialog.kt | 25 +++-- 32 files changed, 390 insertions(+), 167 deletions(-) create mode 100644 tangem-core/src/main/java/com/tangem/common/CardValuesService.kt create mode 100644 tangem-core/src/main/java/com/tangem/common/PinCode.kt create mode 100644 tangem-core/src/main/sqldelight/com/tangem/CardValuesEntity.sq diff --git a/build.gradle b/build.gradle index 40bc31028b..bbc8c94420 100644 --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,9 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin" classpath "com.github.dcendents:android-maven-gradle-plugin:2.1" classpath 'com.google.gms:google-services:4.3.3' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta04' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0' classpath 'com.google.firebase:perf-plugin:1.3.1' + classpath 'com.squareup.sqldelight:gradle-plugin:1.4.0' } } diff --git a/tangem-core/build.gradle b/tangem-core/build.gradle index 8b71b5e8bf..ce4d4d4c3e 100644 --- a/tangem-core/build.gradle +++ b/tangem-core/build.gradle @@ -4,6 +4,7 @@ apply plugin: 'com.github.dcendents.android-maven' apply from: '../dependencies.gradle' apply from: '../jitpack.gradle' apply plugin: 'kotlin-kapt' +apply plugin: 'com.squareup.sqldelight' group = "$jitpackSdk.group" version "$jitpackSdk.version" @@ -11,7 +12,7 @@ version "$jitpackSdk.version" dependencies { // kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin" - implementation "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin" + implementation "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlidn" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.7" @@ -22,6 +23,7 @@ dependencies { // misc implementation 'com.google.code.gson:gson:2.8.6' + implementation "com.squareup.sqldelight:sqlite-driver:1.4.0" //network implementation 'com.squareup.retrofit2:retrofit:2.8.1' @@ -36,6 +38,12 @@ dependencies { testImplementation "com.google.truth:truth:1.0.1" } +sqldelight { + Database { + packageName = "com.tangem" + } +} + sourceCompatibility = "8" targetCompatibility = "8" diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index bcaf32f1a7..4238ce7832 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -1,9 +1,11 @@ package com.tangem +import com.tangem.commands.Command import com.tangem.commands.CommandResponse import com.tangem.commands.OpenSessionCommand import com.tangem.commands.ReadCommand import com.tangem.common.CompletionResult +import com.tangem.common.PinCode import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu import com.tangem.common.extensions.calculateSha256 @@ -29,6 +31,10 @@ interface CardSessionRunnable { fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) } +interface CardSessionStoppedListener { + fun onSessionStopped(environment: SessionEnvironment) +} + enum class CardSessionState { Inactive, Active @@ -61,6 +67,7 @@ class CardSession( ) { var connectedTag: TagType? = null + var sessionStoppedListener: CardSessionStoppedListener? = null /** * True if some operation is still in progress. @@ -74,7 +81,7 @@ class CardSession( private val tag = this.javaClass.simpleName /** - * This metod starts a card session, performs preflight [ReadCommand], + * This method starts a card session, performs preflight [ReadCommand], * invokes [CardSessionRunnable.run] and closes the session. * @param runnable [CardSessionRunnable] that will be performed in the session. * @param callback will be triggered with a [CompletionResult] of a session. @@ -83,6 +90,24 @@ class CardSession( runnable: T, callback: (result: CompletionResult) -> Unit ) { + if (environment.pin1 == null) { + viewDelegate.onSessionStarted(cardId) + viewDelegate.onPinRequested { + environment.pin1 = PinCode(it) + startWithRunnable(runnable, callback) + } + return + } + + if ((runnable as? Command)?.requiresPin2 == true && environment.pin2 == null) { + viewDelegate.onSessionStarted(cardId) + viewDelegate.onPinRequested { + environment.pin2 = PinCode(it) + startWithRunnable(runnable, callback) + } + return + } + start(runnable.performPreflightRead) { session, error -> if (error != null) { callback(CompletionResult.Failure(error)) @@ -209,6 +234,7 @@ class CardSession( } private fun stopSession() { + sessionStoppedListener?.onSessionStopped(environment) reader.stopSession() state = CardSessionState.Inactive scope.cancel() @@ -259,7 +285,7 @@ class CardSession( return CompletionResult.Failure(error) } val uid = result.uid - val protocolKey = environment.pin1.pbkdf2Hash(uid, 50) + val protocolKey = environment.pin1!!.value.pbkdf2Hash(uid, 50) val secret = encryptionHelper.generateSecret(result.sessionKeyB) val sessionKey = (secret + protocolKey).calculateSha256() environment.encryptionKey = sessionKey diff --git a/tangem-core/src/main/java/com/tangem/Config.kt b/tangem-core/src/main/java/com/tangem/Config.kt index 45da4b6912..3cc58b5e6d 100644 --- a/tangem-core/src/main/java/com/tangem/Config.kt +++ b/tangem-core/src/main/java/com/tangem/Config.kt @@ -36,6 +36,14 @@ class Config( */ val cardFilter: CardFilter = CardFilter(), - var handleErrors: Boolean = true + var handleErrors: Boolean = true, + + var defaultPin1: String = "000000", + + var defaultPin2: String = "000", + + var savePin1InStaticField: Boolean = true, + + var savePin2InStaticField: Boolean = false ) \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt index 88050df353..322d25f057 100644 --- a/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt @@ -2,7 +2,9 @@ package com.tangem import com.tangem.commands.Card import com.tangem.commands.EllipticCurve -import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.CardValuesService +import com.tangem.common.PinCode +import com.tangem.common.TerminalKeysService import com.tangem.crypto.CryptoUtils.generatePublicKey @@ -13,38 +15,79 @@ import com.tangem.crypto.CryptoUtils.generatePublicKey * @property card Current card, read by preflight [com.tangem.commands.ReadCommand]. * @property terminalKeys generated terminal keys used in Linked Terminal feature. */ -data class SessionEnvironment( - var pin2: ByteArray = DEFAULT_PIN2.calculateSha256(), - var card: Card? = null, - var terminalKeys: KeyPair? = null, - var encryptionMode: EncryptionMode = EncryptionMode.NONE, - var encryptionKey: ByteArray? = null, - var cvc: ByteArray? = null, - var cardFilter: CardFilter = CardFilter(), - val handleErrors: Boolean = true +class SessionEnvironment( + cardId: String?, + private val config: Config, + private val terminalKeysService: TerminalKeysService?, + private val cardValuesService: CardValuesService? ) { - var pin1: ByteArray = SessionEnvironment.pin1 - private set - get() = SessionEnvironment.pin1 + var pin1: PinCode? + var pin2: PinCode? + var cvc: ByteArray? = null - fun isCurrentPin1Default(): Boolean = Companion.pin1.contentEquals(DEFAULT_PIN.calculateSha256()) - fun isCurrentPin2Default(): Boolean = pin2.contentEquals(DEFAULT_PIN2.calculateSha256()) + var terminalKeys: KeyPair? = null + var cardFilter: CardFilter + val handleErrors: Boolean - fun setPin1(pin1: String) { - SessionEnvironment.pin1 = pin1.calculateSha256() + var encryptionMode: EncryptionMode + var encryptionKey: ByteArray? = null + + var cardVerification: VerificationState + var cardValidation: VerificationState + var codeVerification: VerificationState + + var card: Card? = null + + init { + terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null + cardFilter = config.cardFilter + handleErrors = config.handleErrors + + encryptionMode = config.encryptionMode + + val cardValues = cardId?.let { cardValuesService?.getValues(cardId) } + + cardVerification = cardValues?.cardVerification ?: VerificationState.NotVerified + cardValidation = cardValues?.cardValidation ?: VerificationState.NotVerified + codeVerification = cardValues?.codeVerification ?: VerificationState.NotVerified + + pin1 = TangemSdk.pin1 + ?: if (cardValues?.isPin1Default != false) { + PinCode(config.defaultPin1, true) + } else { + null + } + + pin2 = cardId?.let { TangemSdk.pin2[it] } + ?: if (cardValues?.isPin2Default != false) { + PinCode(config.defaultPin2, true) + } else { + null + } } - fun setPin2(pin2: String) { - this.pin2 = pin2.calculateSha256() + fun restoreCardValues() { + val cardValues = this.card?.cardId?.let { cardValuesService?.getValues(it) } + cardVerification = cardValues?.cardVerification ?: VerificationState.NotVerified + cardValidation = cardValues?.cardValidation ?: VerificationState.NotVerified + codeVerification = cardValues?.codeVerification ?: VerificationState.NotVerified + + if (cardValues?.isPin1Default == false && pin1?.isDefault == true) pin1 = null + if (cardValues?.isPin2Default == false && pin1?.isDefault == true) pin2 = null } - companion object { - const val DEFAULT_PIN = "000000" - const val DEFAULT_PIN2 = "000" + fun saveCardValues() { + if (config.savePin1InStaticField) { + TangemSdk.pin1 = pin1 + } + if (config.savePin2InStaticField) { + card?.cardId?.let { cardId -> TangemSdk.pin2[cardId] = pin2 } + } - var pin1: ByteArray = DEFAULT_PIN.calculateSha256() + cardValuesService?.saveValues(this) } + } /** @@ -60,4 +103,8 @@ class KeyPair(val publicKey: ByteArray, val privateKey: ByteArray) { constructor(privateKey: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1) : this(generatePublicKey(privateKey, curve), privateKey) +} + +enum class VerificationState { + Passed, Offline, Failed, NotVerified } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt b/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt index e3b2306bbf..7177c0a2b6 100644 --- a/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt +++ b/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt @@ -47,7 +47,7 @@ interface SessionViewDelegate { /** * It is called when a user is expected to enter pin code. */ - fun onPinRequested(callback: (pin: String?) -> Unit) + fun onPinRequested(callback: (pin: String) -> Unit) } /** diff --git a/tangem-core/src/main/java/com/tangem/TangemSdk.kt b/tangem-core/src/main/java/com/tangem/TangemSdk.kt index 6508fb8c3f..190c13f515 100644 --- a/tangem-core/src/main/java/com/tangem/TangemSdk.kt +++ b/tangem-core/src/main/java/com/tangem/TangemSdk.kt @@ -1,5 +1,6 @@ package com.tangem +import com.squareup.sqldelight.db.SqlDriver import com.tangem.commands.* import com.tangem.commands.personalization.DepersonalizeCommand import com.tangem.commands.personalization.DepersonalizeResponse @@ -10,7 +11,9 @@ import com.tangem.commands.personalization.entities.Issuer import com.tangem.commands.personalization.entities.Manufacturer import com.tangem.commands.verifycard.VerifyCardCommand import com.tangem.commands.verifycard.VerifyCardResponse +import com.tangem.common.CardValuesDbService import com.tangem.common.CompletionResult +import com.tangem.common.PinCode import com.tangem.common.TerminalKeysService import com.tangem.crypto.CryptoUtils import com.tangem.tasks.CreateWalletTask @@ -30,7 +33,8 @@ import com.tangem.tasks.ScanTask class TangemSdk( private val reader: CardReader, private val viewDelegate: SessionViewDelegate, - var config: Config = Config() + var config: Config = Config(), + private val sqlDriver: SqlDriver ) { private var terminalKeysService: TerminalKeysService? = null @@ -203,7 +207,7 @@ class TangemSdk( initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit ) { - val command = WriteUserDataCommand(userData = userData,userCounter = userCounter) + val command = WriteUserDataCommand(userData = userData, userCounter = userCounter) startSessionWithRunnable(command, cardId, initialMessage, callback) } @@ -378,13 +382,22 @@ class TangemSdk( fun startSessionWithRunnable( runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) + val cardSession = CardSession(buildEnvironment(cardId), reader, viewDelegate, cardId, initialMessage) + addSessionStoppedListener(cardSession) Thread().run { cardSession.startWithRunnable(runnable, callback) } } + private fun addSessionStoppedListener(session: CardSession) { + session.sessionStoppedListener = object : CardSessionStoppedListener { + override fun onSessionStopped(environment: SessionEnvironment) { + environment.saveCardValues() + } + } + } + /** * Allows running a custom bunch of commands in one [CardSession] with lightweight closure syntax. - * Tangem SDK will start a card sesion and perform preflight [ReadCommand]. + * Tangem SDK will start a card session and perform preflight [ReadCommand]. * @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card * with which you tapped a phone has this [cardId] and SDK will return @@ -395,8 +408,9 @@ class TangemSdk( * then you can use the [CardSession] to interact with a card. */ fun startSession(cardId: String? = null, initialMessage: Message? = null, - callback: (session: CardSession, error: TangemSdkError?) -> Unit) { - val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) + callback: (session: CardSession, error: TangemSdkError?) -> Unit) { + val cardSession = CardSession(buildEnvironment(cardId), reader, viewDelegate, cardId, initialMessage) + addSessionStoppedListener(cardSession) Thread().run { cardSession.start(callback = callback) } } @@ -408,14 +422,14 @@ class TangemSdk( this.terminalKeysService = terminalKeysService } - private fun buildEnvironment(): SessionEnvironment { - val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null + private fun buildEnvironment(cardId: String?): SessionEnvironment { return SessionEnvironment( - terminalKeys = terminalKeys, - cardFilter = config.cardFilter, - handleErrors = config.handleErrors + cardId, config, terminalKeysService, CardValuesDbService(sqlDriver) ) } - companion object + companion object { + var pin1: PinCode? = null + val pin2: MutableMap = mutableMapOf() + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt index 672ca2da62..7b7116ba44 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -82,7 +82,7 @@ class CheckWalletCommand( override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Challenge, challenge) return CommandApdu(Instruction.CheckWallet, tlvBuilder.serialize()) diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt index ce21f6cdba..cad5effb77 100644 --- a/tangem-core/src/main/java/com/tangem/commands/Command.kt +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -2,6 +2,7 @@ package com.tangem.commands import com.tangem.* import com.tangem.common.CompletionResult +import com.tangem.common.PinCode import com.tangem.common.apdu.* import com.tangem.common.extensions.toInt import com.tangem.common.tlv.TlvTag @@ -50,7 +51,6 @@ abstract class Command : ApduSerializable, CardSessionRu open fun mapError(card: Card?, error: TangemSdkError): TangemSdkError = error fun transceive(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val card = session.environment.card if (session.environment.handleErrors && card != null) { performPreCheck(card)?.let { error -> @@ -58,12 +58,14 @@ abstract class Command : ApduSerializable, CardSessionRu return } } - - if (requiresPin2 && session.environment.isCurrentPin2Default()) { + if (requiresPin2 && session.environment.pin2?.isDefault == true) { handlePin2(session, callback) return } + transceiveInternal(session, callback) + } + private fun transceiveInternal(session: CardSession, callback: (result: CompletionResult) -> Unit) { val apdu = serialize(session.environment) transceiveApdu(apdu, session) { result -> when (result) { @@ -74,6 +76,10 @@ abstract class Command : ApduSerializable, CardSessionRu handlePin1(session, callback) return@transceiveApdu } + if (error is TangemSdkError.Pin2OrCvcRequired) { + handlePin2(session, callback) + return@transceiveApdu + } callback(CompletionResult.Failure(error)) return@transceiveApdu @@ -93,9 +99,9 @@ abstract class Command : ApduSerializable, CardSessionRu } private fun transceiveApdu( - apdu: CommandApdu, - session: CardSession, - callback: (result: CompletionResult) -> Unit + apdu: CommandApdu, + session: CardSession, + callback: (result: CompletionResult) -> Unit ) { Log.i(this::class.simpleName!!, "transieve: ${Instruction.byCode(apdu.ins)}") @@ -114,17 +120,17 @@ abstract class Command : ApduSerializable, CardSessionRu StatusWord.NeedPause -> { // NeedPause is returned from the card whenever security delay is triggered. val remainingTime = - deserializeSecurityDelay(responseApdu) + deserializeSecurityDelay(responseApdu) if (remainingTime != null) { session.viewDelegate.onSecurityDelay( - remainingTime, - session.environment.card?.pauseBeforePin2 ?: 0 + remainingTime, + session.environment.card?.pauseBeforePin2 ?: 0 ) } Log.i( - this::class.simpleName!!, - "Nfc command ${this::class.simpleName!!} " + - "triggered security delay of $remainingTime milliseconds" + this::class.simpleName!!, + "Nfc command ${this::class.simpleName!!} " + + "triggered security delay of $remainingTime milliseconds" ) transceiveApdu(apdu, session, callback) } @@ -149,7 +155,7 @@ abstract class Command : ApduSerializable, CardSessionRu } else -> { val error = responseApdu.statusWord.toTangemSdkError() - if (error != null && !tryHandleError(error)) { + if (error != null) { callback(CompletionResult.Failure(error)) } else { callback(CompletionResult.Failure(TangemSdkError.UnknownError())) @@ -173,61 +179,56 @@ abstract class Command : ApduSerializable, CardSessionRu * @return Remaining security delay in milliseconds. */ private fun deserializeSecurityDelay( - responseApdu: ResponseApdu + responseApdu: ResponseApdu ): Int? { val tlv = responseApdu.getTlvData() return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt() } - private fun tryHandleError(error: TangemSdkError): Boolean { - return false - } - private fun handlePin1( - session: CardSession, - callback: (result: CompletionResult) -> Unit + session: CardSession, + callback: (result: CompletionResult) -> Unit ) { - if (!session.environment.isCurrentPin1Default()) { - session.environment.setPin1(SessionEnvironment.DEFAULT_PIN) - transceive(session, callback) - return - } session.pause() session.viewDelegate.onPinRequested { pin1 -> - if (!pin1.isNullOrEmpty()) { - session.environment.setPin1(pin1) - session.resume() - transceive(session, callback) - } else { - session.environment.setPin1(SessionEnvironment.DEFAULT_PIN) - session.resume() - transceive(session, callback) - } + session.environment.pin1 = PinCode(pin1) + session.resume() + transceive(session, callback) } } private fun handlePin2( - session: CardSession, - callback: (result: CompletionResult) -> Unit + session: CardSession, + callback: (result: CompletionResult) -> Unit ) { - val checkPinCommand = SetPinCommand(session.environment.pin1, session.environment.pin2) + val currentPin1 = session.environment.pin1?.value ?: run { + callback(CompletionResult.Failure(TangemSdkError.Pin1Required())) + return + } + val currentPin2 = session.environment.pin2?.value ?: run { + callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired())) + return + } + + val checkPinCommand = SetPinCommand(currentPin1, currentPin2) checkPinCommand.run(session) { result -> when (result) { is CompletionResult.Failure -> { - session.viewDelegate.onPinRequested { pin2 -> - if (!pin2.isNullOrEmpty()) { - session.environment.setPin2(pin2) - transceive(session, callback) - } else { - session.environment.setPin2(SessionEnvironment.DEFAULT_PIN2) - callback(CompletionResult.Failure(TangemSdkError.Pin2OrCvcRequired())) - } - } + session.environment.pin2 = null + getPin2FromDelegate(session, callback) } is CompletionResult.Success -> { - transceive(session, callback) + transceiveInternal(session, callback) } } } } + + private fun getPin2FromDelegate(session: CardSession, + callback: (result: CompletionResult) -> Unit) { + session.viewDelegate.onPinRequested { pin2 -> + session.environment.pin2 = PinCode(pin2) + transceive(session, callback) + } + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt index 83a6cbd78d..af6cce48b8 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt @@ -62,9 +62,9 @@ class CreateWalletCommand : Command() { override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) - tlvBuilder.append(TlvTag.Pin2, environment.pin2) + tlvBuilder.append(TlvTag.Pin2, environment.pin2?.value) tlvBuilder.append(TlvTag.Cvc, environment.cvc) return CommandApdu(Instruction.CreateWallet, tlvBuilder.serialize()) } diff --git a/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt index c4ca477ca3..3d0631f3df 100644 --- a/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt @@ -61,9 +61,9 @@ class PurgeWalletCommand : Command() { override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) - tlvBuilder.append(TlvTag.Pin2, environment.pin2) + tlvBuilder.append(TlvTag.Pin2, environment.pin2?.value) return CommandApdu(Instruction.PurgeWallet, tlvBuilder.serialize()) } diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index 189588a080..5e692f2e40 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -382,7 +382,7 @@ class ReadCommand : Command() { * In order to obtain card’s data, [ReadCommand] should use the correct pin 1 value. * The card will not respond if wrong pin 1 has been submitted. */ - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.TerminalPublicKey, environment.terminalKeys?.publicKey) return CommandApdu(Instruction.Read, tlvBuilder.serialize()) } diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt index 97bc306083..4d2301675a 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt @@ -96,7 +96,7 @@ class ReadIssuerDataCommand( override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, IssuerDataMode.ReadData) return CommandApdu(Instruction.ReadIssuerData, tlvBuilder.serialize()) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt index cb7bba09d0..88e5af5569 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt @@ -145,7 +145,7 @@ class ReadIssuerExtraDataCommand( override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, IssuerDataMode.ReadExtraData) tlvBuilder.append(TlvTag.Offset, offset) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt index 2e68b631ef..58baea5b08 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt @@ -62,7 +62,7 @@ class ReadUserDataCommand : Command() { override fun serialize(environment: SessionEnvironment): CommandApdu { val builder = TlvBuilder() builder.append(TlvTag.CardId, environment.card?.cardId) - builder.append(TlvTag.Pin, environment.pin1) + builder.append(TlvTag.Pin, environment.pin1?.value) return CommandApdu(Instruction.ReadUserData, builder.serialize()) } diff --git a/tangem-core/src/main/java/com/tangem/commands/SetPinCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SetPinCommand.kt index 084e755cc1..6b7f12fa80 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SetPinCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SetPinCommand.kt @@ -6,7 +6,6 @@ import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.apdu.StatusWord -import com.tangem.common.extensions.calculateSha256 import com.tangem.common.tlv.TlvBuilder import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag @@ -40,28 +39,27 @@ enum class SetPinStatus { } class SetPinResponse( - /** - * CID, Unique Tangem card ID number. - */ - val cardId: String, - /** - * - */ - val status: SetPinStatus + /** + * CID, Unique Tangem card ID number. + */ + val cardId: String, + /** + * + */ + val status: SetPinStatus ) : CommandResponse - class SetPinCommand( - private val newPin1: ByteArray = SessionEnvironment.DEFAULT_PIN.calculateSha256(), - private val newPin2: ByteArray = SessionEnvironment.DEFAULT_PIN2.calculateSha256(), - private val newPin3: ByteArray? = null + private var newPin1: ByteArray, + private var newPin2: ByteArray, + private var newPin3: ByteArray? = null ) : Command() { override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) - tlvBuilder.append(TlvTag.Pin2, environment.pin2) + tlvBuilder.append(TlvTag.Pin2, environment.pin2?.value) tlvBuilder.append(TlvTag.Cvc, environment.cvc) tlvBuilder.append(TlvTag.NewPin, newPin1) tlvBuilder.append(TlvTag.NewPin2, newPin2) @@ -72,12 +70,13 @@ class SetPinCommand( override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): SetPinResponse { val tlvData = apdu.getTlvData() ?: throw TangemSdkError.DeserializeApduFailed() - val status = SetPinStatus.fromStatusWord(apdu.statusWord) ?: throw TangemSdkError.DecodingFailed() + val status = SetPinStatus.fromStatusWord(apdu.statusWord) + ?: throw TangemSdkError.DecodingFailed() val decoder = TlvDecoder(tlvData) return SetPinResponse( - cardId = decoder.decode(TlvTag.CardId), - status = status + cardId = decoder.decode(TlvTag.CardId), + status = status ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index b48263e2d1..21c003b64b 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -75,8 +75,8 @@ class SignCommand(private val hashes: Array) : Command( override fun serialize(environment: SessionEnvironment): CommandApdu { val dataToSign = flattenHashes() val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) - tlvBuilder.append(TlvTag.Pin2, environment.pin2) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) + tlvBuilder.append(TlvTag.Pin2, environment.pin2?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.TransactionOutHashSize, byteArrayOf(hashSizes.toByte())) tlvBuilder.append(TlvTag.TransactionOutHash, dataToSign) diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt index 0b563a018e..62c44269e2 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt @@ -83,7 +83,7 @@ class WriteIssuerDataCommand( override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, IssuerDataMode.WriteData) tlvBuilder.append(TlvTag.IssuerData, issuerData) diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt index 115ebcd1dd..6859aff915 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt @@ -149,7 +149,7 @@ class WriteIssuerExtraDataCommand( override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin, environment.pin1?.value) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, mode) diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt index 380f18eaf1..fadb16731d 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt @@ -57,7 +57,7 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val override fun serialize(environment: SessionEnvironment): CommandApdu { val builder = TlvBuilder() builder.append(TlvTag.CardId, environment.card?.cardId) - builder.append(TlvTag.Pin, environment.pin1) + builder.append(TlvTag.Pin, environment.pin1?.value) builder.append(TlvTag.UserData, userData) builder.append(TlvTag.UserCounter, userCounter) builder.append(TlvTag.UserProtectedData, userProtectedData) diff --git a/tangem-core/src/main/java/com/tangem/commands/verifycard/VerifyCardCommand.kt b/tangem-core/src/main/java/com/tangem/commands/verifycard/VerifyCardCommand.kt index 1eb97c4aa9..bbb3454e66 100644 --- a/tangem-core/src/main/java/com/tangem/commands/verifycard/VerifyCardCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/verifycard/VerifyCardCommand.kt @@ -137,7 +137,7 @@ class VerifyCardCommand(private val onlineVerification: Boolean) : Command + cardValuesQueries.insertOrReplace( + cardId, + environment.pin1?.isDefault ?: false, + environment.pin2?.isDefault ?: false, + environment.cardVerification, environment.cardVerification, + environment.codeVerification + ) + } + } + + override fun getValues(cardId: String): CardValues? = + cardValuesQueries.selectByCardId(cardId).executeAsOneOrNull() + +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/common/PinCode.kt b/tangem-core/src/main/java/com/tangem/common/PinCode.kt new file mode 100644 index 0000000000..0262089def --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/common/PinCode.kt @@ -0,0 +1,10 @@ +package com.tangem.common + +import com.tangem.common.extensions.calculateSha256 + +class PinCode( + val value: ByteArray, + val isDefault: Boolean +) { + constructor(value: String, isDefault: Boolean = false): this(value.calculateSha256(), isDefault) +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index 8b9d8f2452..d957d2966c 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -31,24 +31,44 @@ internal class ScanTask : CardSessionRunnable { is CompletionResult.Success -> { val card = readResult.data session.environment.card = card - if (card.cardData?.productMask?.contains(Product.Tag) != false) { - callback(CompletionResult.Success(card)) - - } else if (card.status != CardStatus.Loaded) { - callback(CompletionResult.Success(card)) - - } else if (card.curve == null || card.walletPublicKey == null) { - callback(CompletionResult.Failure(TangemSdkError.CardError())) - - } else { - val checkWalletCommand = CheckWalletCommand(card.curve, card.walletPublicKey) - checkWalletCommand.run(session) { result -> + if (session.environment.pin1 != null && session.environment.pin2 != null) { + val checkPinCommand = SetPinCommand(session.environment.pin1!!.value, session.environment.pin2!!.value) + checkPinCommand.run(session) { result -> when (result) { - is CompletionResult.Success -> callback(CompletionResult.Success(card)) - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + is CompletionResult.Failure -> { + session.environment.pin2 = null + } } + session.environment.restoreCardValues() } + } else { + runCheckWalletIfNeeded(card, session, callback) } + + } + } + } + } + + private fun runCheckWalletIfNeeded( + card: Card, session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + if (card.cardData?.productMask?.contains(Product.Tag) != false) { + callback(CompletionResult.Success(card)) + + } else if (card.status != CardStatus.Loaded) { + callback(CompletionResult.Success(card)) + + } else if (card.curve == null || card.walletPublicKey == null) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + + } else { + val checkWalletCommand = CheckWalletCommand(card.curve, card.walletPublicKey) + checkWalletCommand.run(session) { result -> + when (result) { + is CompletionResult.Success -> callback(CompletionResult.Success(card)) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } diff --git a/tangem-core/src/main/sqldelight/com/tangem/CardValuesEntity.sq b/tangem-core/src/main/sqldelight/com/tangem/CardValuesEntity.sq new file mode 100644 index 0000000000..7ac4521e98 --- /dev/null +++ b/tangem-core/src/main/sqldelight/com/tangem/CardValuesEntity.sq @@ -0,0 +1,34 @@ +import com.tangem.VerificationState; + +CREATE TABLE cardValues ( + cardId TEXT NOT NULL UNIQUE PRIMARY KEY, + isPin1Default INTEGER AS Boolean DEFAULT 1, + isPin2Default INTEGER AS Boolean DEFAULT 1, + cardVerification TEXT AS VerificationState, + cardValidation TEXT AS VerificationState, + codeVerification TEXT AS VerificationState +); + +insertOrReplace: +INSERT OR REPLACE INTO cardValues( + cardId, + isPin1Default, + isPin2Default, + cardVerification, + cardValidation, + codeVerification +) +VALUES (?, ?, ?, ?, ?, ?); + +selectByCardId: +SELECT * +FROM cardValues +WHERE cardId = ?; + +deleteAll: +DELETE FROM cardValues; + +deleteByCardId: +DELETE +FROM cardValues +WHERE cardId = ?; \ No newline at end of file diff --git a/tangem-core/src/test/java/com/tangem/common/apdu/CommandApduTest.kt b/tangem-core/src/test/java/com/tangem/common/apdu/CommandApduTest.kt index b995c4de57..05a5b3ca0b 100644 --- a/tangem-core/src/test/java/com/tangem/common/apdu/CommandApduTest.kt +++ b/tangem-core/src/test/java/com/tangem/common/apdu/CommandApduTest.kt @@ -1,23 +1,21 @@ package com.tangem.common.apdu import com.google.common.truth.Truth.assertThat +import com.tangem.Config import com.tangem.SessionEnvironment import com.tangem.common.tlv.TlvBuilder import com.tangem.common.tlv.TlvTag -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.BroadcastChannel -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.collect import org.junit.Test class CommandApduTest { + val sessionEnvironment = SessionEnvironment(null, Config(), null, null) + @Test fun `simple READ command to bytes`() { - val sessionEnvironment = SessionEnvironment() val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, sessionEnvironment.pin1) + tlvBuilder.append(TlvTag.Pin, sessionEnvironment.pin1?.value) val commandApdu = CommandApdu( Instruction.Read, tlvBuilder.serialize() @@ -33,13 +31,12 @@ class CommandApduTest { @Test fun `READ with terminal key to bytes`() { - val sessionEnvironment = SessionEnvironment() val terminalPublicKey = byteArrayOf(4, 80, -122, 58, -42, 74, -121, -82, -118, 47, -24, 60, 26, -15, -88, 64, 60, -75, 63, 83, -28, -122, -40, 81, 29, -83, -118, 4, -120, 126, 91, 35, 82, 44, -44, 112, 36, 52, 83, -94, -103, -6, -98, 119, 35, 119, 22, 16, 58, -68, 17, -95, -33, 56, -123, 94, -42, -14, -18, 24, 126, -100, 88, 43, -90) val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, sessionEnvironment.pin1) + tlvBuilder.append(TlvTag.Pin, sessionEnvironment.pin1?.value) tlvBuilder.append(TlvTag.TerminalPublicKey, terminalPublicKey) val commandApdu = CommandApdu( Instruction.Read, diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt b/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt index 5628c01f29..2be072959f 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/TestUserDataActivity.kt @@ -5,7 +5,6 @@ import android.view.View import android.widget.CompoundButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import com.tangem.SessionEnvironment import com.tangem.TangemSdk import com.tangem.TangemSdkError import com.tangem.common.CompletionResult @@ -103,7 +102,7 @@ class TestUserDataActivity : AppCompatActivity() { chb_with_ud_protected.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedData(buttonView) } chb_with_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateCounter(buttonView) } chb_with_protected_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedCounter(buttonView) } - chb_with_pin2.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updatePin2(buttonView) } +// chb_with_pin2.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updatePin2(buttonView) } } private fun showReadWriteSection(show: Boolean) { @@ -140,8 +139,8 @@ class WriteOptions { userProtectedCounter = if (chbx.isChecked) value else null } - fun updatePin2(chbx: CompoundButton) { - val value = SessionEnvironment.DEFAULT_PIN2 - pin2 = if (chbx.isChecked) value else null - } +// fun updatePin2(chbx: CompoundButton) { +// val value = SessionEnvironment.pin1.DEFAULT_PIN2 +// pin2 = if (chbx.isChecked) value else null +// } } \ No newline at end of file diff --git a/tangem-sdk/build.gradle b/tangem-sdk/build.gradle index 7b4e3dd92f..bb868f952a 100644 --- a/tangem-sdk/build.gradle +++ b/tangem-sdk/build.gradle @@ -62,6 +62,7 @@ dependencies { // misc implementation 'com.skyfishjy.ripplebackground:library:1.0.1' implementation 'at.favre.lib:armadillo:0.9.0' + implementation "com.squareup.sqldelight:android-driver:1.4.0" // testing testImplementation 'junit:junit:4.12' diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt index 3cd49e1741..9308dffddc 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt @@ -19,20 +19,19 @@ class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDel } override fun onSessionStarted(cardId: String?, message: Message?) { - postUI { showReadingDialog(activity, cardId, message) } + postUI { + if (readingDialog == null) createReadingDialog(activity) + readingDialog?.show(SessionViewDelegateState.Ready(cardId, message)) + } } - private fun showReadingDialog(activity: FragmentActivity, cardId: String?, message: Message?) { + private fun createReadingDialog(activity: FragmentActivity) { val dialogView = activity.layoutInflater.inflate(R.layout.nfc_bottom_sheet, null) readingDialog = NfcSessionDialog(activity) readingDialog?.setContentView(dialogView) readingDialog?.dismissWithAnimation = true readingDialog?.create() - readingDialog?.setOnShowListener { - readingDialog?.show(SessionViewDelegateState.Ready(cardId, message)) - } readingDialog?.setOnCancelListener { reader.stopSession(true) } - readingDialog?.show() } override fun onSecurityDelay(ms: Int, totalDurationSeconds: Int) { @@ -68,7 +67,7 @@ class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDel postUI { readingDialog?.show(SessionViewDelegateState.Error(error)) } } - override fun onPinRequested(callback: (pin: String?) -> Unit) { + override fun onPinRequested(callback: (pin: String) -> Unit) { postUI { readingDialog?.show(SessionViewDelegateState.PinRequested(callback)) } } diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/SessionViewDelegateState.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/SessionViewDelegateState.kt index d5e02a44bb..1880e23a17 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/SessionViewDelegateState.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/SessionViewDelegateState.kt @@ -9,7 +9,7 @@ sealed class SessionViewDelegateState() { data class SecurityDelay(val ms: Int, val totalDurationSeconds: Int) : SessionViewDelegateState() data class Delay(val total: Int, val current: Int, val step: Int) : SessionViewDelegateState() data class Ready(val cardId: String?, val message: Message?) : SessionViewDelegateState() - data class PinRequested(val callback: (pin: String?) -> Unit) : SessionViewDelegateState() + data class PinRequested(val callback: (pin: String) -> Unit) : SessionViewDelegateState() object TagLost : SessionViewDelegateState() object TagConnected : SessionViewDelegateState() object WrongCard : SessionViewDelegateState() diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt index e9a6ab555c..0022ac2066 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt @@ -1,7 +1,9 @@ package com.tangem.tangem_sdk_new.extensions import androidx.fragment.app.FragmentActivity +import com.squareup.sqldelight.android.AndroidSqliteDriver import com.tangem.Config +import com.tangem.Database import com.tangem.SessionViewDelegate import com.tangem.TangemSdk import com.tangem.tangem_sdk_new.DefaultSessionViewDelegate @@ -15,7 +17,8 @@ fun TangemSdk.Companion.init(activity: FragmentActivity, config: Config = Config val viewDelegate = DefaultSessionViewDelegate(nfcManager.reader) viewDelegate.activity = activity - val tangemSdk = TangemSdk(nfcManager.reader, viewDelegate, config) + val databaseDriver = AndroidSqliteDriver(Database.Schema, activity.applicationContext, "cards.db") + val tangemSdk = TangemSdk(nfcManager.reader, viewDelegate, config, databaseDriver) tangemSdk.setTerminalKeysService(TerminalKeysStorage(activity.application)) return tangemSdk @@ -32,7 +35,8 @@ fun TangemSdk.Companion.customInit( nfcManager.reader, viewDelegate ?: DefaultSessionViewDelegate(nfcManager.reader) .apply { this.activity = activity }, - config + config, + AndroidSqliteDriver(Database.Schema, activity.applicationContext, "cards.db") ) tangemSdk.setTerminalKeysService(TerminalKeysStorage(activity.application)) diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/ui/NfcSessionDialog.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/ui/NfcSessionDialog.kt index 3ece303dc9..e1ffec7f98 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/ui/NfcSessionDialog.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/ui/NfcSessionDialog.kt @@ -10,7 +10,6 @@ import android.view.inputmethod.InputMethodManager import android.widget.TextView import androidx.fragment.app.FragmentActivity import com.google.android.material.bottomsheet.BottomSheetDialog -import com.tangem.Log import com.tangem.tangem_sdk_new.R import com.tangem.tangem_sdk_new.SessionViewDelegateState import com.tangem.tangem_sdk_new.extensions.localizedDescription @@ -24,6 +23,9 @@ class NfcSessionDialog(val activity: FragmentActivity) : BottomSheetDialog(activ private var currentState: SessionViewDelegateState? = null fun show(state: SessionViewDelegateState) { + if (!this.isShowing) { + this.show() + } when (state) { is SessionViewDelegateState.Ready -> onReady(state) is SessionViewDelegateState.Success -> onSuccess(state) @@ -135,14 +137,21 @@ class NfcSessionDialog(val activity: FragmentActivity) : BottomSheetDialog(activ etPin?.setOnEditorActionListener { v: TextView?, actionId: Int, event: KeyEvent? -> postUI { if (actionId == KeyEvent.KEYCODE_ENDCALL) { - show(lTouchCard) - tvTaskTitle?.show() - tvTaskText?.show() - val imm: InputMethodManager = - context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(v?.windowToken, 0) + if (v?.text.isNullOrBlank()) { + etPin?.error = "" + } else { + show(lTouchCard) + tvTaskTitle?.show() + tvTaskText?.show() + val imm: InputMethodManager = + context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(v?.windowToken, 0) - v?.text?.toString()?.let { state.callback(it) } + v?.text.toString().let { pin -> + v?.text = "" + state.callback(pin) + } + } } } true