Updated on 2026-08-14

This commit is contained in:
Tangem 2019-04-08 14:39:49 +03:00
commit 73c73f12ae
49 changed files with 2047 additions and 74 deletions

View file

@ -12,7 +12,6 @@ static def generateVersionName() {
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.tangem.wallet"
minSdkVersion 21
targetSdkVersion 28
versionCode 107
@ -33,6 +32,22 @@ android {
debuggable true
minifyEnabled false
applicationIdSuffix ".debug"
versionNameSuffix '-DEBUG'
signingConfig signingConfigs.debug
}
}
flavorDimensions ""
productFlavors {
tangemAccess {
versionName "0.903.2." + generateVersionName()
versionCode 106
applicationId "com.tangem.wallet"
signingConfig signingConfigs.debug
}
tangemCardano {
versionName "0.1.0"
versionCode 1
applicationId "com.tangem.cardano"
signingConfig signingConfigs.debug
}
}
@ -43,10 +58,20 @@ android {
buildToolsVersion '28.0.3'
}
configurations {
all*.exclude group: 'com.google.guava', module: 'listenablefuture'
}
dependencies {
implementation project(':card-common')
implementation project(':card-android')
implementation project(':server-android')
// implementation 'com.github.TangemCash:card-common:0.1.2'
// implementation 'com.github.TangemCash:card-android:0.1.3'
// implementation 'com.github.TangemCash:server-android:0.1.4'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
// implementation 'com.github.TangemCash:card-common:0.1.2'
// implementation 'com.github.TangemCash:card-android:0.1.3'
// implementation 'com.github.TangemCash:server-android:0.1.4'
@ -55,7 +80,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.biometric:biometric:1.0.0-alpha03'
implementation 'co.nstant.in:cbor:0.8'
implementation 'com.google.android.material:material:1.1.0-alpha02'
implementation 'com.google.android.material:material:1.1.0-alpha05'
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
@ -70,7 +95,10 @@ dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.skyfishjy.ripplebackground:library:1.0.1'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation "com.orhanobut:hawk:2.0.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"
implementation 'org.bitcoinj:bitcoinj-parent:0.14.7'
implementation 'org.bitcoinj:bitcoinj-core:0.14.7'
implementation 'org.greenrobot:eventbus:3.1.1'

View file

@ -0,0 +1,4 @@
package com.tangem;
public class Test {
}

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DEV</string>
<string name="log_file_provider_authorities">com.tangem.data.LogFileProvider.debug</string>
<!--<string name="app_name">DEBUG</string>-->
</resources>

View file

@ -29,12 +29,12 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<provider
android:name="com.tangem.data.LogFileProvider"
android:authorities="@string/log_file_provider_authorities"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true" />
<!--<provider-->
<!--android:name="com.tangem.data.LogFileProvider"-->
<!--android:authorities="@string/log_file_provider_authorities"-->
<!--android:enabled="true"-->
<!--android:exported="false"-->
<!--android:grantUriPermissions="true" />-->
<activity
android:name="com.tangem.ui.activity.LogoActivity"
@ -99,17 +99,17 @@
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name="com.tangem.ui.activity.PrepareTransactionActivity"
android:name="com.tangem.ui.PrepareTransactionActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name="com.tangem.ui.activity.ConfirmTransactionActivity"
android:name="com.tangem.ui.ConfirmTransactionActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name="com.tangem.ui.activity.SignTransactionActivity"
android:name="com.tangem.ui.SignTransactionActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />

View file

@ -61,6 +61,17 @@
"transactionKey": {
"publicKey": "04ECA685AFD135EF2AB24D07BED71ED82CC1882C282976616DAC655ED3E5B8E75487C131F5D79A87895ECDE89AC58CD1BB220A0C29DD544D8D9AD5156184456F3F"
}
},
{
"id": "CARDANO",
"dataKey": {
"publicKey": "0445C312089E57A6952F96D74FF75C962E1E2C2DDA5FB4195661B92E2744791350FE6C60E1A1D145C6A34D8BEAD57506A90FE4A149A9AA43CFF3C6CAE0748B771E",
"privateKeyEncrypted": "2791FA18DFD4D2AD11509266C4721CE9BB4E9C7C8A06BF5071C6294EAB1F47A153CFDEBAF804425E5ED49AFEB70DD741"
},
"transactionKey": {
"publicKey": "04969A008E8A61F92EF8D380DB7BB7B8F6F667CA169934371F544F6697D1D2AF97FB87B8D75F550CD782472D46BFCADF354AF17D807C1D2EB82E184C367CACF1FC",
"privateKeyEncrypted": "4707EEB8704D53EB1B6D905604C394D1514C205E3EC16423961BB82A7A47896783F8035BE56D6DAE5DE200E67F2D92E3"
}
}

View file

@ -8,8 +8,10 @@ import com.tangem.data.local.PendingTransactionsStorage
import com.tangem.card_android.android.data.Firmwares
import com.tangem.card_android.android.data.PINStorage
import com.tangem.card_common.data.Issuer
import com.tangem.data.dp.PrefsManager
import com.tangem.di.*
import com.tangem.server_android.data.LocalStorage
import com.tangem.wallet.BuildConfig
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
@ -42,6 +44,8 @@ class App : Application() {
navigatorComponent = buildNavigatorComponent()
toastHelperComponent = buildToastHelperComponent()
PrefsManager.getInstance().init(this)
// common init
if (PINStorage.needInit())
PINStorage.init(applicationContext)
@ -52,6 +56,24 @@ class App : Application() {
localStorage = LocalStorage(applicationContext)
pinStorage = PINStorage()
pendingTransactionsStorage = PendingTransactionsStorage(applicationContext)
if (BuildConfig.DEBUG) {
com.tangem.card_common.util.Log.setLogger(
object : com.tangem.card_common.util.LoggerInterface {
override fun i(logTag: String, message: String) {
android.util.Log.i(logTag, message)
}
override fun e(logTag: String, message: String) {
android.util.Log.e(logTag, message)
}
override fun v(logTag: String, message: String) {
android.util.Log.v(logTag, message)
}
}
)
}
}
private fun buildNavigatorComponent(): NavigatorComponent {

View file

@ -4,6 +4,12 @@ import android.app.Activity
object Constant {
const val FLAVOR_TANGEM_ACCESS = "tangemAccess"
const val FLAVOR_TANGEM_CARDANO = "tangemCardano"
const val PREF_LAST_WALLET_ADDRESS = "last_wallet_address"
const val URL_TANGEM = "https://play.google.com/store/apps/details?id=com.tangem.wallet"
const val EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA"

View file

@ -37,7 +37,8 @@ public class LogFileProvider extends ContentProvider {
// 'content://it.my.app.LogFileProvider/*'
// and return 1 in the case that the incoming Uri matches this pattern
uriMatcher.addURI(Objects.requireNonNull(getContext()).getString(R.string.log_file_provider_authorities), "*", 1);
// uriMatcher.addURI(Objects.requireNonNull(getContext()).getString(R.string.log_file_provider_authorities), "*", 1);
// uriMatcher.addURI(Objects.requireNonNull("com.tangem.data.LogFileProvider"), "*", 1);
return true;
}
@ -60,7 +61,7 @@ public class LogFileProvider extends ContentProvider {
// The desired file name is specified by the last segment of the
// path
// E.g.
// 'content://it.my.app.LogFileProvider/Test.txt'
// 'content://it.my.app.LogFileProvider/Test1.txt'
// Take this and build the path to the file
String fileLocation = getContext().getCacheDir() + File.separator
+ uri.getLastPathSegment();

View file

@ -0,0 +1,44 @@
package com.tangem.data.dp
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import com.orhanobut.hawk.Hawk
import com.tangem.Constant
class PrefsManager {
companion object {
const val PREF_NAME = "tangem_access"
@SuppressLint("StaticFieldLeak")
private var instance: PrefsManager? = null
fun getInstance(): PrefsManager {
if (instance == null) {
instance = PrefsManager()
}
return instance as PrefsManager
}
}
private lateinit var context: Context
private lateinit var preferences: SharedPreferences
fun init(context: Context) {
this.context = context
preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
Hawk.init(context).build()
}
val lastWalletAddress: String
get() = Hawk.get(Constant.PREF_LAST_WALLET_ADDRESS, "")
fun saveLastWalletAddress(value: String) {
Hawk.put(Constant.PREF_LAST_WALLET_ADDRESS, value)
}
fun clearLastWalletAddress() {
Hawk.delete(Constant.PREF_LAST_WALLET_ADDRESS)
}
}

View file

@ -4,7 +4,9 @@ import android.app.Activity
import android.nfc.Tag
import com.tangem.Constant
import com.tangem.domain.wallet.TangemContext
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.*
import com.tangem.wallet.BuildConfig
class Navigator {
@ -69,7 +71,14 @@ class Navigator {
}
fun showPrepareTransaction(context: Activity, ctx: TangemContext) {
context.startActivityForResult(PrepareTransactionActivity.callingIntent(context, ctx), Constant.REQUEST_CODE_SEND_TRANSACTION)
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
context.startActivity(PrepareTransactionActivity.callingIntent(context, ctx))
}
else -> {
context.startActivityForResult(PrepareTransactionActivity.callingIntent(context, ctx), Constant.REQUEST_CODE_SEND_TRANSACTION)
}
}
}
fun showCreateNewWallet(context: Activity, ctx: TangemContext) {

View file

@ -6,7 +6,7 @@ import com.tangem.ui.activity.LogoActivity
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.activity.PrepareCryptonitOtherApiWithdrawalActivity
import com.tangem.ui.activity.PrepareKrakenWithdrawalActivity
import com.tangem.ui.activity.PrepareTransactionActivity
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.PurgeActivity
import com.tangem.ui.activity.VerifyCardActivity

View file

@ -9,6 +9,7 @@ import com.google.android.material.snackbar.Snackbar
import com.tangem.Constant
import com.tangem.wallet.R
class ToastHelper {
fun showSnackbarUpdateVersion(context: Context, vg: ViewGroup, versionName: String) {
@ -24,6 +25,24 @@ class ToastHelper {
}.show()
}
fun showSnackbarSuccess(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_INDEFINITE)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_okay))
snackbar.setAction(R.string.ok) {
snackbar.dismiss()
}
snackbar.show()
}
fun showSnackbarError(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_INDEFINITE)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_err))
snackbar.setAction(R.string.ok) {
snackbar.dismiss()
}
snackbar.show()
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.di
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.MainActivity
import dagger.Component
import javax.inject.Singleton
@ -10,4 +11,6 @@ interface ToastHelperComponent {
fun inject(activity: MainActivity)
fun inject(activity: PrepareTransactionActivity)
}

