Updated on 2026-08-14
This commit is contained in:
parent
99c8d24f7c
commit
9daf982e41
5 changed files with 392 additions and 137 deletions
|
|
@ -20,7 +20,7 @@ interface CardSessionRunnable<T : CommandResponse> {
|
|||
|
||||
/**
|
||||
* The starting point for custom business logic.
|
||||
* Implement this interface and use [TangemSdk.startSession] to run.
|
||||
* Implement this interface and use [TangemSdk.startSessionWithRunnable] to run.
|
||||
* @param session run commands in this [CardSession].
|
||||
* @param callback trigger the callback to complete the task.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class TangemSdk(
|
|||
* in the form of [Card] if the task was performed successfully or [SessionError] in case of an error.
|
||||
*/
|
||||
fun scanCard(initialMessage: Message? = null, callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
startSession(ScanTask(), null, initialMessage, callback)
|
||||
startSessionWithRunnable(ScanTask(), null, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +72,7 @@ class TangemSdk(
|
|||
*/
|
||||
fun sign(hashes: Array<ByteArray>, cardId: String, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<SignResponse>) -> Unit) {
|
||||
startSession(SignCommand(hashes), cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(SignCommand(hashes), cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -90,7 +90,7 @@ class TangemSdk(
|
|||
*/
|
||||
fun readIssuerData(cardId: String, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<ReadIssuerDataResponse>) -> Unit) {
|
||||
startSession(ReadIssuerDataCommand(config.issuerPublicKey), cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(ReadIssuerDataCommand(config.issuerPublicKey), cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,7 +109,7 @@ class TangemSdk(
|
|||
*/
|
||||
fun readIssuerExtraData(cardId: String,
|
||||
callback: (result: CompletionResult<ReadIssuerExtraDataResponse>) -> Unit) {
|
||||
startSession(ReadIssuerExtraDataCommand(config.issuerPublicKey), cardId, null, callback)
|
||||
startSessionWithRunnable(ReadIssuerExtraDataCommand(config.issuerPublicKey), cardId, null, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -140,7 +140,7 @@ class TangemSdk(
|
|||
issuerDataCounter,
|
||||
config.issuerPublicKey
|
||||
)
|
||||
startSession(command, cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(command, cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -179,7 +179,7 @@ class TangemSdk(
|
|||
issuerDataCounter,
|
||||
config.issuerPublicKey
|
||||
)
|
||||
startSession(command, cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(command, cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -206,7 +206,7 @@ class TangemSdk(
|
|||
callback: (result: CompletionResult<WriteUserDataResponse>) -> Unit
|
||||
) {
|
||||
val command = WriteUserDataCommand(userData, userProtectedData, userCounter, userProtectedCounter)
|
||||
startSession(command, cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(command, cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -228,7 +228,7 @@ class TangemSdk(
|
|||
*/
|
||||
fun readUserData(cardId: String, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<ReadUserDataResponse>) -> Unit) {
|
||||
startSession(ReadUserDataCommand(), cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(ReadUserDataCommand(), cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -250,7 +250,7 @@ class TangemSdk(
|
|||
*/
|
||||
fun createWallet(cardId: String, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
|
||||
startSession(CreateWalletTask(), cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(CreateWalletTask(), cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -269,7 +269,7 @@ class TangemSdk(
|
|||
*/
|
||||
fun purgeWallet(cardId: String, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<PurgeWalletResponse>) -> Unit) {
|
||||
startSession(PurgeWalletCommand(), cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(PurgeWalletCommand(), cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -287,7 +287,7 @@ class TangemSdk(
|
|||
* */
|
||||
fun depersonalize(cardId: String, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<DepersonalizeResponse>) -> Unit) {
|
||||
startSession(DepersonalizeCommand(), cardId, initialMessage, callback)
|
||||
startSessionWithRunnable(DepersonalizeCommand(), cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -313,7 +313,7 @@ class TangemSdk(
|
|||
initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
val command = PersonalizeCommand(config, issuer, manufacturer, acquirer)
|
||||
startSession(command, null, initialMessage, callback)
|
||||
startSessionWithRunnable(command, null, initialMessage, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -330,7 +330,7 @@ class TangemSdk(
|
|||
* If null, default message will be used.
|
||||
* @callback: Standard [TangemSdk] callback.
|
||||
*/
|
||||
fun <T : CommandResponse> startSession(
|
||||
fun <T : CommandResponse> startSessionWithRunnable(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<T>) -> Unit) {
|
||||
val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage)
|
||||
|
|
@ -349,8 +349,7 @@ class TangemSdk(
|
|||
* @callback: At first, you should check that the [SessionError] is not null,
|
||||
* then you can use the [CardSession] to interact with a card.
|
||||
*/
|
||||
fun <T : CommandResponse> startSession(
|
||||
cardId: String? = null, initialMessage: Message? = null,
|
||||
fun startSession(cardId: String? = null, initialMessage: Message? = null,
|
||||
callback: (session: CardSession, error: SessionError?) -> Unit) {
|
||||
val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage)
|
||||
Thread().run { cardSession.start(callback) }
|
||||
|
|
|
|||
23
tangem-sdk-android-config/.circleci/config.yml
Executable file
23
tangem-sdk-android-config/.circleci/config.yml
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
version: 2.1
|
||||
|
||||
orbs:
|
||||
android: circleci/android@0.2.0
|
||||
|
||||
jobs:
|
||||
build:
|
||||
executor: android/android
|
||||
environment:
|
||||
JVM_OPTS: -Xmx2048m
|
||||
GRADLE_OPTS: -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: ./gradlew build
|
||||
- run:
|
||||
name: Run Tests
|
||||
command: ./gradlew test
|
||||
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
|
||||
path: tangem-core/build/test-results/
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||

|
||||
[](https://jitpack.io/#tangem/tangem-sdk-android)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://circleci.com/gh/Tangem/tangem-sdk-android)
|
||||
# Welcome to Tangem
|
||||
|
||||
The Tangem card is a self-custodial hardware wallet for blockchain assets. The main functions of Tangem cards are to securely create and store a private key from a blockchain wallet and sign blockchain transactions. The Tangem card does not allow users to import/export, backup/restore private keys, thereby guaranteeing that the wallet is unique and unclonable.
|
||||
|
|
@ -12,10 +13,19 @@ The Tangem card is a self-custodial hardware wallet for blockchain assets. The m
|
|||
- [Card interaction](#card-interaction)
|
||||
- [Scan card](#scan-card)
|
||||
- [Sign](#sign)
|
||||
- [Read Issuer Data](#read-issuer-data)
|
||||
- [Read Issuer Extra Data](#read-issuer-extra-data)
|
||||
- [Write Issuer Data](#write-issuer-data)
|
||||
- [Write Issuer Extra Data](#write-issuer-extra-data)
|
||||
- [Write User Data](#write-user-data)
|
||||
- [Read User Data](#read-user-data)
|
||||
- [Create Wallet](#create-wallet)
|
||||
- [Purge Wallet](#purge-wallet)
|
||||
- [Depersonalize](#depersonalize)
|
||||
- [Personalize](#personalize)
|
||||
- [Customization](#customization)
|
||||
- [UI](#ui)
|
||||
- [Tasks](#tasks)
|
||||
- [Localization](#localization)
|
||||
- [Custom Tasks](#custom-tasks)
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
|
@ -41,8 +51,8 @@ And add Tangem library to the dependencies (in an app or module build.gradle fil
|
|||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation "com.github.tangem.tangem-sdk-android:tangem-core:$latestVersionOfCore"
|
||||
implementation "com.github.tangem.tangem-sdk-android:tangem-sdk:$latestVersionOfSdk"
|
||||
implementation "com.github.tangem.tangem-sdk-android:tangem-core:$latestVersion"
|
||||
implementation "com.github.tangem.tangem-sdk-android:tangem-sdk:$latestVersion"
|
||||
}
|
||||
```
|
||||
Tangem Core is a JVM library (without Android dependencies) that provides core functionality of interacting with Tangem cards.
|
||||
|
|
@ -75,155 +85,351 @@ Tangem Sdk is an Android library that implements NFC interaction between Android
|
|||
Tangem SDK is a self-sufficient solution that implements a card abstraction model, methods of interaction with the card and interactions with the user via UI.
|
||||
|
||||
### Initialization
|
||||
To get started, you need to create an instance of the `CardManager` class. It provides the simple way of interacting with the card.
|
||||
Our default implementation of `CardManager` requires `NfcReader` and `DefaultCardManagerDelegate`.
|
||||
To get started, you need to create an instance of the `TangemSdk` class. It provides the simple way of interacting with the card.
|
||||
Our default implementation of `TangemSdk` comes with `NfcReader` and `DefaultSessionViewDelegate` and can be initialized with a static method
|
||||
TangemSdk.init(activity: Activity).
|
||||
|
||||
|
||||
```kotlin
|
||||
private val nfcManager = NfcManager()
|
||||
private val cardManagerDelegate: DefaultCardManagerDelegate = DefaultCardManagerDelegate(nfcManager.reader)
|
||||
private val cardManager = CardManager(nfcManager.reader, cardManagerDelegate)
|
||||
val tangemSdk: TangemSdk = TangemSdk.init(activity)
|
||||
```
|
||||
|
||||
NfcManager requires a reference to activity in order to use Android API for interacting with NFC. DefaultCardManagerDelegate requires a reference to activity in order to render views.
|
||||
TangemSdk requires a reference to activity in order to use Android API for interacting with NFC. DefaultSessionViewDelegate requires a reference to activity in order to render views.
|
||||
|
||||
```kotlin
|
||||
nfcManager.setCurrentActivity(this)
|
||||
cardManagerDelegate.activity = this
|
||||
```
|
||||
Default implementation of `TangemSdk` allows you to start using SDK in your application without any additional setup.
|
||||
|
||||
```kotlin
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
```
|
||||
You can also provide your implementations of `CardReader` and `SessionViewDelegate`.
|
||||
You can read more about this in [Customization](#customization).
|
||||
|
||||
Default implementation of `NfcManager`, `CardReader` and `CardManagerDelegate` allows you to start using CardManager in your application without any additional setup.
|
||||
### Card interaction
|
||||
|
||||
You can also provide your implementations of `CardReader` and `CardManagerDelegate`.
|
||||
You can read more about this in [Customization](#сustomization).
|
||||
|
||||
### Tasks
|
||||
Tangem SDK provides a number of commands and tasks that can be run from a `TangemSdk` class.
|
||||
|
||||
#### Scan card
|
||||
To start using any card, you first need to read it using the `scanCard()` method. This method launches an NFC session, and once it’s 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.
|
||||
To start using any card, you first need to read it using the `scanCard()` method. This method launches an NFC session, and once it’s connected with the card, it obtains the card data. 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.
|
||||
|
||||
Example:
|
||||
|
||||
```kotlin
|
||||
cardManager.scanCard { taskEvent ->
|
||||
when (taskEvent) {
|
||||
is TaskEvent.Event<ScanEvent> -> {
|
||||
when (taskEvent.data) {
|
||||
is ScanEvent.OnReadEvent -> {
|
||||
// Handle returned card data
|
||||
cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
|
||||
// Switch to UI thread to show results in UI
|
||||
runOnUiThread {}
|
||||
}
|
||||
is ScanEvent.OnVerifyEvent -> {
|
||||
//Handle card verification
|
||||
val isGenuine = (taskEvent.data as ScanEvent.OnVerifyEvent).isGenuine
|
||||
tangemSdk.scanCard { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
// Handle returned card data
|
||||
val card = result.data
|
||||
cardId = card.cardId
|
||||
// Switch to UI thread to show results in UI
|
||||
runOnUiThread {
|
||||
tv_card_cid?.text = cardId
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is TaskEvent.Completion<ScanEvent> -> {
|
||||
if (taskEvent.error != null) {
|
||||
if (taskEvent.error is TaskError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
// Handle completion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Communication with the card is an asynchronous operation. In order to get a result for the method, you need to subscribe to the task callback. In order to render the callback results on UI, you need to switch to the main thread.
|
||||
|
||||
Every task can invoke callback several times with different events:
|
||||
Every `CardSessionRunnable` (`Command` or custom tasks) can invoke callback once with either success or error:
|
||||
|
||||
`Completion<T>(val error: TaskError? = null)` – this event is triggered only once when task is completely finished. It means that it's the final callback. If error is not nil, then something went wrong during the operation.
|
||||
`CompletionResult<T>` – this is the sealed class for the results of `CardSessionRunnable`.
|
||||
|
||||
`Event<T>(val data: T)` – this event is triggered when one of operations inside the task is completed.
|
||||
|
||||
**Possible events of the Scan card task:**
|
||||
|
||||
`OnReadEvent(val result: Card)` – this event is triggered after the card has been successfully read. In addition, the obtained card object is contained inside the enum. At this stage, the authenticity of the card is ***NOT*** verified.
|
||||
|
||||
`OnVerifyEvent(val isGenuine: Boolean)` – this event is triggered when the card’s authenticity has been verified. If the card is authentic, isGenuine will be set to true, otherwise, it will be set to false.
|
||||
`Success<T>(val data: T)` is triggered after successful operation and contains a `CommandResponse`.
|
||||
`Failure<T>(val error: SessionError)` is triggered on error.
|
||||
|
||||
#### Sign
|
||||
This method allows you to sign one or multiple hashes. Simultaneous signing of array of hashes in a single SIGN command is required to support Bitcoin-type multi-input blockchains (UTXO). The SIGN command will return a corresponding array of signatures.
|
||||
|
||||
```kotlin
|
||||
cardManager.sign(
|
||||
hashes = arrayOf(hash1, hash2),
|
||||
cardId = card.cardId) { taskEvent ->
|
||||
when (taskEvent) {
|
||||
is TaskEvent.Event -> {
|
||||
// Handle sign response data
|
||||
val signResponse = taskEvent.data
|
||||
}
|
||||
is TaskEvent.Completion<ScanEvent> -> {
|
||||
if (taskEvent.error != null) {
|
||||
if (taskEvent.error is TaskError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
tangemSdk.sign(
|
||||
hashes = arrayOf(hash1, hash2),
|
||||
cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> {
|
||||
val signResponse = result.data
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
// Handle completion
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Read Issuer Data
|
||||
An example of usage (description is available at documentation for `TangemSdk.readIssuerData` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.readIssuerData(cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> {
|
||||
val issuerData = result.data.issuerData
|
||||
val issuerDataSignature = result.data.issuerDataSignature
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Read Issuer Extra Data
|
||||
An example of usage (description is available at documentation for `TangemSdk.readIssuerExtraData` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.readIssuerExtraData(cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> {
|
||||
val issuerData = result.data.issuerData
|
||||
val issuerDataSignature = result.data.issuerDataSignature
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Write Issuer Data
|
||||
An example of usage (description is available at documentation for `TangemSdk.writeIssuerData` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.writeIssuerData(cardId, issuerData, issuerDataSignature, issuerDataCounter) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> {
|
||||
val writeIssuerDataResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Write Issuer Extra Data
|
||||
An example of usage (description is available at documentation for `TangemSdk.writeIssuerExtraData` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.writeIssuerExtraData(cardId,
|
||||
issuerData, startingSignature, finalizingSignature, issuerDataCounter
|
||||
) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val writeIssuerDataResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Write User Data
|
||||
An example of usage (description is available at documentation for `TangemSdk.writeUserData` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.writeUserData(
|
||||
cardId, userData, userProtectedData, userCounter, userProtectedCounter
|
||||
) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val writeUserDataResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Read User Data
|
||||
An example of usage (description is available at documentation for `TangemSdk.readUserData` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.readUserData(cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val readUserDataResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Create Wallet
|
||||
An example of usage (description is available at documentation for `TangemSdk.createWallet` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.createWallet(cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val createWalletResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Purge Wallet
|
||||
An example of usage (description is available at documentation for `TangemSdk.purgeWallet` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.purgeWallet(cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val purgeWalletResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Depersonalize
|
||||
An example of usage (description is available at documentation for `TangemSdk.depersonalize` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.depersonalize(cardId) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val depersonalizeResult = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Personalize
|
||||
An example of usage (description is available at documentation for `TangemSdk.personalize` method and corresponding command class):
|
||||
|
||||
```kotlin
|
||||
tangemSdk.personalize(config, issuer, manufacturer, acquirer) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> runOnUiThread {
|
||||
val card = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Customization
|
||||
### UI
|
||||
If the interaction with user is required, the SDK performs the entire cycle of this interaction. In order to change the appearance or behavior of the user UI, you can provide you own implementation of the `CardManagerDelegate` inteface. After this, initialize the `CardManager` class with your delegate class.
|
||||
If the interaction with user is required, the SDK performs the entire cycle of this interaction. In order to change the appearance or behavior of the user UI, you can provide you own implementation of the `SessionViewDelegate` inteface. After this, initialize the `TangemSdk` class with your delegate class.
|
||||
|
||||
```kolin
|
||||
val myCardManagerDelegate = MyCardManagerDelegate()
|
||||
val cardManager = CardManager(cardManagerDelegate = myCardManagerDelegate)
|
||||
val tangemSdk = TangemSdk.customInit(this, viewDelegate = MySessionViewDelegate())
|
||||
```
|
||||
|
||||
> If you pass null instead of `cardManagerDelegate`, the SDK won’t be able to process errors that require user intervention and return them to `.failure(let error)`.
|
||||
### Custom Tasks
|
||||
`TangemSdk` specific methods run particular commands and tasks. If you want to trigger card commands in a different order, or implement additional
|
||||
business logic, you have several options.
|
||||
|
||||
One option is to start a session, get an instance of `CardSession`, and use it to perform commands.
|
||||
To do this, you need to call `tangemSdk.startSession()` method and get a `CardSession` instance in a callback.
|
||||
|
||||
### Tasks
|
||||
`CardManager` only covers general tasks. If you want to trigger card commands in a certain order, you need to create your own task.
|
||||
|
||||
To do this, you need to create a subclass of the `Task` class, and override the `onRun(..)` method.
|
||||
|
||||
Then call the `runTask(..)` method of the `CardManager` class with you task.
|
||||
> For example, if you want to read the card and immediately sign a transaction with it, you can achieve it this way.
|
||||
|
||||
```kotlin
|
||||
val task = YourTask()
|
||||
cardManager.runTask(task) { taskEvent ->
|
||||
// Handle your events
|
||||
}
|
||||
```
|
||||
> For example, you want to read the card and immediately sign a transaction on it. In such a case, you need to inherit from the `Task` class and override the `onRun(..)` method, in which you implement the required behavior.
|
||||
|
||||
It’s possible to run just one command without the need to create a separate task by using the `runCommand(..)` method.
|
||||
> For example, if you need to read the card details, but don’t need to check the authenticity.
|
||||
```kotlin
|
||||
// Create command
|
||||
val readCommand = ReadCardCommand()
|
||||
// Run command with the callback
|
||||
cardManager.runCommand(readCommand) { taskEvent ->
|
||||
when (taskEvent) {
|
||||
is TaskEvent.Event -> {
|
||||
// Handle returned card data
|
||||
val card: Card = taskEvent.data
|
||||
}
|
||||
is TaskEvent.Completion -> {
|
||||
if (taskEvent.error != null) {
|
||||
if (taskEvent.error is TaskError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
tangemSdk.startSession { session, error ->
|
||||
if (error == null) {
|
||||
session.startWithRunnable(
|
||||
SignCommand(createSampleHashes())) {result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is SessionError.UserCancelledError) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
is CompletionResult.Success -> {
|
||||
val signResponse = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Another option is to put all logic in a in `CardSessionRunnable` class and launch it using `tangemSdk.startSessionWithRunnable()` method.
|
||||
In Tangem SDK we use this approach for our `ScanTask`.
|
||||
|
||||
> For example, you want to read the card and immediately sign a transaction with it. With subclassing `CardSessionRunnable` you can achieve it this way.
|
||||
|
||||
```kotlin
|
||||
class OneTapSignTask(private val hashesToSign: Array<ByteArray>) : CardSessionRunnable<Card> {
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
|
||||
val card = session.environment.card
|
||||
|
||||
if (card == null) {
|
||||
callback(CompletionResult.Failure(SessionError.MissingPreflightRead()))
|
||||
|
||||
} else 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(SessionError.CardError()))
|
||||
|
||||
} else {
|
||||
val signCommand = SignCommand(hashesToSign)
|
||||
|
||||
signCommand.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> callback(CompletionResult.Success(result.data))
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
// Handle completion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,48 @@
|
|||
package com.tangem.tangem_sdk_new.extensions
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.tangem.CardReader
|
||||
import com.tangem.Config
|
||||
import com.tangem.SessionViewDelegate
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.tangem_sdk_new.DefaultSessionViewDelegate
|
||||
import com.tangem.tangem_sdk_new.NfcLifecycleObserver
|
||||
import com.tangem.tangem_sdk_new.TerminalKeysStorage
|
||||
import com.tangem.tangem_sdk_new.nfc.NfcManager
|
||||
|
||||
fun TangemSdk.Companion.init(activity: FragmentActivity): TangemSdk {
|
||||
val nfcManager = NfcManager().apply {
|
||||
this.setCurrentActivity(activity)
|
||||
activity.lifecycle.addObserver(NfcLifecycleObserver(this))
|
||||
}
|
||||
val viewDelegate = DefaultSessionViewDelegate(nfcManager.reader).apply {
|
||||
this.activity = activity
|
||||
}
|
||||
return TangemSdk(nfcManager.reader, viewDelegate).apply {
|
||||
this.setTerminalKeysService(TerminalKeysStorage(activity.application))
|
||||
}
|
||||
fun TangemSdk.Companion.init(activity: FragmentActivity, config: Config = Config()): TangemSdk {
|
||||
val nfcManager = TangemSdk.initNfcManager(activity)
|
||||
|
||||
val viewDelegate = DefaultSessionViewDelegate(nfcManager.reader)
|
||||
viewDelegate.activity = activity
|
||||
|
||||
val tangemSdk = TangemSdk(nfcManager.reader, viewDelegate, config)
|
||||
tangemSdk.setTerminalKeysService(TerminalKeysStorage(activity.application))
|
||||
|
||||
return tangemSdk
|
||||
}
|
||||
|
||||
fun TangemSdk.Companion.customInit(
|
||||
activity: FragmentActivity,
|
||||
viewDelegate: SessionViewDelegate? = null, cardReader: CardReader? = null,
|
||||
config: Config = Config()
|
||||
): TangemSdk {
|
||||
val nfcManager = TangemSdk.initNfcManager(activity)
|
||||
|
||||
val tangemSdk = TangemSdk(
|
||||
cardReader ?: nfcManager.reader,
|
||||
viewDelegate ?: DefaultSessionViewDelegate(nfcManager.reader)
|
||||
.apply { this.activity = activity },
|
||||
config
|
||||
)
|
||||
tangemSdk.setTerminalKeysService(TerminalKeysStorage(activity.application))
|
||||
|
||||
return tangemSdk
|
||||
}
|
||||
|
||||
fun TangemSdk.Companion.initNfcManager(activity: FragmentActivity): NfcManager {
|
||||
val nfcManager = NfcManager()
|
||||
nfcManager.setCurrentActivity(activity)
|
||||
activity.lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
return nfcManager
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue