diff --git a/app/build.gradle b/app/build.gradle index 3ca01aaf09..37203672f4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,6 +30,7 @@ android { debuggable true minifyEnabled false applicationIdSuffix ".debug" + versionNameSuffix '-DEBUG' signingConfig signingConfigs.debug } } @@ -90,6 +91,8 @@ dependencies { 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' @@ -98,7 +101,4 @@ dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.2.0' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0' - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0" - //implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5" } \ No newline at end of file diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml index 9ef8838437..ec5cc446df 100644 --- a/app/src/debug/res/values/strings.xml +++ b/app/src/debug/res/values/strings.xml @@ -1,6 +1,6 @@ - DEBUG + \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt index 25fa8f21d3..f6c1538c21 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt @@ -81,6 +81,17 @@ object CoinEngineFactory { Log.e(TAG, "Can't create Cardano CoinEngine!") } return result + +// var result: EthEngine? +// try { +// result = EthEngine(context) +// +// } catch (e: Exception) { +// e.printStackTrace() +// result = null +// Log.e(TAG, "Can't create Cardano CoinEngine!") +// } +// return result } } \ No newline at end of file diff --git a/app/src/tangemAccess/java/com/tangem/ui/SignTransactionActivity.kt b/app/src/tangemAccess/java/com/tangem/ui/SignTransactionActivity.kt new file mode 100644 index 0000000000..089fe31274 --- /dev/null +++ b/app/src/tangemAccess/java/com/tangem/ui/SignTransactionActivity.kt @@ -0,0 +1,301 @@ +package com.tangem.ui.activity + +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_common.reader.CardProtocol +import com.tangem.card_common.tasks.SignTask +import com.tangem.card_common.util.Util +import com.tangem.domain.wallet.CoinEngine +import com.tangem.domain.wallet.CoinEngineFactory +import com.tangem.domain.wallet.TangemContext +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.activity_sign_transaction.* +import kotlinx.android.synthetic.main.layout_progress_horizontal.* +import kotlinx.android.synthetic.main.layout_touch_card.* +import com.tangem.card_android.data.asBundle +import com.tangem.card_android.data.EXTRA_TANGEM_CARD +import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID + +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 signTransactionTask: SignTask? = 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() { + signTransactionTask?.cancel(true) + super.onPause() + } + + public override fun onStop() { + signTransactionTask?.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) + val uid = tag.id + val sUID = Util.byteArrayToHexString(uid) + + if (sUID == ctx.card.uid) { + if (lastReadSuccess) + isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000 + else + isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000 + + val coinEngine = CoinEngineFactory.create(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 transactionToSign = coinEngine?.constructTransaction(amount, fee, isIncludeFee, outAddressStr) + + signTransactionTask = SignTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, transactionToSign) + signTransactionTask?.start() + } else + nfcManager.ignoreTag(isoDep.tag) + + } 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?) { + signTransactionTask = 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() { + signTransactionTask = null + + progressBar?.postDelayed({ + try { + progressBar?.progress = 0 + progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY) + progressBar?.visibility = View.INVISIBLE + } catch (e: Exception) { + e.printStackTrace() + } + }, 500) + } + +// private val waitSecurityDelayDialogNew = WaitSecurityDelayDialogNew() + + override fun onReadBeforeRequest(timeout: Int) { + LOG.i(TAG, "onReadBeforeRequest timeout $timeout") + WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout) + +// if (!waitSecurityDelayDialogNew.isAdded) +// waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG) + + +// val readBeforeRequest = ReadBeforeRequest() +// readBeforeRequest.timeout = timeout +// EventBus.getDefault().post(readBeforeRequest) + } + + override fun onReadAfterRequest() { + LOG.i(TAG, "onReadAfterRequest") + WaitSecurityDelayDialog.onReadAfterRequest(this) + +// val readAfterRequest = ReadAfterRequest() +// EventBus.getDefault().post(readAfterRequest) + } + + override fun onReadWait(msec: Int) { + LOG.i(TAG, "onReadWait msec $msec") + WaitSecurityDelayDialog.onReadWait(this, msec) + +// val readWait = ReadWait() +// readWait.msec = msec +// EventBus.getDefault().post(readWait) + } + +} \ No newline at end of file diff --git a/app/src/tangemCardano/java/com/tangem/ui/SignTransactionActivity.kt b/app/src/tangemCardano/java/com/tangem/ui/SignTransactionActivity.kt index 588a5af413..dee2044bd2 100644 --- a/app/src/tangemCardano/java/com/tangem/ui/SignTransactionActivity.kt +++ b/app/src/tangemCardano/java/com/tangem/ui/SignTransactionActivity.kt @@ -86,7 +86,7 @@ class SignTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, isIncludeFee = intent.getBooleanExtra(Constant.EXTRA_FEE_INCLUDED, true) outAddressStr = intent.getStringExtra(Constant.EXTRA_TARGET_ADDRESS) - // tvCardID.text = ctx.card!!.cidDescription + // tvCardID.text = ctx.card!!.cidDescription progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY) progressBar.visibility = View.INVISIBLE } @@ -133,55 +133,54 @@ class SignTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, // if (lastReadSuccess) // isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000 // else - isoDep.timeout = 65000 + 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 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 transactionToSign = coinEngine?.constructTransaction(amount, fee, isIncludeFee, outAddressStr) // // task = SignTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, transactionToSign) // task?.start() - 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!!) - } + 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 onProgress() { + } - override fun allowAdvance(): Boolean { - return true - } - }) - } + override fun allowAdvance(): Boolean { + return true + } + }) + } - fun initData(card: TangemCard) - { + fun initData(card: TangemCard) { // if( ctx==null ){ // ctx=TangemContext(card) // } - if( ctx.card==null ){ - ctx.card=card - } - if( ctx.coinData==null ) { - ctx.coinData = CardanoData() - } - if( ctx.coinData.wallet.isNullOrEmpty() ) { - coinEngine!!.defineWallet() - runBlocking { requestBalanceAndUnspentTransactions() } - Log.e(MainActivity.TAG, "requestBalanceAndUnspentTransactions completed") + if (ctx.card == null) { + ctx.card = card + } + if (ctx.coinData == null) { + ctx.coinData = CardanoData() + } + if (ctx.coinData.wallet.isNullOrEmpty()) { + coinEngine.defineWallet() + runBlocking { requestBalanceAndUnspentTransactions() } + Log.e(MainActivity.TAG, "requestBalanceAndUnspentTransactions completed") // coinEngine?.setOnNeedSendTransaction { tx -> // if (tx != null) { @@ -192,43 +191,43 @@ class SignTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, // } // } - } - if( txToSign==null ) txToSign=coinEngine!!.constructTransaction(amount,fee,isIncludeFee,outAddressStr) } - - override fun isSigningOnCardSupported(card: TangemCard?): Boolean { - initData(card!!) - return (card?.blockchainID== Blockchain.Cardano.id)and(txToSign!!.isSigningMethodSupported(card?.signingMethod)) - } - - override fun getHashesToSign(card: TangemCard?): Array { - 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!!) - return txToSign!!.getIssuerTransactionSignature(dataToSignByIssuer) - } - - override fun onSignCompleted(card: TangemCard?, signature: ByteArray?) { - initData(card!!) - txToSign!!.onSignCompleted(signature) - } - + if (txToSign == null) txToSign = coinEngine!!.constructTransaction(amount, fee, isIncludeFee, outAddressStr) } - task = OneTouchSignTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, tx) - task!!.start() + + override fun isSigningOnCardSupported(card: TangemCard?): Boolean { + initData(card!!) + return (card?.blockchainID == Blockchain.Cardano.id) and (txToSign!!.isSigningMethodSupported(card?.signingMethod)) + } + + override fun getHashesToSign(card: TangemCard?): Array { + 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!!) + 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() // } else // nfcManager.ignoreTag(isoDep.tag)