View file

@ -7,7 +7,9 @@ import com.tangem.domain.wallet.eth.EthEngine
import com.tangem.domain.wallet.token.TokenEngine
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.data.Blockchain
import com.tangem.domain.wallet.cardano.CardanoData
import com.tangem.domain.wallet.cardano.CardanoEngine
import com.tangem.domain.wallet.eth.EthData
import com.tangem.domain.wallet.ltc.LtcEngine
import com.tangem.domain.wallet.nftToken.NftTokenEngine
import com.tangem.domain.wallet.rsk.RskEngine
@ -43,7 +45,6 @@ object CoinEngineFactory {
}
fun create(context: TangemContext): CoinEngine? {
var result: CoinEngine?
try {
result = if (Blockchain.BitcoinCash == context.blockchain)
@ -76,4 +77,24 @@ object CoinEngineFactory {
return result
}
fun createCardano(context: TangemContext): CoinEngine? {
var result: CoinEngine?
try {
result = CardanoEngine(context)//EthEngine(context)//
} catch (e: Exception) {
e.printStackTrace()
result = null
Log.e(TAG, "Can't create Cardano CoinEngine!")
}
return result
}
fun createCardanoData(): CoinData? {
return CardanoData()//EthData()// CardanoData()
}
fun isCardano(blockchainID: String): Boolean {
return blockchainID==Blockchain.Cardano.id//Ethereum.id
}
}

View file

@ -12,7 +12,6 @@ import com.tangem.card_common.util.Util;
import com.tangem.data.Blockchain;
import com.tangem.data.local.PendingTransactionsStorage;
import com.tangem.data.network.ElectrumRequest;
import com.tangem.data.network.ServerApiCommon;
import com.tangem.data.network.ServerApiElectrum;
import com.tangem.domain.wallet.BTCUtils;
import com.tangem.domain.wallet.BalanceValidator;

View file

@ -6,6 +6,7 @@ import android.util.Base64;
import android.util.Log;
import com.tangem.App;
import com.tangem.Constant;
import com.tangem.data.local.PendingTransactionsStorage;
import com.tangem.data.network.ServerApiAdalite;
import com.tangem.data.network.model.AdaliteResponse;
@ -23,6 +24,7 @@ import com.tangem.card_common.reader.CardProtocol;
import com.tangem.card_common.tasks.SignTask;
import com.tangem.card_common.util.Util;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.wallet.BuildConfig;
import com.tangem.wallet.R;
import org.spongycastle.crypto.Digest;
@ -198,6 +200,9 @@ public class CardanoEngine extends CoinEngine {
@Override
public boolean checkNewTransactionAmount(Amount amount) {
if( BuildConfig.FLAVOR==Constant.FLAVOR_TANGEM_CARDANO ) {
return true;
}
if (coinData == null) return false;
if (amount.compareTo(convertToAmount(coinData.getBalanceInInternalUnits())) > 0) {
return false;
@ -424,9 +429,10 @@ public class CardanoEngine extends CoinEngine {
final long amountFinal = amount;
final long changeFinal = change;
if (amount + fees > fullAmount) {
throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
}
//TODO - uncomment
// if (amount + fees > fullAmount) {
// throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
// }
CborBuilder cborBuilder = new CborBuilder();
ArrayBuilder<CborBuilder> txArray = cborBuilder.addArray();

View file

@ -4,6 +4,7 @@ import android.net.Uri;
import android.text.InputFilter;
import android.util.Log;
import com.tangem.Constant;
import com.tangem.data.Blockchain;
import com.tangem.data.network.ServerApiInfura;
import com.tangem.data.network.model.InfuraResponse;
@ -19,6 +20,7 @@ import com.tangem.card_common.data.TangemCard;
import com.tangem.card_common.tasks.SignTask;
import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.wallet.BuildConfig;
import com.tangem.wallet.R;
import org.bitcoinj.core.ECKey;
@ -264,7 +266,9 @@ public class EthEngine extends CoinEngine {
@Override
public boolean checkNewTransactionAmount(Amount amount) {
if (coinData == null) return false;
if( BuildConfig.FLAVOR== Constant.FLAVOR_TANGEM_CARDANO ) {
return true;
}
Amount balance = getBalance();
if (balance == null || amount.compareTo(balance) > 0) {
return false;

View file

@ -29,7 +29,6 @@ import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.VerifyCardTask
import com.tangem.card_common.util.Util
import com.tangem.util.LOG
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_empty_wallet.*

View file

@ -1,13 +1,14 @@
package com.tangem.ui.activity
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.domain.wallet.TangemContext
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_logo.*
@ -36,21 +37,40 @@ class LogoActivity : AppCompatActivity() {
ivLogo.setOnClickListener { hide() }
}
@SuppressLint("SetTextI18n")
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// set beta version name
if (BuildConfig.DEBUG)
tvAppVersion.text = "BETA v." + BuildConfig.VERSION_NAME + "\n" + "dev" + "\n" + "build " + BuildConfig.VERSION_CODE
tvAppVersion.text = String.format(getString(R.string.version_name_debug), BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
else
tvAppVersion.text = "BETA v." + BuildConfig.VERSION_NAME
tvAppVersion.text = String.format(getString(R.string.version_name_release), BuildConfig.VERSION_NAME)
// set flavor app name
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
tvExtension.visibility = View.VISIBLE
tvExtension.text = getString(R.string.cardano)
}
else -> {
tvExtension.visibility = View.GONE
}
}
if (intent.getBooleanExtra(Constant.EXTRA_AUTO_HIDE, true))
ivLogo.postDelayed(hideRunnable, Constant.MILLIS_AUTO_HIDE.toLong())
}
private fun hide() {
navigator.showMain(this)
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
navigator.showPrepareTransaction(this, TangemContext())
}
else -> {
navigator.showMain(this)
}
}
finish()
}

View file

@ -1,14 +1,13 @@
@file:Suppress("ObsoleteExperimentalCoroutines")
package com.tangem.ui.activity
import android.Manifest
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.graphics.Color
import android.net.Uri
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
@ -21,7 +20,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.scottyab.rootbeer.RootBeer
import com.tangem.App
import com.tangem.Constant
@ -29,14 +27,23 @@ import com.tangem.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_android.data.saveToBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.CustomReadCardTask
import com.tangem.card_common.tasks.OneTouchSignTask
import com.tangem.card_common.tasks.ReadCardInfoTask
import com.tangem.card_common.tasks.SignTask
import com.tangem.card_common.util.Log
import com.tangem.data.Blockchain
import com.tangem.data.Logger
import com.tangem.data.network.ServerApiCommon
import com.tangem.di.Navigator
import com.tangem.di.ToastHelper
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
@ -52,9 +59,9 @@ import kotlinx.android.synthetic.main.layout_touch_card.*
import java.io.File
import java.util.*
import javax.inject.Inject
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.di.ToastHelper
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, PopupMenu.OnMenuItemClickListener {
@ -74,7 +81,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
private var unsuccessReadCount = 0
private var lastTag: Tag? = null
private var readCardInfoTask: ReadCardInfoTask? = null
private var task: CustomReadCardTask? = null
private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
override fun onNewIntent(intent: Intent?) {
@ -142,7 +149,8 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
val responseBuildVersion = responseVersionName.split('.').last()
val appBuildVersion = BuildConfig.VERSION_NAME.split('.').last()
if (responseBuildVersion.toInt() > appBuildVersion.toInt())
toastHelper.showSnackbarUpdateVersion(this, cl, responseVersionName)
if (BuildConfig.FLAVOR.equals(Constant.FLAVOR_TANGEM_ACCESS))
toastHelper.showSnackbarUpdateVersion(this, cl, responseVersionName)
} catch (E: Exception) {
E.printStackTrace()
}
@ -237,8 +245,8 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
lastTag = tag
readCardInfoTask = ReadCardInfoTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
readCardInfoTask?.start()
task = ReadCardInfoTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
task?.start()
} catch (e: Exception) {
e.printStackTrace()
nfcManager.notifyReadResult(false)
@ -252,12 +260,12 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
}
public override fun onPause() {
readCardInfoTask?.cancel(true)
task?.cancel(true)
super.onPause()
}
public override fun onStop() {
readCardInfoTask?.cancel(true)
task?.cancel(true)
super.onStop()
}
@ -270,7 +278,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
readCardInfoTask = null
task = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
nfcManager.notifyReadResult(true)
@ -336,7 +344,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
}
override fun onReadCancel() {
readCardInfoTask = null
task = null
ReadCardInfoTask.resetLastReadInfo()
rlProgressBar.postDelayed({
try {

View file

@ -28,7 +28,6 @@ import com.tangem.card_android.data.asBundle
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.PurgeTask
import com.tangem.card_common.util.Util
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_purge.*
import kotlinx.android.synthetic.main.layout_touch_card.*

View file

@ -10,6 +10,7 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.ProgressBar;
import com.tangem.card_common.util.Log;
import com.tangem.wallet.R;
import java.util.Timer;
@ -104,22 +105,39 @@ public class WaitSecurityDelayDialog extends DialogFragment {
}
public static void onReadWait(final AppCompatActivity activity, final int msec) {
Log.e(TAG, "onReadWait callback(" + msec + ")");
activity.runOnUiThread(() -> {
Log.e(TAG, "onReadWait on ui thread(" + msec + ")");
if (timerToShowDelayDialog != null) {
timerToShowDelayDialog.cancel();
timerToShowDelayDialog = null;
}
if (msec == 0) {
if (instance != null && instance.isAdded()) {
instance.dismiss();
instance = null;
if (instance != null) {
if (instance.isAdded()) {
instance.dismiss();
instance = null;
} else {
Log.e(TAG, "onReadWait(0) with not added dialog");
// instance.progressBar.postDelayed(() -> {
// Log.e(TAG, "onReadWait(0) dismiss delayed");
try {
instance.dismiss();
instance = null;
}
catch (Exception e)
{
e.printStackTrace();
}
// }, 10000);
}
}
return;
}
if (instance == null) {
if (msec > minRemainingDelayToShowDialog) {
if (msec > delayBeforeShowDialog + minRemainingDelayToShowDialog) {
instance = new WaitSecurityDelayDialog();
// 1000ms - card delay notification interval
instance.setup(msec + 1000, 1000);

View file

@ -7,6 +7,25 @@
tools:context="com.tangem.ui.activity.LogoActivity"
tools:ignore="contentDescription">
<TextView
android:id="@+id/tvExtension"
android:layout_width="wrap_content"
android:layout_height="13dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="@font/maax"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@android:color/holo_red_light"
android:textSize="@dimen/text_size_small"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ivLogo"
tools:text="@string/cardano"
tools:visibility="visible" />
<ImageView
android:id="@+id/ivLogo"
android:layout_width="match_parent"
@ -32,7 +51,7 @@
android:textSize="@dimen/text_size_small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ivLogo" />
app:layout_constraintTop_toBottomOf="@+id/tvExtension" />
<TextView
android:layout_width="wrap_content"

View file

@ -15,8 +15,8 @@
<color name="text_normal">#FF505050</color>
<color name="msg_send">@color/black</color>
<color name="msg_rcv">#4a763d</color>
<color name="msg_okay">#BF7FF27F</color>
<color name="msg_err">#BFFF4444</color>
<color name="msg_okay">#F27FF27F</color>
<color name="msg_err">#F2FF4444</color>
<color name="msg_start">#4a086e</color>
<color name="msg_action">#0c15c1</color>
<color name="btn_light">#e3e3e3</color>

View file

@ -7,7 +7,6 @@
<string name="no_connection">No connection with blockchain nodes</string>
<string name="contin">Continue</string>
<string name="not_found">Not found</string>
<!--<string name="wrong_tag_err">Not ISO tag!</string>-->
<string name="msgCardMustBeLoaded">Card must be loaded first!</string>
<string name="tag_lost_err">Tag lost!</string>
<string name="cmd_suffix">command</string>
@ -56,6 +55,7 @@
<string name="load_via_qr">Show QR-code</string>
<string name="show_wallet_qr_code">LOAD</string>
<string name="beta">BETA</string>
<string name="cardano">Cardano</string>
<!-- dialogs -->
<string name="device_is_rooted">Your Android device is rooted. Security at risk!</string>
@ -84,6 +84,12 @@
<string name="reset_pins_to_default">Reset PINs to default</string>
<string name="erase_wallet">Erase wallet</string>
<!-- LogoActivity -->
<string name="version_name_debug">BETA v.%1$s \n dev \n build %2$s</string>
<string name="version_name_release">BETA v.%1$s</string>
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>
<!-- MainActivity -->
<string name="tap_tangem_card">Tap Tangem card with your smartphone</string>
<string name="scan_banknote">Scan a banknote with your \n %1$s \n as shown above</string>
@ -143,6 +149,8 @@
<string name="enter_wallet_address">enter wallet address</string>
<string name="enter_amount">enter amount</string>
<string name="verify">Verify</string>
<string name="wallet_address_empty">Wallet address is empty</string>
<string name="amount_empty">Amount is empty</string>
<!-- PinRequestActivity -->
<string name="enter_new_pin_or_use_fingerprint_scanner">Enter new PIN or use fingerprint scanner</string>

View file

@ -20,6 +20,7 @@
<string name="_9">9</string>
<string name="_0">0</string>
<string name="log_file_provider_authorities">com.tangem.data.LogFileProvider</string>
<string name="ex_cant_create_coinEngine">Can\'t create CoinEngine!</string>
</resources>

View file

@ -0,0 +1,4 @@
package com.tangem;
public class Test2 {
}

View file

@ -1,4 +1,4 @@
package com.tangem.ui.activity
package com.tangem.ui
import android.app.Activity
import android.content.Intent
@ -25,12 +25,14 @@ import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_confirm_transaction.*
import org.greenrobot.eventbus.EventBus
import java.io.IOException
import java.util.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.ui.activity.PinRequestActivity
import com.tangem.ui.activity.SignTransactionActivity
import kotlinx.android.synthetic.tangemAccess.activity_confirm_transaction.*
class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {

View file

@ -1,4 +1,4 @@
package com.tangem.ui.activity
package com.tangem.ui
import android.app.Activity
import android.content.Context
@ -23,7 +23,7 @@ import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_prepare_transaction.*
import kotlinx.android.synthetic.tangemAccess.activity_prepare_transaction.*
import java.io.IOException
import javax.inject.Inject

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.ConfirmTransactionActivity"
tools:context="com.tangem.ui.ConfirmTransactionActivity"
tools:ignore="Autofill">
<TextView

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.PrepareTransactionActivity"
tools:context="com.tangem.ui.PrepareTransactionActivity"
tools:ignore="contentDescription">
<TextView

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.SignTransactionActivity">
tools:context="com.tangem.ui.SignTransactionActivity">
<include
layout="@layout/layout_touch_card"

View file

@ -2,6 +2,5 @@
<resources>
<string name="app_name">Tangem Access</string>
<string name="log_file_provider_authorities">com.tangem.data.LogFileProvider</string>
</resources>

View file

@ -0,0 +1,4 @@
package com.tangem;
public class Test1 {
}

View file

@ -0,0 +1,310 @@
package com.tangem.ui
import android.app.Activity
import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.text.Editable
import android.text.Html
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.View
import android.widget.Toast
import com.tangem.Constant
import com.tangem.card_android.android.data.PINStorage
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.ui.event.TransactionFinishWithError
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import org.greenrobot.eventbus.EventBus
import java.io.IOException
import java.util.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.ui.activity.PinRequestActivity
import kotlinx.android.synthetic.tangemCardano.activity_confirm_transaction.*
class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private lateinit var amount: CoinEngine.Amount
private var isIncludeFee: Boolean = true
private var requestPIN2Count = 0
private var nodeCheck = true
private var dtVerified: Date? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_confirm_transaction)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext();//.loadFromBundle(this, intent.extras)
val engine = CoinEngineFactory.createCardano(ctx) //create(ctx)
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(engine!!.balanceHTML, Html.FROM_HTML_MODE_LEGACY)
else
Html.fromHtml(engine!!.balanceHTML)
tvBalance.text = html
isIncludeFee = intent.getBooleanExtra(Constant.EXTRA_FEE_INCLUDED, true)
if (isIncludeFee)
tvIncFee.setText(R.string.including_fee)
else
tvIncFee.setText(R.string.not_including_fee)
amount = CoinEngine.Amount(intent.getStringExtra(Constant.EXTRA_AMOUNT), intent.getStringExtra(Constant.EXTRA_AMOUNT_CURRENCY))
if (engine.allowSelectFeeInclusion())
tvIncFee.visibility = View.VISIBLE
else
tvIncFee.visibility = View.INVISIBLE
// if (ctx.card.blockchainID == Blockchain.Token.id) {
// // for Blockchain.Token limit decimals
// etAmount.setText(amount.toValueString(ctx.card.tokensDecimal))
// } else {
// for others
etAmount.setText(amount.toValueString())
// }
tvCurrency.text = engine.balanceCurrency
tvCurrency2.text = engine.feeCurrency
// tvCardID.text = ctx.card.cidDescription
etWallet.setText(intent.getStringExtra(Constant.EXTRA_TARGET_ADDRESS))
btnSend.visibility = View.INVISIBLE
if (!engine.allowSelectFeeLevel()) {
rgFee.visibility = View.GONE
}
// set listeners
rgFee.setOnCheckedChangeListener { _, checkedId -> doSetFee(checkedId) }
etFee.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
try {
val eqFee = engine.evaluateFeeEquivalent(etFee!!.text.toString())
tvFeeEquivalent.text = eqFee
if (!ctx.coinData!!.amountEquivalentDescriptionAvailable) {
tvFeeEquivalent.error = getString(R.string.service_unavailable)
tvCurrency2.visibility = View.GONE
tvFeeEquivalent.visibility = View.GONE
} else
tvFeeEquivalent.error = null
} catch (e: Exception) {
e.printStackTrace()
tvFeeEquivalent.text = ""
}
}
override fun afterTextChanged(s: Editable) {
}
})
btnSend.setOnClickListener {
if (UtilHelper.isOnline(this)) {
val calendar = Calendar.getInstance()
calendar.add(Calendar.MINUTE, -1)
if (dtVerified == null || dtVerified!!.before(calendar.time)) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.the_obtained_data_is_outdated_try_again))
return@setOnClickListener
}
val engineCoin = CoinEngineFactory.createCardano(ctx)
if (engineCoin!!.isNeedCheckNode && !nodeCheck) {
Toast.makeText(baseContext, getString(R.string.cannot_reach_current_active_blockchain_node_try_again), Toast.LENGTH_LONG).show()
return@setOnClickListener
}
val txFee = engineCoin.convertToAmount(etFee.text.toString(), tvCurrency2.text.toString())
val txAmount = engineCoin.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
// if (!engineCoin.hasBalanceInfo()) {
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_check_balance_no_connection_with_blockchain_nodes))
// return@setOnClickListener
//
// } else if (!engineCoin.isBalanceNotZero) {
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.the_wallet_is_empty))
// return@setOnClickListener
//
// } else if (!engineCoin.isExtractPossible) {
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.please_wait_for_confirmation_of_incoming_transaction))
// return@setOnClickListener
// }
//
// if (!engineCoin.checkNewTransactionAmountAndFee(txAmount, txFee, isIncludeFee)) {
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.not_enough_funds_on_your_card))
// return@setOnClickListener
// }
requestPIN2Count = 0
// val intent = Intent(baseContext, PinRequestActivity::class.java)
// intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
// ctx.saveToIntent(intent)
// intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
// startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_)
//TODO - PIN2 workaround
PINStorage.setPIN2(PINStorage.getDefaultPIN2())
val intent = Intent(baseContext, SignTransactionActivity::class.java)
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
intent.putExtra(Constant.EXTRA_AMOUNT, etAmount.text.toString())
intent.putExtra(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
intent.putExtra(Constant.EXTRA_FEE, etFee.text.toString())
intent.putExtra(Constant.EXTRA_FEE_CURRENCY, tvCurrency2.text.toString())
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
startActivityForResult(intent, Constant.REQUEST_CODE_SIGN_TRANSACTION)
} else
Toast.makeText(this, getString(R.string.no_connection), Toast.LENGTH_SHORT).show()
}
progressBar.visibility = View.VISIBLE
engine!!.requestFee(
object : CoinEngine.BlockchainRequestsCallbacks {
override fun onComplete(success: Boolean) {
if (success) {
onProgress()
progressBar.visibility = View.INVISIBLE
dtVerified = Date()
} else {
finishWithError(Activity.RESULT_CANCELED, ctx.error)
}
}
override fun onProgress() {
doSetFee(rgFee.checkedRadioButtonId)
}
override fun allowAdvance(): Boolean {
return UtilHelper.isOnline(this@ConfirmTransactionActivity)
}
},
etWallet.text.toString(),
amount)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.REQUEST_CODE_SIGN_TRANSACTION) {
if (data != null && data.extras != null) {
if (data.extras!!.containsKey(EXTRA_TANGEM_CARD_UID) && data.extras!!.containsKey(EXTRA_TANGEM_CARD)) {
val updatedCard = TangemCard(data.getStringExtra(EXTRA_TANGEM_CARD_UID))
updatedCard.loadFromBundle(data.getBundleExtra(EXTRA_TANGEM_CARD))
ctx.card = updatedCard
}
}
if (resultCode == Constant.RESULT_INVALID_PIN_ && requestPIN2Count < 2) {
requestPIN2Count++
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_)
return
}
setResult(resultCode, data)
finish()
} else if (requestCode == Constant.REQUEST_CODE_REQUEST_PIN2_) {
if (resultCode == Activity.RESULT_OK) {
val intent = Intent(baseContext, SignTransactionActivity::class.java)
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
intent.putExtra(Constant.EXTRA_AMOUNT, etAmount.text.toString())
intent.putExtra(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
intent.putExtra(Constant.EXTRA_FEE, etFee.text.toString())
intent.putExtra(Constant.EXTRA_FEE_CURRENCY, tvCurrency2.text.toString())
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
startActivityForResult(intent, Constant.REQUEST_CODE_SIGN_TRANSACTION)
} else
Toast.makeText(baseContext, R.string.pin_2_is_required_to_sign_the_transaction, Toast.LENGTH_LONG).show()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
val intent = Intent()
setResult(Activity.RESULT_CANCELED, intent)
finish()
return true
}
}
return super.onKeyDown(keyCode, event)
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun doSetFee(checkedRadioButtonId: Int) {
var txtFee = ""
when (checkedRadioButtonId) {
R.id.rbMinimalFee ->
if (ctx.coinData.minFee != null) {
txtFee = ctx.coinData.minFee!!.toValueString()
btnSend.visibility = View.VISIBLE
} else
btnSend.visibility = View.INVISIBLE
R.id.rbNormalFee ->
if (ctx.coinData.normalFee != null) {
txtFee = ctx.coinData.normalFee!!.toValueString()
btnSend.visibility = View.VISIBLE
} else
btnSend.visibility = View.INVISIBLE
R.id.rbMaximumFee ->
if (ctx.coinData.maxFee != null) {
txtFee = ctx.coinData.maxFee!!.toValueString()
btnSend.visibility = View.VISIBLE
} else
btnSend.visibility = View.INVISIBLE
}
etFee.setText(txtFee.replace(',', '.'))
}
private fun finishWithError(errorCode: Int, message: String) {
val transactionFinishWithError = TransactionFinishWithError()
transactionFinishWithError.message = message
EventBus.getDefault().post(transactionFinishWithError)
val intent = Intent()
intent.putExtra(Constant.EXTRA_MESSAGE, message)
setResult(errorCode, intent)
finish()
}
}

