diff --git a/blockchain-demo/.gitignore b/blockchain-demo/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/blockchain-demo/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/blockchain-demo/build.gradle b/blockchain-demo/build.gradle
new file mode 100644
index 0000000000..6567a8b453
--- /dev/null
+++ b/blockchain-demo/build.gradle
@@ -0,0 +1,63 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+
+
+ defaultConfig {
+ applicationId "com.example.blockchain_demo"
+ minSdkVersion 21
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ packagingOptions {
+ exclude 'lib/x86_64/darwin/libscrypt.dylib'
+ exclude 'lib/x86_64/freebsd/libscrypt.so'
+ exclude 'lib/x86_64/linux/libscrypt.so'
+ }
+
+ viewBinding {
+ enabled = true
+ }
+}
+
+dependencies {
+ implementation project(':tangem-core')
+ implementation project(':tangem-sdk')
+ implementation project(':blockchain')
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.core:core-ktx:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.google.android.material:material:1.1.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.3"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
diff --git a/blockchain-demo/proguard-rules.pro b/blockchain-demo/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/blockchain-demo/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/blockchain-demo/src/androidTest/java/com/tangem/blockchain_demo/ExampleInstrumentedTest.kt b/blockchain-demo/src/androidTest/java/com/tangem/blockchain_demo/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000000..2a03ebc11f
--- /dev/null
+++ b/blockchain-demo/src/androidTest/java/com/tangem/blockchain_demo/ExampleInstrumentedTest.kt
@@ -0,0 +1,22 @@
+package com.tangem.blockchain_demo
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.blockchain_demo", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/blockchain-demo/src/main/AndroidManifest.xml b/blockchain-demo/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..9d092d68db
--- /dev/null
+++ b/blockchain-demo/src/main/AndroidManifest.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt b/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt
new file mode 100644
index 0000000000..0028ee31c5
--- /dev/null
+++ b/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt
@@ -0,0 +1,188 @@
+package com.tangem.blockchain_demo
+
+import android.os.Bundle
+import android.text.Editable
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.tangem.CardManager
+import com.tangem.blockchain.common.*
+import com.tangem.blockchain.common.extensions.Result
+import com.tangem.blockchain.common.extensions.Signer
+import com.tangem.blockchain.common.extensions.SimpleResult
+import com.tangem.blockchain_demo.databinding.ActivityBlockchainDemoBinding
+import com.tangem.commands.Card
+import com.tangem.tangem_sdk_new.extensions.init
+import com.tangem.tasks.ScanEvent
+import com.tangem.tasks.TaskError
+import com.tangem.tasks.TaskEvent
+import kotlinx.coroutines.*
+import java.math.BigDecimal
+import kotlin.coroutines.CoroutineContext
+
+class BlockchainDemoActivity : AppCompatActivity() {
+
+ private lateinit var cardManager: CardManager
+ private lateinit var signer: TransactionSigner
+ private lateinit var card: Card
+ private lateinit var walletManager: WalletManager
+ private var issuerDataCounter: Int = 1
+
+ private lateinit var fee: BigDecimal
+
+ private lateinit var binding: ActivityBlockchainDemoBinding
+
+ private val parentJob = Job()
+ private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
+ handleError(throwable.localizedMessage)
+ }
+ private val coroutineContext: CoroutineContext
+ get() = parentJob + Dispatchers.IO + exceptionHandler
+ private val scope = CoroutineScope(coroutineContext)
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = ActivityBlockchainDemoBinding.inflate(layoutInflater)
+ val view = binding.root
+ setContentView(view)
+
+ cardManager = CardManager.init(this)
+ signer = Signer(cardManager)
+
+ binding.btnScan.setOnClickListener { scan() }
+
+ binding.btnCheckFee.setOnClickListener { requestFee() }
+
+ binding.btnSend.setOnClickListener { send() }
+ }
+
+
+ private fun scan() {
+ cardManager.scanCard { taskEvent ->
+ when (taskEvent) {
+ is TaskEvent.Event -> {
+ when (taskEvent.data) {
+ is ScanEvent.OnReadEvent -> {
+ card = (taskEvent.data as ScanEvent.OnReadEvent).card
+ walletManager = WalletManagerFactory.makeWalletManager(card)!!
+ getInfo()
+ }
+ }
+ }
+ is TaskEvent.Completion -> {
+ if (taskEvent.error != null) {
+ if (taskEvent.error !is TaskError.UserCancelled) {
+ handleError(taskEvent.error.toString())
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun getInfo() {
+ scope.launch {
+ walletManager.update()
+ withContext(Dispatchers.Main) {
+ binding.tvBalance.text =
+ "${walletManager.wallet.balances[AmountType.Coin]?.value
+ ?: "error"} ${walletManager.blockchain.currency}"
+ val token = walletManager.wallet.balances[AmountType.Token]
+ if (token != null) {
+ binding.tvBalance.text = token.currencySymbol + " " + token.value
+ binding.etSumToSend.text = Editable.Factory.getInstance().newEditable(
+ token.value.toString()
+ )
+ } else {
+ binding.etSumToSend.text =
+ Editable.Factory.getInstance().newEditable(
+ walletManager.wallet.balances[AmountType.Coin]?.value.toString()
+ )
+ }
+ binding.btnCheckFee.isEnabled = true
+ }
+ }
+ }
+
+ private fun requestFee() {
+ if (binding.etReceiverAddress.text.isBlank()) {
+ Toast.makeText(this, "Please enter receiver address", Toast.LENGTH_LONG).show()
+ return
+ } else if (binding.etSumToSend.text.isBlank()) {
+ Toast.makeText(this, "Choose sum to send", Toast.LENGTH_LONG).show()
+ return
+ }
+
+ scope.launch {
+ val feeResult = (walletManager as FeeProvider).getFee(
+ walletManager.wallet.balances[AmountType.Token]
+ ?: walletManager.wallet.balances[AmountType.Coin]!!,
+ binding.etReceiverAddress.text.toString())
+ withContext(Dispatchers.Main) {
+ when (feeResult) {
+ is Result.Failure -> {
+ handleError(feeResult.error?.localizedMessage ?: "Error")
+ }
+ is Result.Success -> {
+ binding.btnSend.isEnabled = true
+ val fees = feeResult.data
+ if (fees.size == 1) {
+ binding.tvFee.text = fees[0].value.toString()
+ fee = fees[0].value ?: BigDecimal(0)
+ } else {
+ binding.tvFee.text = fees[0].value.toString() + "\n" +
+ fees[1].value.toString() + "\n" +
+ fees[2].value.toString()
+ fee = fees[1].value ?: BigDecimal(0)
+
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun send() {
+ scope.launch {
+ val result = (walletManager as TransactionSender).send(
+ formTransactionData(),
+ signer)
+ withContext(Dispatchers.Main) {
+ when (result) {
+ is SimpleResult.Failure -> {
+ handleError(result.error?.localizedMessage ?: "Error")
+ }
+ is SimpleResult.Success -> {
+ binding.tvFee.text = "Success"
+ }
+ }
+ }
+ }
+ }
+
+ private fun formTransactionData(): TransactionData {
+ val amount = if (walletManager.wallet.balances[AmountType.Token] != null) {
+ walletManager.wallet.balances[AmountType.Token]!!
+ } else {
+ walletManager.wallet.balances[AmountType.Coin]!!.copy(
+ value = binding.etSumToSend.text.toString().toBigDecimal() - fee
+ )
+ }
+ return TransactionData(
+ amount,
+ walletManager.wallet.balances[AmountType.Coin]!!.copy(value = fee),
+ walletManager.wallet.balances[AmountType.Coin]!!.address!!,
+ binding.etReceiverAddress.text.toString()
+ )
+ }
+
+ private fun handleError(error: String?) {
+ Toast.makeText(this, error, Toast.LENGTH_LONG).show()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ scope.cancel()
+ }
+}
\ No newline at end of file
diff --git a/blockchain-demo/src/main/res/drawable-v24/ic_launcher_foreground.xml b/blockchain-demo/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..1f6bb29060
--- /dev/null
+++ b/blockchain-demo/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/blockchain-demo/src/main/res/drawable/ic_launcher_background.xml b/blockchain-demo/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..0d025f9bf6
--- /dev/null
+++ b/blockchain-demo/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/blockchain-demo/src/main/res/layout/activity_blockchain_demo.xml b/blockchain-demo/src/main/res/layout/activity_blockchain_demo.xml
new file mode 100644
index 0000000000..faa8ad1d9e
--- /dev/null
+++ b/blockchain-demo/src/main/res/layout/activity_blockchain_demo.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/blockchain-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/blockchain-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..eca70cfe52
--- /dev/null
+++ b/blockchain-demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/blockchain-demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/blockchain-demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..eca70cfe52
--- /dev/null
+++ b/blockchain-demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/blockchain-demo/src/main/res/mipmap-hdpi/ic_launcher.png b/blockchain-demo/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..898f3ed59a
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png b/blockchain-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..dffca3601e
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-mdpi/ic_launcher.png b/blockchain-demo/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..64ba76f75e
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png b/blockchain-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..dae5e08234
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-xhdpi/ic_launcher.png b/blockchain-demo/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..e5ed46597e
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/blockchain-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..14ed0af350
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png b/blockchain-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..b0907cac3b
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/blockchain-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..d8ae031549
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/blockchain-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..2c18de9e66
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/blockchain-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/blockchain-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..beed3cdd2c
Binary files /dev/null and b/blockchain-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/blockchain-demo/src/main/res/values/colors.xml b/blockchain-demo/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..1f9a3ff55b
--- /dev/null
+++ b/blockchain-demo/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #527Aff
+ #027AFF
+ #027AFF
+ #4f98c0
+
diff --git a/blockchain-demo/src/main/res/values/strings.xml b/blockchain-demo/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..556392ebfb
--- /dev/null
+++ b/blockchain-demo/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Blockchain Demo
+
diff --git a/blockchain-demo/src/main/res/values/styles.xml b/blockchain-demo/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..5885930df6
--- /dev/null
+++ b/blockchain-demo/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/blockchain-demo/src/main/res/xml/nfc_tech_filter.xml b/blockchain-demo/src/main/res/xml/nfc_tech_filter.xml
new file mode 100644
index 0000000000..1773c9e9b4
--- /dev/null
+++ b/blockchain-demo/src/main/res/xml/nfc_tech_filter.xml
@@ -0,0 +1,8 @@
+
+
+
+ android.nfc.tech.IsoDep
+ android.nfc.tech.Ndef
+ android.nfc.tech.NfcV
+
+
\ No newline at end of file
diff --git a/blockchain-demo/src/test/java/com/tangem/blockchain_demo/ExampleUnitTest.kt b/blockchain-demo/src/test/java/com/tangem/blockchain_demo/ExampleUnitTest.kt
new file mode 100644
index 0000000000..76ebdca6b1
--- /dev/null
+++ b/blockchain-demo/src/test/java/com/tangem/blockchain_demo/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package com.tangem.blockchain_demo
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file