Updated on 2026-08-14

This commit is contained in:
Tangem 2019-11-15 06:41:08 +00:00
commit 4c64438643
28 changed files with 373 additions and 65 deletions

View file

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":tangem-card-old" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":tangem-card-old" />
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" />
<option name="LAST_KNOWN_AGP_VERSION" />
</configuration>
</facet>
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="true" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/classes/java/main" />
<output-test url="file://$MODULE_DIR$/build/classes/java/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="Gradle: prov-1.56.0.0" level="project" />
<orderEntry type="library" exported="" name="Gradle: core-1.56.0.0" level="project" />
<orderEntry type="library" exported="" name="Gradle: eddsa-0.3.0" level="project" />
</component>
</module>

View file

@ -1,6 +1,10 @@
package com.tangem
/**
* Contains data relating to a Tangem card. It is used in constructing all the commands,
* and commands can return modified [CardEnvironment].
*/
data class CardEnvironment(
val pin1: String = DEFAULT_PIN,
val pin2: String = DEFAULT_PIN2,

View file

@ -8,6 +8,15 @@ import com.tangem.crypto.CryptoUtils
import com.tangem.tasks.*
import java.util.concurrent.Executors
/**
* The main interface of Tangem SDK that allows your app to communicate with Tangem cards.
*
* @property reader is an interface that is responsible for NFC connection and
* transfer of data to and from the Tangem Card.
* Its default implementation, NfcCardReader, is in our tangem-sdk module.
* @property cardManagerDelegate An interface that allows interaction with users and shows relevant UI.
* Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module.
*/
class CardManager(
private val reader: CardReader,
private val cardManagerDelegate: CardManagerDelegate? = null) {
@ -20,11 +29,41 @@ class CardManager(
CryptoUtils.initCrypto()
}
/**
* To start using any card, you first need to read it using the scanCard() method.
* This method launches an NFC session, and once its connected with the card,
* it obtains the card data. Optionally, if the card contains a wallet (private and public key pair),
* it proves that the wallet owns a private key that corresponds to a public one.
*
* It launches on the new thread a [ScanTask] that will send the following events in a callback:
* [ScanEvent.OnReadEvent] after completing [com.tangem.commands.ReadCommand]
* [ScanEvent.OnVerifyEvent] after completing [com.tangem.commands.CheckWalletCommand]
* [TaskEvent.Completion] with an error field null after successful completion of a task or
* [TaskEvent.Completion] with a [TaskError] if some error occurs.
*/
fun scanCard(callback: (result: TaskEvent<ScanEvent>) -> Unit) {
val task = ScanTask()
runTask(task, callback = callback)
}
/**
* This method allows you to sign one or multiple hashes.
* Simultaneous signing of array of hashes in a single [SignCommand] is required to support
* Bitcoin-type multi-input blockchains (UTXO).
* The [SignCommand] will return a corresponding array of signatures.
*
* This method launches on the new thread [SignCommand] that will send the following events in a callback:
* [SignResponse] after completing [SignCommand]
* [TaskEvent.Completion] with an error field null after successful completion of a task or
* [TaskEvent.Completion] with a [TaskError] if some error occurs.
* Please note that Tangem cards usually protect the signing with a security delay
* that may last up to 90 seconds, depending on a card.
* It is for [CardManagerDelegate] to notify users of security delay.
* @param hashes Array of transaction hashes. It can be from one or up to ten hashes of the same length.
* @param cardId CID, Unique Tangem card ID number
* @param callback
*
*/
fun sign(hashes: Array<ByteArray>, cardId: String,
callback: (result: TaskEvent<SignResponse>) -> Unit) {
val signCommand: SignCommand
@ -42,6 +81,9 @@ class CardManager(
runTask(task, cardId, callback)
}
/**
*/
fun <T> runTask(task: Task<T>, cardId: String? = null,
callback: (result: TaskEvent<T>) -> Unit) {
if (isBusy) {
@ -63,6 +105,9 @@ class CardManager(
}
}
/**
*/
fun <T : CommandResponse> runCommand(command: CommandSerializer<T>,
cardId: String? = null,
callback: (result: TaskEvent<T>) -> Unit) {

View file

@ -3,14 +3,43 @@ package com.tangem
import com.tangem.common.CompletionResult
import com.tangem.tasks.TaskError
/**
* Allows interaction with users and shows visual elements.
*
* Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module.
*/
interface CardManagerDelegate {
/**
* It is called when user is expected to scan a Tangem Card with an Android device.
*/
fun onNfcSessionStarted()
/**
* It is called when security delay is triggered by the card.
* A user is expected to hold the card until the security delay is over.
*/
fun onSecurityDelay(ms: Int)
/**
* It is called when user takes the card away from the Android device during the scanning
* (for example when security delay is in progress) and the TagLostException is received.
*/
fun onTagLost()
/**
* It is called when NFC session was completed and a user can take the card away from the Android device.
*/
fun onNfcSessionCompleted()
/**
* It is called when some error occur during NFC session.
*/
fun onError(error: TaskError? = null)
fun requestPin(callback: (result: CompletionResult<String>) -> Unit)
/**
* It is called when a user is expected to enter pin code.
*/
fun onPinRequested(callback: (result: CompletionResult<String>) -> Unit)
}

View file

@ -4,9 +4,29 @@ import com.tangem.common.CompletionResult
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.ResponseApdu
/**
* Allows interaction between the phone or any other terminal and Tangem card.
*
* Its default implementation, NfcCardReader, is in our tangem-sdk module.
*/
interface CardReader {
var readingActive: Boolean
/**
* Sends data to the card and receives the reply.
*
* @param apdu Data to be sent. [CommandApdu] serializes it to a [ByteArray]
* @param callback Returns response from the card,
* [ResponseApdu] Allows to convert raw data to [Tlv]
*/
fun transceiveApdu(apdu: CommandApdu, callback: (response: CompletionResult<ResponseApdu>) -> Unit)
fun startNfcSession()
/**
* Signals to [CardReader] to become ready to transceive data.
*/
fun openSession()
/**
* Signals to [CardReader] that no further NFC transition is expected.
*/
fun closeSession()
}

View file

@ -22,6 +22,11 @@ object Log {
}
}
/**
* Interface for logging events within the SDK.
*
* It allows to use Android logger or to choose another.
*/
interface LoggerInterface {
fun i(logTag: String, message: String)
fun e(logTag: String, message: String)

View file

@ -11,13 +11,27 @@ import com.tangem.common.tlv.TlvMapper
import com.tangem.common.tlv.TlvTag
import com.tangem.tasks.TaskError
/**
* Deserialized response from the Tangem card after [CheckWalletCommand].
*
* @property cardId Unique Tangem card ID number
* @property salt Random salt generated by the card
* @property walletSignature Challenge and salt signed with the wallet private key.
*/
class CheckWalletResponse(
val cardId: String,
val salt: ByteArray,
val walletSignature: ByteArray
) : CommandResponse
/**
* This command proves that the wallet private key from the card corresponds to the wallet public key.
* Standard challenge/response scheme is used.
*
* @property pin1 Hashed users pin 1 code to access the card. Default unhashed value: 000000.
* @property cardId Unique Tangem card ID number
* @property challenge Random challenge generated by application
*/
class CheckWalletCommand(
private val pin1: String,
private val cardId: String,

View file

@ -2,22 +2,41 @@ package com.tangem.commands
import com.tangem.CardEnvironment
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.Instruction
import com.tangem.common.apdu.ResponseApdu
import com.tangem.common.extentions.toInt
import com.tangem.common.tlv.TlvTag
/**
* Simple interface for responses received after sending commands to Tangem cards.
*/
interface CommandResponse
/**
* Abstract class for all Tangem card commands.
*/
abstract class CommandSerializer<T : CommandResponse> {
abstract val instruction: Instruction
abstract val instructionCode: Int
/**
* Serializes data into a [List] of [com.tangem.common.tlv.Tlv],
* then creates [CommandApdu] with this data.
*
* @return Command data that can be converted to raw bytes with a method [CommandApdu.toBytes].
*/
abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu
/**
* Deserializes data, received from a card and stored in [ResponseApdu],
* into a [List] of [com.tangem.common.tlv.Tlv]. Then this method maps it into a [CommandResponse].
*
* @return Card response, converted to a [CommandResponse] of a type [T].
*/
abstract fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): T?
/**
* Helper method to parse security delay information received from a card.
*
* @return Remaining security delay in milliseconds.
*/
fun deserializeSecurityDelay(responseApdu: ResponseApdu, cardEnvironment: CardEnvironment): Int? {
val tlv = responseApdu.getTlvData(cardEnvironment.encryptionKey)
return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt()

View file

@ -71,7 +71,7 @@ enum class ProductMask(val code: Byte) {
/**
* Stores and maps Tangem card settings.
*
* @property rawValue are card settings in a form of flags,
* @property rawValue Card settings in a form of flags,
* while flags definitions and values are in [SettingsMask.Companion] as constants.
*/
data class SettingsMask(val rawValue: Int) {
@ -152,7 +152,6 @@ class Card(
/**
* This command receives from the Tangem Card all the data about the card and the wallet,
* including unique card number (CID or cardId) that has to be submitted while calling all other commands.
*
*/
class ReadCommand : CommandSerializer<Card>() {

View file

@ -12,6 +12,13 @@ import com.tangem.common.tlv.TlvTag
import com.tangem.crypto.sign
import com.tangem.tasks.TaskError
/**
* @param cardId CID, Unique Tangem card ID number
* @param signature Signed hashes (array of resulting signatures)
* @param remainingSignatures Remaining number of sign operations before the wallet will stop signing transactions.
* @param signedHashes Total number of signed single hashes returned by the card in sign command responses.
* Sums up array elements within all SIGN commands
*/
class SignResponse(
val cardId: String,
val signature: ByteArray,
@ -19,7 +26,12 @@ class SignResponse(
val signedHashes: Int
) : CommandResponse
/**
* Signs transaction hashes using a wallet private key, stored on the card.
*
* @property hashes Array of transaction hashes.
* @property cardId CID, Unique Tangem card ID number
*/
class SignCommand(private val hashes: Array<ByteArray>, private val cardId: String)
: CommandSerializer<SignResponse>() {
@ -51,6 +63,14 @@ class SignCommand(private val hashes: Array<ByteArray>, private val cardId: Stri
return CommandApdu(Instruction.Sign, tlvData)
}
/**
d
* Application can optionally submit a public key Terminal_PublicKey in [SignCommand].
* Submitted key is stored by the Tangem card if it differs from a previous submitted Terminal_PublicKey.
* The Tangem card will not enforce security delay if [SignCommand] will be called with
* TerminalTransactionSignature parameter containing a correct signature of raw data to be signed made with TerminalPrivateKey
* (this key should be generated and securily stored by the application).
*/
private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvData: MutableList<Tlv>) {
cardEnvironment.terminalKeys?.let { terminalKeyPair ->
val signedData = dataToSign.sign(terminalKeyPair.privateKey)

View file

@ -1,7 +1,12 @@
package com.tangem.common
import com.tangem.common.CompletionResult.Success
import com.tangem.tasks.TaskError
/**
* Response class encapsulating successful and failed results.
* @param T Type of data that is returned in [Success].
*/
sealed class CompletionResult<T> {
class Success<T>(val data: T) : CompletionResult<T>()
class Failure<T>(val error: TaskError) : CompletionResult<T>()

View file

@ -5,6 +5,13 @@ import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.toBytes
import java.io.ByteArrayOutputStream
/**
* Class that provides conversion of serialized request and Instruction code
* to a raw data that can be sent to the card.
*
* @property ins Instruction code that determines the type of request for the card.
* @property tlvList A list of TLVs that are to be sent to the card
*/
class CommandApdu(
private val ins: Int,
@ -31,6 +38,10 @@ class CommandApdu(
encryptionKey = encryptionKey
)
/**
* Request converted to a raw data
*/
val apduData: ByteArray
init {

View file

@ -1,5 +1,9 @@
package com.tangem.common.apdu
/**
* Instruction code that determines the type of the command that is sent to the Tangem card.
* It is used in the construction of [com.tangem.common.apdu.CommandApdu].
*/
enum class Instruction(var code: Int) {
Unknown(0x00),
Read(0xF2),

View file

@ -2,6 +2,13 @@ package com.tangem.common.apdu
import com.tangem.common.tlv.Tlv
/**
* Stores response data from the card and parses it to [Tlv] and [StatusWord].
*
* @property data Raw response from the card.
* @property sw Status word code, reflecting the status of the response.
* @property statusWord Parsed status word.
*/
class ResponseApdu(val data: ByteArray) {
private val sw1: Int = 0x00FF and data[data.size - 2].toInt()
@ -11,6 +18,12 @@ class ResponseApdu(val data: ByteArray) {
val statusWord: StatusWord = StatusWord.byCode(sw)
/**
* Converts raw response data to the list of TLVs.
*
* @param encryptionKey key to decrypt response.
* (Encryption / decryption functionality is not implemented yet.)
*/
fun getTlvData(encryptionKey: ByteArray? = null): List<Tlv>? {
return when {
data.size < 2 -> null

View file

@ -1,6 +1,9 @@
package com.tangem.common.apdu
enum class StatusWord (val code: Int, val description: String){
/**
* Part of a response from the card, shows the status of the operation
*/
enum class StatusWord(val code: Int, val description: String) {
ProcessCompleted(0x9000, "SW_PROCESS_COMPLETED"),
InvalidParams(0x6A86, "SW_INVALID_PARAMS"),

View file

@ -5,6 +5,9 @@ import java.security.MessageDigest
import java.util.*
import kotlin.experimental.and
/**
* Extension functions for [ByteArray].
*/
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }

View file

@ -3,7 +3,9 @@ package com.tangem.common.extentions
import java.nio.charset.Charset
import java.security.MessageDigest
/**
* Extension functions for [String].
*/
fun String.calculateSha256(): ByteArray {
val sha256 = MessageDigest.getInstance("SHA-256")
val data = this.toByteArray(Charset.forName("UTF-8"))

View file

@ -3,6 +3,9 @@ package com.tangem.common.tlv
import java.io.ByteArrayInputStream
import java.io.IOException
/**
* The data converted to the Tag Length Value protocol.
*/
class Tlv {
val tag: TlvTag

View file

@ -14,8 +14,22 @@ class MissingTagException(message: String? = null) : TlvMapperException(message)
class WrongTypeException(message: String? = null) : TlvMapperException(message)
class ConversionException(message: String? = null) : TlvMapperException(message)
/**
* Maps value fields in [Tlv] from raw [ByteArray] to concrete classes
* according to their [TlvTag] and corresponding [TlvValueType].
*
* @property tlvList List of TLVs, which values are to be converted to particular classes.
*/
class TlvMapper(val tlvList: List<Tlv>) {
/**
* Finds [Tlv] by its [TlvTag].
* Returns null if [Tlv] is not found, otherwise converts its value to [T].
*
* @param tag [TlvTag] of a [Tlv] which value is to be returned.
*
* @return Value converted to a nullable type [T].
*/
inline fun <reified T> mapOptional(tag: TlvTag): T? =
try {
map<T>(tag)
@ -23,6 +37,17 @@ class TlvMapper(val tlvList: List<Tlv>) {
null
}
/**
* Finds [Tlv] by its [TlvTag].
* Throws [MissingTagException] if [Tlv] is not found,
* otherwise converts [Tlv] value to [T].
*
* @param tag [TlvTag] of a [Tlv] which value is to be returned.
*
* @return [Tlv] value converted to a nullable type [T].
*
* @throws [MissingTagException] if no [Tlv] is found by the Tag.
*/
inline fun <reified T> map(tag: TlvTag): T {
val tlvValue: ByteArray = tlvList.find { it.tag == tag }?.value
?: if (tag.valueType() == TlvValueType.BoolValue && T::class == Boolean::class) {

View file

@ -1,5 +1,8 @@
package com.tangem.common.tlv
/**
* Contains all possible value types that value for [TlvTag] can contain.
*/
enum class TlvValueType {
HexString,
Utf8String,
@ -14,6 +17,9 @@ enum class TlvValueType {
SigningMethod
}
/**
* Contains all TLV tags, with their code and descriptive name.
*/
enum class TlvTag(val code: Int) {
Unknown(0x00),
CardId(0x01),
@ -97,13 +103,9 @@ enum class TlvTag(val code: Int) {
TerminalPublicKey(0x5C),
TerminalTransactionSignature(0x57);
fun hasNestedTlv(): Boolean {
return when (this) {
TlvTag.CardData -> true
else -> false
}
}
/**
* @return [TlvValueType] associated with a [TlvTag]
*/
fun valueType(): TlvValueType {
return when (this) {
CardId, Pin, Batch -> TlvValueType.HexString

View file

@ -13,21 +13,65 @@ object CryptoUtils {
Security.addProvider(EdDSASecurityProvider())
}
/**
* Generates ByteArray of random bytes.
* It is used, among other things, to generate helper private keys
* (not the one for the blockchains, that one is generated on the card and does not leave the card).
*
* @param length length of the ByteArray that is to be generated.
*/
fun generateRandomBytes(length: Int): ByteArray {
val bytes = ByteArray(length)
SecureRandom().nextBytes(bytes)
return bytes
}
/**
* Helper function to verify that the data was signed with a private key that corresponds
* to the provided public key.
*
* @param publicKey Corresponding to the private key that was used to sing a message
* @param message The data that was signed
* @param signature Signed data
* @param curve Elliptic curve used
*
* @return Result of a verification
*/
fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray,
curve: EllipticCurve = EllipticCurve.Secp256k1): Boolean {
return when (curve) {
EllipticCurve.Secp256k1 -> verifySecp256k1(publicKey, message, signature)
EllipticCurve.Ed25519 -> verifyEd25519(publicKey, message, signature)
EllipticCurve.Secp256k1 -> Sepc256k1.verify(publicKey, message, signature)
EllipticCurve.Ed25519 -> Ed25519.verify(publicKey, message, signature)
}
}
/**
* Helper function that generates public key from a private key.
*
* @param privateKeyArray A private key from which a public key is generated
* @param curve Elliptic curve used
*
* @return Public key [ByteArray]
*/
fun generatePublicKey(
privateKeyArray: ByteArray,
curve: EllipticCurve = EllipticCurve.Secp256k1
): ByteArray {
return when (curve) {
EllipticCurve.Secp256k1 -> Sepc256k1.generatePublicKey(privateKeyArray)
EllipticCurve.Ed25519 -> Ed25519.generatePublicKey(privateKeyArray)
}
}
}
/**
* Extension function to sign a ByteArray with an elliptic curve cryptography.
*
* @param privateKeyArray Key to sign data
* @param curve Elliptic curve that is used to sign data
*
* @return Signed data
*/
fun ByteArray.sign(privateKeyArray: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1): ByteArray {
return when (curve) {
EllipticCurve.Secp256k1 -> signSecp256k1(this, privateKeyArray)

View file

@ -7,12 +7,27 @@ import com.tangem.commands.ReadCommand
import com.tangem.common.CompletionResult
import com.tangem.crypto.CryptoUtils
/**
* Events that [ScanTask] returns on completion of its commands.
*/
sealed class ScanEvent {
/**
* Contains data from a Tangem card after successful completion of [ReadCommand].
*/
data class OnReadEvent(val card: Card) : ScanEvent()
/**
* Shows whether the Tangem card was verified on completion of [CheckWalletCommand].
*/
data class OnVerifyEvent(val isGenuine: Boolean) : ScanEvent()
}
/**
* Task that allows to read Tangem card and verify its private key.
*
* It performs two commands, [ReadCommand] and [CheckWalletCommand], subsequently.
*/
internal class ScanTask : Task<ScanEvent>() {
override fun onRun(cardEnvironment: CardEnvironment,

View file

@ -5,6 +5,11 @@ import com.tangem.commands.CommandResponse
import com.tangem.commands.CommandSerializer
import com.tangem.common.CompletionResult
/**
* Allows to perform a single command.
*
* @property command A command that will be performed.
*/
class SingleCommandTask<Event : CommandResponse>(
private val command: CommandSerializer<Event>
) : Task<Event>() {

View file

@ -10,6 +10,10 @@ import com.tangem.common.CompletionResult
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.StatusWord
/**
* An error class that represent typical errors that may occur when performing Tangem SDK tasks.
* Errors are propagated back to the caller in callbacks.
*/
sealed class TaskError(description: String? = null) : Exception(description) {
class UnknownStatus(sw: Int) : TaskError("Unknown StatusWord: $sw")
class MappingError : TaskError()
@ -36,24 +40,54 @@ sealed class TaskError(description: String? = null) : Exception(description) {
class HashSizeMustBeEqual() : TaskError()
}
/**
* Events that are are sent in callbacks from [Task].
*/
sealed class TaskEvent<T> {
/**
* A callback that is triggered by a Task.
*/
class Event<T>(val data: T) : TaskEvent<T>()
/**
* A callback that is triggered when a [Task] is completed.
*
* @param error is null if it's a successful completion of a [Task]
*/
class Completion<T>(val error: TaskError? = null) : TaskEvent<T>()
}
/**
* Allows to perform a group of commands interacting between the card and the application.
* A task opens an NFC session, sends commands to the card and receives its responses,
* repeats the commands if needed, and closes session after receiving the last answer.
*/
abstract class Task<T> {
var delegate: CardManagerDelegate? = null
var reader: CardReader? = null
/**
* This method should be called to run the [Task] and perform all its operations.
*
* @param cardEnvironment Relevant current version of a card environment
* @param callback It will be triggered during the performance of the [Task]
*/
fun run(cardEnvironment: CardEnvironment,
callback: (result: TaskEvent<T>) -> Unit) {
delegate?.onNfcSessionStarted()
reader?.startNfcSession()
reader?.openSession()
Log.i(this::class.simpleName!!, "Nfc task is started")
onRun(cardEnvironment, callback)
}
/**
* Should be called on [Task] completion, whether it was successful or with failure.
*
* @param withError True when there is an error
* @param taskError The error to be shown by [CardManagerDelegate]
*/
protected fun completeNfcSession(withError: Boolean = false, taskError: TaskError? = null) {
reader?.closeSession()
if (withError) {
@ -63,9 +97,16 @@ abstract class Task<T> {
}
}
/**
* In this method the individual Tasks' logic should be implemented.
*/
protected abstract fun onRun(cardEnvironment: CardEnvironment,
callback: (result: TaskEvent<T>) -> Unit)
/**
* This method should be called by Tasks in their [onRun] method wherever
* they need to communicate with the Tangem Card by launching commands.
*/
protected fun <T : CommandResponse> sendCommand(
command: CommandSerializer<T>,
cardEnvironment: CardEnvironment,
@ -116,10 +157,10 @@ abstract class Task<T> {
}
is CompletionResult.Failure ->
if (result.error is TaskError.TagLost) {
delegate?.hideSecurityDelay()
delegate?.onTagLost()
} else if (result.error is TaskError.UserCancelledError) {
callback(CompletionResult.Failure(TaskError.UserCancelledError()))
reader?.readingActive = false
reader?.closeSession()
}
}
}

View file

@ -14,7 +14,10 @@ import com.tangem.tasks.TaskError
import kotlinx.android.synthetic.main.layout_touch_card.*
import kotlinx.android.synthetic.main.nfc_bottom_sheet.*
/**
* Default implementation of [CardManagerDelegate].
* If no customisation is required, this is the preferred way to use Tangem SDK.
*/
class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDelegate {
lateinit var activity: FragmentActivity

View file

@ -6,7 +6,9 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.tangem.tangem_sdk_new.nfc.NfcManager
/**
* [LifecycleObserver] for [NfcManager], helps to coordinate NFC modes with Activity lifecycle.
*/
class NfcLifecycleObserver(private var nfcManager: NfcManager) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)

View file

@ -12,6 +12,11 @@ import android.os.Build
import android.os.Bundle
import com.tangem.Log
/**
* Helps use of NFC, leveraging Android NFC functionality.
* Launches [NfcAdapter], manages it with [Activity] lifecycle,
* enables and disables Nfc Reading Mode, receives NFC [Tag].
*/
class NfcManager : NfcAdapter.ReaderCallback {
val reader = NfcReader()

View file

@ -10,6 +10,9 @@ import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.ResponseApdu
import com.tangem.tasks.TaskError
/**
* Provides NFC communication between an Android application and Tangem card.
*/
class NfcReader : CardReader {
override var readingActive = false