View file

@ -0,0 +1,225 @@
package com.tangem.ui
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import android.text.Html
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.data.dp.PrefsManager
import com.tangem.di.Navigator
import com.tangem.di.ToastHelper
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.ui.event.TransactionFinishWithError
import com.tangem.ui.event.TransactionFinishWithSuccess
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.tangemCardano.activity_prepare_transaction.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.io.IOException
import javax.inject.Inject
class PrepareTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
companion object {
val TAG: String = PrepareTransactionActivity::class.java.simpleName
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, PrepareTransactionActivity::class.java)
ctx.saveToIntent(intent)
return intent
}
}
@Inject
internal lateinit var navigator: Navigator
@Inject
internal lateinit var toastHelper: ToastHelper
private lateinit var nfcManager: NfcManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_prepare_transaction)
App.navigatorComponent.inject(this)
App.toastHelperComponent.inject(this)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
val engine = CoinEngineFactory.createCardano(TangemContext())
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(engine?.balanceHTML, Html.FROM_HTML_MODE_LEGACY)
else
Html.fromHtml(engine?.balanceHTML)
tvBalance.text = html
if (!engine!!.allowSelectFeeInclusion()) {
rgIncFee.visibility = View.INVISIBLE
} else {
rgIncFee.visibility = View.VISIBLE
}
tvCurrency.text = engine.balance?.currency
etAmount.setText(engine.balance?.toValueString())
// limit number of symbols after comma
etAmount.filters = engine.amountInputFilters
etWallet.setText(PrefsManager.getInstance().lastWalletAddress)
// set listeners
etAmount.setOnEditorActionListener { lv, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val imm = lv.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(lv.windowToken, 0)
lv.clearFocus()
true
} else {
false
}
}
btnVerify.setOnClickListener {
if (!UtilHelper.isOnline(this)) {
Toast.makeText(this, R.string.no_connection, Toast.LENGTH_LONG).show()
return@setOnClickListener
}
// check on empty wallet address
if (etWallet.text.toString() == "") {
etWallet.error = getString(R.string.wallet_address_empty)
return@setOnClickListener
}
// check on empty amount
if (etAmount.text.toString() == "") {
etAmount.error = getString(R.string.amount_empty)
return@setOnClickListener
}
val strAmount: String = etAmount.text.toString().replace(",", ".")
val amount = engine.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
// check amount
try {
if (!engine.checkNewTransactionAmount(amount))
etAmount.error = getString(R.string.not_enough_funds_on_your_card)
else
etAmount.error = null
} catch (e: Exception) {
etAmount.error = getString(R.string.unknown_amount_format)
}
// check wallet address
if (!engine.validateAddress(etWallet.text.toString())) {
etWallet.error = getString(R.string.incorrect_destination_wallet_address)
return@setOnClickListener
} else
etWallet.error = null
if (!etAmount.error.isNullOrEmpty() || !etWallet.error.isNullOrEmpty()) {
return@setOnClickListener
}
val intent = Intent(baseContext, ConfirmTransactionActivity::class.java)
intent.putExtra(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, (rgIncFee!!.checkedRadioButtonId == R.id.rbFeeIn))
intent.putExtra(Constant.EXTRA_AMOUNT, strAmount)
intent.putExtra(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
startActivityForResult(intent, Constant.REQUEST_CODE_SEND_TRANSACTION__)
PrefsManager.getInstance().saveLastWalletAddress(etWallet.text.toString())
}
ivCamera.setOnClickListener {
navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR)
etWallet.text.clear()
}
etWallet.setOnTouchListener(OnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_UP) {
if (event.rawX >= etWallet.right - etWallet.compoundDrawables[2].bounds.width()) {
etWallet.text.clear()
return@OnTouchListener true
}
}
false
})
}
override fun onStart() {
super.onStart()
if (!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this)
}
override fun onDestroy() {
EventBus.getDefault().unregister(this)
super.onDestroy()
}
@Subscribe
fun onTransactionFinishWithSuccess(transactionFinishWithSuccess: TransactionFinishWithSuccess) {
transactionFinishWithSuccess.message?.let { toastHelper.showSnackbarSuccess(this, cl, it) }
}
@Subscribe
fun onTransactionFinishWithError(transactionFinishWithError: TransactionFinishWithError) {
transactionFinishWithError.message?.let { toastHelper.showSnackbarError(this, cl, it) }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.extras!!.containsKey("QRCode")) {
var code = data.getStringExtra("QRCode")
// when (ctx.blockchain) {
// Blockchain.Bitcoin -> {
// if (code.contains("bitcoin:")) {
// val tmp = code.split("bitcoin:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
// code = tmp[1]
// }
// }
// Blockchain.Ethereum, Blockchain.Token -> {
// if (code.contains("ethereum:")) {
// val tmp = code.split("ethereum:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
// code = tmp[1]
// } else if (code.contains("blockchain:")) {
// val tmp = code.split("blockchain:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
// code = tmp[1]
// }
// }
// else -> {
// }
// }
etWallet?.setText(code)
// } else if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION__) {
// setResult(resultCode, data)
// finish()
}
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
}

