Updated on 2026-08-14

This commit is contained in:
Tangem 2020-02-27 14:26:43 +03:00
parent b125063fab
commit 1abafd26b4
14 changed files with 286 additions and 23 deletions

View file

@ -125,7 +125,7 @@ dependencies {
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation "com.orhanobut:hawk:2.0.1" implementation "com.orhanobut:hawk:2.0.1"
implementation 'co.nstant.in:cbor:0.8' implementation 'co.nstant.in:cbor:0.8'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.2'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.2" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.2"
// implementation 'org.bitcoinj:bitcoinj-parent:0.14.7' //TODO: is this needed? // implementation 'org.bitcoinj:bitcoinj-parent:0.14.7' //TODO: is this needed?

View file

@ -31,7 +31,7 @@ dependencies {
implementation project(':tangem-core') implementation project(':tangem-core')
implementation project(':tangem-sdk') implementation project(':tangem-sdk')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.core:core-ktx:1.1.0'

View file

@ -1,15 +1,15 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.61' apply from: 'dependencies.gradle'
repositories { repositories {
google() google()
jcenter() jcenter()
maven { url 'https://maven.fabric.io/public' } maven { url 'https://maven.fabric.io/public' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.3' classpath "com.android.tools.build:gradle:$versions.build_gradle"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath "io.fabric.tools:gradle:1.31.0"
classpath 'io.fabric.tools:gradle:1.31.0'
} }
} }

7
dependencies.gradle Normal file
View file

@ -0,0 +1,7 @@
ext.versions = [
kotlin : '1.3.61',
build_gradle: '3.6.0',
gitpackGroup: 'com.github.Tangem',
gitpackCore : '0.2.1',
gitpackSdk : '0.2.1',
]

View file

@ -1,6 +1,6 @@
#Thu Aug 22 12:21:00 MSK 2019 #Thu Feb 27 13:33:42 AST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View file

@ -1,6 +1,5 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'com.github.dcendents.android-maven'
group='com.tangem' group='com.tangem'
@ -41,7 +40,7 @@ dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
} }
repositories { repositories {
mavenCentral() mavenCentral()

View file

@ -1,5 +1,4 @@
apply plugin: 'java-library' apply plugin: 'java-library'
apply plugin: 'com.github.dcendents.android-maven'
group='com.tangem' group='com.tangem'

View file

@ -1,12 +1,13 @@
apply plugin: "kotlin" apply plugin: "kotlin"
apply plugin: 'org.jetbrains.dokka' apply plugin: 'org.jetbrains.dokka'
apply from: '../dependencies.gradle'
group = 'com.github.TangemCash' group = "$versions.gitpackGroup"
version '0.1.0' version "$versions.gitpackCore"
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation "com.madgag.spongycastle:core:1.58.0.0" implementation "com.madgag.spongycastle:core:1.58.0.0"
implementation "com.madgag.spongycastle:prov:1.58.0.0" implementation "com.madgag.spongycastle:prov:1.58.0.0"
implementation 'net.i2p.crypto:eddsa:0.3.0' implementation 'net.i2p.crypto:eddsa:0.3.0'
@ -19,14 +20,13 @@ sourceCompatibility = "8"
targetCompatibility = "8" targetCompatibility = "8"
buildscript { buildscript {
ext.kotlin_version = '1.3.61'
ext.dokka_version = '0.10.0' ext.dokka_version = '0.10.0'
repositories { repositories {
mavenCentral() mavenCentral()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
} }
} }

View file

@ -36,7 +36,7 @@ dependencies {
implementation project(':tangem-sdk') implementation project(':tangem-sdk')
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

View file

@ -0,0 +1,231 @@
[![Release](https://jitpack.io/v/Tangem/tangem-sdk-android.svg)]
# 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.
- [Getting Started](#getting-started)
- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Initialization](#initialization)
- [Card interaction](#card-interaction)
- [Scan card](#scan-card)
- [Sign](#sign)
- [Customization](#customization)
- [UI](#ui)
- [Tasks](#tasks)
- [Localization](#localization)
## Getting Started
### Requirements
Android with minimal SDK version of 21 and a device with NFC support
### Installation
1) Add Tangem library to the project:
Add to a project build.gradle file:
```gradle
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}
```
And add Tangem library to the dependencies (in an app or module build.gradle file):
```gradle
dependencies {
implementation "com.github.tangem.tangem-sdk-android:tangem-core:$latestVersionOfCore"
implementation "com.github.tangem.tangem-sdk-android:tangem-sdk:$latestVersionOfSdk"
}
```
Tangem Core is a JVM library (without Android dependencies) that provides core functionality of interacting with Tangem cards.
Tangem Sdk is an Android library that implements NFC interaction between Android devices and Tangem cards and graphical interface for this interaction.
2) Add res file tech_filter with the following:
```xml
<resources>
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NfcV</tech>
</tech-list>
</resources>
```
3) Add to Manifest:
```xml
<intent-filter>
<action android:name=“android.nfc.action.TECH_DISCOVERED” />
</intent-filter>
<meta-data
android:name=“android.nfc.action.TECH_DISCOVERED”
android:resource=“@xml/tech_filter” />
```
## Usage
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`.
```kotlin
private val nfcManager = NfcManager()
private val cardManagerDelegate: DefaultCardManagerDelegate = DefaultCardManagerDelegate(nfcManager.reader)
private val cardManager = CardManager(nfcManager.reader, cardManagerDelegate)
```
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.
```kotlin
nfcManager.setCurrentActivity(this)
cardManagerDelegate.activity = this
```
```kotlin
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
```
Default implementation of `NfcManager`, `CardReader` and `CardManagerDelegate` allows you to start using CardManager in your application without any additional setup.
You can also provide your implementations of `CardReader` and `CardManagerDelegate`.
You can read more about this in [Customization](#сustomization).
### Tasks
#### 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 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.
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
}
}
}
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:
`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.
`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 cards authenticity has been verified. If the card is authentic, isGenuine will be set to true, otherwise, it will be set to false.
#### 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
}
// Handle other errors
}
// Handle completion
}
}
}
```
## 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.
```kolin
val myCardManagerDelegate = MyCardManagerDelegate()
val cardManager = CardManager(cardManagerDelegate = myCardManagerDelegate)
```
> If you pass null instead of `cardManagerDelegate`, the SDK wont be able to process errors that require user intervention and return them to `.failure(let error)`.
### 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.
```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.
Its 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 dont 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
}
// Handle other errors
}
// Handle completion
}
}
}
```

View file

@ -0,0 +1,26 @@
buildscript {
apply from: 'dependencies.gradle'
repositories {
google()
jcenter()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath "com.android.tools.build:gradle:$versions.build_gradle"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath "com.github.dcendents:android-maven-gradle-plugin:2.1"
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -0,0 +1 @@
include ':tangem-core', ':tangem-sdk', ':tangem-demo'

View file

@ -1,6 +1,5 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'com.github.dcendents.android-maven'
group='com.tangem' group='com.tangem'
@ -51,7 +50,7 @@ dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
} }
repositories { repositories {

View file

@ -1,9 +1,10 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply from: '../dependencies.gradle'
group = 'com.github.TangemCash' group = "$versions.gitpackGroup"
version '0.1.0' version "$versions.gitpackSdk"
android { android {
compileSdkVersion 29 compileSdkVersion 29
@ -38,7 +39,7 @@ android {
dependencies { dependencies {
implementation project(':tangem-core') implementation project(':tangem-core')
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.material:material:1.1.0'
implementation 'com.skyfishjy.ripplebackground:library:1.0.1' implementation 'com.skyfishjy.ripplebackground:library:1.0.1'