View file

@ -0,0 +1,366 @@
package com.tangem.ui
import android.app.Activity
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.media.MediaPlayer
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_android.data.asBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardCrypto
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.CustomReadCardTask
import com.tangem.card_common.tasks.OneTouchSignTask
import com.tangem.card_common.tasks.SignTask
import com.tangem.card_common.util.Log
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.activity.SendTransactionActivity
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.layout_progress_horizontal.*
import kotlinx.android.synthetic.main.layout_touch_card.*
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class SignTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = SignTransactionActivity::class.java.simpleName
}
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private lateinit var mpFinishSignSound: MediaPlayer
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
private var task: CustomReadCardTask? = null
private lateinit var amount: CoinEngine.Amount
private lateinit var fee: CoinEngine.Amount
private var isIncludeFee = true
private var outAddressStr: String? = null
private var lastReadSuccess = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sign_transaction)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext.loadFromBundle(this, intent.extras)
mpFinishSignSound = MediaPlayer.create(this, R.raw.scan_card_sound)
// init NFC Antenna
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
nfcDeviceAntenna.init()
amount = CoinEngine.Amount(intent.getStringExtra(Constant.EXTRA_AMOUNT), intent.getStringExtra(Constant.EXTRA_AMOUNT_CURRENCY))
fee = CoinEngine.Amount(intent.getStringExtra(Constant.EXTRA_FEE), intent.getStringExtra(Constant.EXTRA_FEE_CURRENCY))
isIncludeFee = intent.getBooleanExtra(Constant.EXTRA_FEE_INCLUDED, true)
outAddressStr = intent.getStringExtra(Constant.EXTRA_TARGET_ADDRESS)
// tvCardID.text = ctx.card!!.cidDescription
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar.visibility = View.INVISIBLE
}
public override fun onPause() {
task?.cancel(true)
super.onPause()
}
public override fun onStop() {
task?.cancel(true)
super.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) {
setResult(resultCode, data)
finish()
return
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
val intent = Intent()
setResult(Activity.RESULT_CANCELED, intent)
finish()
return true
}
}
return super.onKeyDown(keyCode, event)
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
isoDep.timeout = 65000
val coinEngine = CoinEngineFactory.createCardano(ctx!!)!!
coinEngine.setOnNeedSendTransaction { tx ->
if (tx != null) {
val intent = Intent(this, SendTransactionActivity::class.java)
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_TX, tx)
startActivityForResult(intent, Constant.REQUEST_CODE_SEND_TRANSACTION_)
}
}
val tx: OneTouchSignTask.TransactionToSign = object : OneTouchSignTask.TransactionToSign {
var txToSign: SignTask.TransactionToSign? = null
suspend fun requestBalanceAndUnspentTransactions(): Boolean = suspendCoroutine { cont ->
coinEngine!!.requestBalanceAndUnspentTransactions(object : CoinEngine.BlockchainRequestsCallbacks {
override fun onComplete(success: Boolean?) {
cont.resume(success!!)
}
override fun onProgress() {
}
override fun allowAdvance(): Boolean {
return true
}
})
}
fun initData(card: TangemCard) {
if (ctx.card == null) {
ctx.card = card
}
if (ctx.coinData == null) {
ctx.coinData = CoinEngineFactory.createCardanoData()
}
if (ctx.coinData.wallet.isNullOrEmpty()) {
coinEngine.defineWallet()
runBlocking { requestBalanceAndUnspentTransactions() }
Log.e(MainActivity.TAG, "requestBalanceAndUnspentTransactions completed")
}
if (txToSign == null) txToSign = coinEngine!!.constructTransaction(amount, fee, isIncludeFee, outAddressStr)
}
override fun isSigningOnCardSupported(card: TangemCard?): Boolean {
initData(card!!)
return CoinEngineFactory.isCardano(card?.blockchainID) and (txToSign!!.isSigningMethodSupported(card?.signingMethod))
}
override fun isIssuerCanSignData(card: TangemCard?): Boolean {
return try {
card?.issuer?.privateDataKey!=null
} catch (e: java.lang.Exception) {
false
}
}
override fun isIssuerCanSignTransaction(card: TangemCard?): Boolean {
return try {
card?.issuer?.privateTransactionKey!=null
} catch (e: java.lang.Exception) {
false
}
}
override fun getHashesToSign(card: TangemCard?): Array<ByteArray> {
initData(card!!)
return txToSign!!.hashesToSign
}
override fun getRawDataToSign(card: TangemCard?): ByteArray {
initData(card!!)
return txToSign!!.rawDataToSign
}
override fun getHashAlgToSign(card: TangemCard?): String {
initData(card!!)
return txToSign!!.hashAlgToSign
}
override fun getIssuerTransactionSignature(card: TangemCard?, dataToSignByIssuer: ByteArray?): ByteArray {
initData(card!!)
if( card.issuer!=null && card.issuer.privateTransactionKey!=null)
{
return CardCrypto.Signature(card.issuer.privateTransactionKey, dataToSignByIssuer)
}
return txToSign!!.getIssuerTransactionSignature(dataToSignByIssuer)
}
override fun onSignCompleted(card: TangemCard?, signature: ByteArray?) {
initData(card!!)
txToSign!!.onSignCompleted(signature)
}
}
task = OneTouchSignTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, tx)
task!!.start()
} catch (e: CardProtocol.TangemException_WrongAmount) {
try {
val intent = Intent()
intent.putExtra(Constant.EXTRA_MESSAGE, getString(R.string.cannot_sign_transaction_wrong_amount))
intent.putExtra(EXTRA_TANGEM_CARD_UID, ctx.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, ctx.card.asBundle)
setResult(Activity.RESULT_CANCELED, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
progressBar?.post {
progressBar?.visibility = View.VISIBLE
progressBar?.progress = 5
}
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar?.post { progressBar!!.progress = progress }
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
task = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
rlProgressBar.post { rlProgressBar.visibility = View.GONE }
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
}
mpFinishSignSound.start()
} else {
lastReadSuccess = false
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra(Constant.EXTRA_MESSAGE, getString(R.string.cannot_sign_transaction_make_sure_you_enter_correct_pin_2))
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
setResult(Constant.RESULT_INVALID_PIN_, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) {
try {
val intent = Intent()
intent.putExtra(Constant.EXTRA_MESSAGE, getString(R.string.cannot_sign_transaction_wrong_amount))
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
setResult(Activity.RESULT_CANCELED, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}
progressBar?.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog.message = getText(R.string.the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.the_nfc_adapter_length_apdu_advice).toString()
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else {
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
}
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
rlProgressBar.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadCancel() {
task = null
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadBeforeRequest(timeout: Int) {
LOG.i(TAG, "onReadBeforeRequest timeout $timeout")
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
LOG.i(TAG, "onReadAfterRequest")
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
override fun onReadWait(msec: Int) {
LOG.i(TAG, "onReadWait msec $msec")
WaitSecurityDelayDialog.onReadWait(this, msec)
}
}

View file

@ -0,0 +1,344 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.ConfirmTransactionActivity"
tools:ignore="Autofill">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:paddingTop="8dp"
android:paddingBottom="4dp"
android:text="@string/send_transaction"
android:textAlignment="center"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/llTransaction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/btn_light"
android:orientation="vertical"
android:paddingStart="5dp"
android:paddingTop="8dp"
android:paddingEnd="5dp"
android:paddingBottom="8dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:text="@string/from_banknote"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
android:visibility="gone" />
<TextView
android:id="@+id/tvCardID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:fontFamily="@font/maax"
android:textColor="@color/black"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:visibility="gone"
tools:text="BB00 0000 1210 0233" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:fontFamily="@font/maax"
android:text="@string/with_balance"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
android:visibility="gone" />
<TextView
android:id="@+id/tvBalance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:fontFamily="@font/maax"
android:textColor="@color/black"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:visibility="gone"
tools:text="4.51735 BTC" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/maax"
android:text="@string/send_to_wallet"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<EditText
android:id="@+id/etWallet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:fontFamily="@font/maax"
android:hint="@string/target_wallet_address"
android:inputType="textMultiLine"
android:maxLines="2"
android:padding="12dp"
android:singleLine="false"
android:textColor="@color/black"
android:textSize="@dimen/text_size_1_small"
android:textStyle="bold" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fontFamily="@font/maax"
android:text="@string/amount"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvIncFee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginBottom="1dp"
android:fontFamily="@font/maax"
android:textColor="@color/primary"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@+id/textView3"
app:layout_constraintLeft_toRightOf="@+id/textView3"
tools:text="(including fee)" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/etAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:fontFamily="@font/maax"
android:hint="@string/payment_amount"
android:inputType="numberDecimal"
android:padding="12dp"
android:textColor="@color/black"
android:textColorLink="@color/colorPrimary"
android:textSize="@dimen/text_size_1_large"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvCurrency"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/maax"
android:text="@string/m_btc"
android:textSize="@dimen/text_size_1_large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/etAmount"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/llFee"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/llTransaction">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="4dp"
android:fontFamily="@font/maax"
android:text="@string/fee"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<info.hoang8f.android.segmented.SegmentedGroup
android:id="@+id/rgFee"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkedButton="@+id/rbNormalFee"
android:gravity="center"
android:orientation="horizontal"
app:sc_border_width="2dp"
app:sc_corner_radius="10dp"
app:sc_tint_color="@color/colorPrimary">
<RadioButton
android:id="@+id/rbMinimalFee"
style="@style/RadioButton"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/minimal"
android:textSize="@dimen/text_size_medium" />
<RadioButton
android:id="@+id/rbNormalFee"
style="@style/RadioButton"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/normal"
android:textSize="@dimen/text_size_medium" />
<RadioButton
android:id="@+id/rbMaximumFee"
style="@style/RadioButton"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/priority"
android:textSize="@dimen/text_size_medium" />
</info.hoang8f.android.segmented.SegmentedGroup>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/etFee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:enabled="false"
android:fontFamily="@font/maax"
android:hint="@string/fee_amount"
android:inputType="numberDecimal"
android:padding="12dp"
android:textColor="@color/black"
android:textColorLink="@android:color/holo_blue_dark"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="100dp" />
<TextView
android:id="@+id/tvCurrency2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/maax"
android:text="@string/m_btc"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/etFee"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvFeeEquivalent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textColor="@android:color/darker_gray"
android:textSize="@dimen/text_size_1_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/tvCurrency2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<Button
android:id="@+id/btnSend"
style="@style/AppTheme.RoundedCornerMaterialButton"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="16dp"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/send_1"
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ProgressBar
android:id="@+id/progressBar"
style="@android:style/Widget.Holo.Light.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginTop="4dp"
android:elevation="1dp"
android:indeterminate="true"
android:indeterminateTint="@color/colorPrimary"
android:visibility="invisible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/llFee" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,265 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.PrepareTransactionActivity"
tools:ignore="contentDescription">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/send_transaction"
android:textAlignment="center"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/llFrom"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/btn_light"
android:orientation="vertical"
android:paddingStart="8dp"
android:paddingTop="24dp"
android:paddingEnd="8dp"
android:paddingBottom="24dp"
android:visibility="gone"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/maax"
android:text="@string/from_banknote"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium" />
<TextView
android:id="@+id/tvCardID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:fontFamily="@font/maax"
android:textColor="@color/black"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
tools:text="BB00 0000 1210 0233" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="12dp"
android:fontFamily="@font/maax"
android:text="@string/with_balance"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tvBalance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="2dp"
android:fontFamily="@font/maax"
android:textColor="@color/black"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
tools:text="4.51735 Btc" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/llTo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/card_state_loaded_with_zero"
android:orientation="vertical"
android:paddingTop="32dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintLeft_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/llFrom">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:fontFamily="@font/maax"
android:text="@string/send_to_wallet"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivCamera"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="12dp"
android:src="@drawable/qr_scan"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<EditText
android:id="@+id/etWallet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:drawableEnd="@android:drawable/ic_menu_close_clear_cancel"
android:fontFamily="@font/maax"
android:hint="@string/enter_wallet_address"
android:imeOptions="actionNext"
android:inputType="text|textMultiLine|textNoSuggestions"
android:padding="12dp"
android:singleLine="false"
android:textColor="@color/colorPrimaryDark"
android:textColorLink="@android:color/holo_blue_dark"
android:textSize="@dimen/text_size_medium"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/ivCamera"
tools:layout_editor_absoluteY="2dp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/maax"
android:text="@string/amount"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="8dp">
<EditText
android:id="@+id/etAmount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:digits="0123456789.,"
android:fontFamily="@font/maax"
android:hint="@string/enter_amount"
android:imeOptions="actionDone"
android:inputType="numberDecimal"
android:padding="12dp"
android:textColor="@color/colorPrimaryDark"
android:textSize="@dimen/text_size_1_large"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="150dp" />
<TextView
android:id="@+id/tvCurrency"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/maax"
android:text="@string/m_btc"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_1_large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/etAmount" />
</androidx.constraintlayout.widget.ConstraintLayout>
<info.hoang8f.android.segmented.SegmentedGroup
android:id="@+id/rgIncFee"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkedButton="@+id/rbFeeIn"
android:gravity="center"
android:orientation="horizontal"
app:sc_border_width="2dp"
app:sc_corner_radius="10dp"
app:sc_tint_color="@color/colorPrimary"
tools:layout_editor_absoluteX="10dp">
<RadioButton
android:id="@+id/rbFeeIn"
style="@style/RadioButton"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/including_fee_"
android:textSize="@dimen/text_size_medium" />
<RadioButton
android:id="@+id/rbFeeOut"
style="@style/RadioButton"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/not_including_fee_"
android:textSize="@dimen/text_size_medium" />
</info.hoang8f.android.segmented.SegmentedGroup>
</LinearLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnVerify"
style="@style/AppTheme.RoundedCornerMaterialButton"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="16dp"
android:fontFamily="@font/saira_condensed_bold"
android:text="@string/verify"
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.SignTransactionActivity">
<include
layout="@layout/layout_touch_card"
android:layout_width="400dp"
android:layout_height="250dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.25" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.75">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:text="@string/now_touch_the_banknote_with_id"
android:textAlignment="center"
android:textSize="@dimen/text_size_medium" />
<TextView
android:id="@+id/tvCardID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:fontFamily="@font/maax"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="@dimen/text_size_large"
android:visibility="gone"
tools:text="CB02 0000 0002 5000" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:text="@string/to_sign_the_transaction"
android:textAlignment="center"
android:textSize="@dimen/text_size_medium" />
</LinearLayout>
<include layout="@layout/layout_progress_horizontal" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Cardano</string>
<string name="verify">Confirm</string>
<string name="send_1">Checkout</string>
<string name="now_touch_the_banknote_with_id">Now touch the banknote</string>
</resources>

View file

@ -1,6 +1,7 @@
package com.tangem.card_common.data;
import com.tangem.card_common.reader.CardCrypto;
import com.tangem.card_common.util.PBKDF2;
import com.tangem.card_common.util.Util;
import java.util.ArrayList;
@ -15,11 +16,25 @@ public class Issuer {
static class KeyPair {
String privateKey;
String privateKeyEncrypted;
String publicKey;
byte[] getPrivateKey() throws Exception {
if (privateKey != null) {
return Util.hexToBytes(privateKey);
} else if (privateKeyEncrypted != null) {
byte[] A = PBKDF2.deriveKey(Util.calculateSHA256(Tangem().getPrivateTransactionKey()),Util.calculateSHA256(Tangem().getPrivateDataKey()), 100);
//String B = Util.bytesToHex(CardCrypto.Encrypt(A, Util.hexToBytes(""), true));
try {
return CardCrypto.Decrypt(A, Util.hexToBytes(privateKeyEncrypted), true);
}
catch (Exception e)
{
e.printStackTrace();
throw new Exception("No private key!");
}
} else {
throw new Exception("No private key!");
}
@ -57,8 +72,7 @@ public class Issuer {
instances.add(unknown);
}
public static void fillIssuers(List<Issuer> issuers)
{
public static void fillIssuers(List<Issuer> issuers) {
instances.addAll(issuers);
}
@ -119,4 +133,9 @@ public class Issuer {
public static Issuer Unknown() {
return instances.get(0);
}
public static Issuer Tangem() {
return instances.get(1);
}
}

View file

@ -821,9 +821,9 @@ public class CardProtocol {
* @throws Exception - if something went wrong
*/
public TLVList run_SignHashes(String PIN2, byte[][] hashes, byte[] issuerTransactionSignature, byte[] issuerData, byte[] issuerDataSignature) throws Exception {
if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer && mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash) {
throw new TangemException("Card don't support signing hashes!");
}
// if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer && mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash) {
// throw new TangemException("Card don't support signing hashes!");
// }
ByteArrayOutputStream bs = new ByteArrayOutputStream();
if (hashes.length > 10) throw new TangemException("To much hashes in one transaction!");
@ -837,7 +837,7 @@ public class CardProtocol {
rqApdu.addTLV_U8(TLV.Tag.TAG_TrOut_HashSize, hashes[0].length);
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, bs.toByteArray());
if (issuerData != null) {
if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData)
if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData))
throw new TangemException("Card don't support simultaneous sign with write issuer data!");
if (issuerDataSignature == null)
@ -849,7 +849,7 @@ public class CardProtocol {
if (issuerTransactionSignature != null) {
//byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), bs.toByteArray());
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerTransactionSignature);
} else if (mCard.getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer) {
} else if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash)) {
throw new TangemException("Card require issuer validation before sign the transaction!");
}
@ -900,7 +900,7 @@ public class CardProtocol {
bs.write(bTxOutData);
if (issuerData != null) {
if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData)
if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer_And_WriteIssuerData))
throw new TangemException("Card don't support simultaneous sign with write issuer data!");
if (issuerDataSignature == null)
@ -911,7 +911,7 @@ public class CardProtocol {
}
if (issuerTransactionSignature != null) {
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerTransactionSignature);
} else if (mCard.getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer) {
} else if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Raw)) {
throw new TangemException("Card require issuer validation before sign the transaction!");
}

View file

@ -38,7 +38,7 @@ public class CustomReadCardTask extends Thread {
// this fields are static to optimize process when need enter pin and scan card again
private static ArrayList<String> lastRead_UnsuccessfullPINs = new ArrayList<>();
private static TangemCard.EncryptionMode lastRead_Encryption = null;
private static String lastRead_UID;
private static String lastRead_UID = "";
public static void resetLastReadInfo() {
lastRead_UID = "";
@ -419,6 +419,7 @@ public class CustomReadCardTask extends Thread {
} finally {
Log.i(TAG, String.format("[-- Finish task -- %s --]", getClass().getSimpleName()));
mNotifications.onReadWait(0);
mNotifications.onReadFinish(protocol);
}
} finally {

View file

@ -0,0 +1,100 @@
package com.tangem.card_common.tasks;
import com.tangem.card_common.data.TangemCard;
import com.tangem.card_common.data.external.CardDataSubstitutionProvider;
import com.tangem.card_common.data.external.PINsProvider;
import com.tangem.card_common.reader.CardProtocol;
import com.tangem.card_common.reader.NfcReader;
import com.tangem.card_common.reader.TLV;
import com.tangem.card_common.reader.TLVList;
import com.tangem.card_common.util.Log;
import java.io.ByteArrayOutputStream;
public class OneTouchSignTask extends ReadCardInfoTask {
public static final String TAG = OneTouchSignTask.class.getSimpleName();
/**
* Transaction Engine request/notifications during sign process
*/
public interface TransactionToSign {
boolean isSigningOnCardSupported(TangemCard card);
boolean isIssuerCanSignData(TangemCard card);
boolean isIssuerCanSignTransaction(TangemCard card);
byte[][] getHashesToSign(TangemCard card) throws Exception;
byte[] getRawDataToSign(TangemCard card) throws Exception;
String getHashAlgToSign(TangemCard card) throws Exception;
byte[] getIssuerTransactionSignature(TangemCard card, byte[] dataToSignByIssuer) throws Exception;
void onSignCompleted(TangemCard card, byte[] signature) throws Exception;
}
private TransactionToSign transactionToSign;
public OneTouchSignTask(NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications, TransactionToSign transactionToSign) {
super(reader, cardDataSubstitutionProvider, pinsProvider, notifications);
this.transactionToSign = transactionToSign;
}
@Override
public void run_Task() throws Exception {
super.run_Task();
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
mNotifications.onReadProgress(protocol, 30);
if (isCancelled) return;
if (!transactionToSign.isSigningOnCardSupported(mCard)) {
throw new CardProtocol.TangemException("Signing method isn't supported!");
}
TLVList signResult;
if (transactionToSign.isIssuerCanSignTransaction(mCard) &&
(
mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer) ||
(
transactionToSign.isIssuerCanSignData(mCard) &&
mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData)
)
)
) {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
byte[][] hashes = transactionToSign.getHashesToSign(mCard);
if (hashes.length > 10) throw new CardProtocol.TangemException("To much hashes in one transaction!");
for (int i = 0; i < hashes.length; i++) {
if (i != 0 && hashes[0].length != hashes[i].length)
throw new CardProtocol.TangemException("Hashes length must be identical!");
bs.write(hashes[i]);
}
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), hashes, transactionToSign.getIssuerTransactionSignature(mCard, bs.toByteArray()), null, null);
} else if (
transactionToSign.isIssuerCanSignTransaction(mCard) &&
(
mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) ||
(
transactionToSign.isIssuerCanSignData(mCard) &&
mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer_And_WriteIssuerData)
)
)
) {
byte[] txOut = transactionToSign.getRawDataToSign(mCard);
signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), transactionToSign.getHashAlgToSign(mCard), txOut, transactionToSign.getIssuerTransactionSignature(mCard, txOut), null, null);
} else if (mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash)) {
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), transactionToSign.getHashesToSign(mCard), null, null, null);
} else if (mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Raw)) {
signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), transactionToSign.getHashAlgToSign(mCard), transactionToSign.getRawDataToSign(mCard), null, null, null);
} else {
throw new CardProtocol.TangemException("Signing method isn't supported!");
}
transactionToSign.onSignCompleted(mCard, signResult.getTLV(TLV.Tag.TAG_Signature).Value);
mNotifications.onReadProgress(protocol, 100);
}
}

View file

@ -2,12 +2,18 @@ package com.tangem.card_common.util;
public class Log {
public static void i(String logTag, String message) {
if( loggerInstance!=null ) loggerInstance.i(logTag,message);
}
public static void e(String logTag, String message) {
if( loggerInstance!=null ) loggerInstance.e(logTag,message);
}
public static void v(String logTag, String message) {
if( loggerInstance!=null ) loggerInstance.v(logTag,message);
}
private static LoggerInterface loggerInstance=null;
public static void setLogger(LoggerInterface logger)
{
loggerInstance=logger;
}
}

View file

@ -0,0 +1,7 @@
package com.tangem.card_common.util;
public interface LoggerInterface {
void i(String logTag, String message);
void e(String logTag, String message);
void v(String logTag, String message);
}

View file

@ -29,7 +29,7 @@ dependencies {
implementation project(':card-common')
// implementation 'com.github.TangemCash.card_android-common:card_android-android:0.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'