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.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.activity.OnBackPressedCallback import com.tangem.App import com.tangem.Constant import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation import com.tangem.tangem_sdk.android.reader.NfcManager import com.tangem.tangem_sdk.android.reader.NfcReader import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID import com.tangem.tangem_sdk.data.asBundle import com.tangem.tangem_card.data.TangemCard import com.tangem.tangem_card.reader.CardCrypto import com.tangem.tangem_card.reader.CardProtocol import com.tangem.tangem_card.tasks.CustomReadCardTask import com.tangem.tangem_card.tasks.OneTouchSignTask import com.tangem.tangem_card.tasks.SignTask import com.tangem.tangem_card.util.Log import com.tangem.wallet.CoinEngine import com.tangem.wallet.CoinEngineFactory import com.tangem.wallet.TangemContext import com.tangem.ui.activity.MainActivity import com.tangem.ui.dialog.NoExtendedLengthSupportDialog import com.tangem.ui.dialog.WaitSecurityDelayDialog import com.tangem.ui.fragment.BaseFragment import com.tangem.ui.navigation.NavigationResultListener 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 SignTransactionFragment : BaseFragment(), NavigationResultListener, NfcAdapter.ReaderCallback, CardProtocol.Notifications { companion object { val TAG: String = SignTransactionFragment::class.java.simpleName } override val layoutId = R.layout.fragment_sign_transaction 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) ctx = TangemContext.loadFromBundle(context, arguments) val callback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { navigateBackWithResult(Activity.RESULT_CANCELED) } } requireActivity().onBackPressedDispatcher.addCallback(this, callback) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound) // init NFC Antenna nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) nfcDeviceAntenna.init() amount = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_AMOUNT), arguments?.getString(Constant.EXTRA_AMOUNT_CURRENCY)) fee = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_FEE), arguments?.getString(Constant.EXTRA_FEE_CURRENCY)) isIncludeFee = arguments?.getBoolean(Constant.EXTRA_FEE_INCLUDED, true) ?: true outAddressStr = arguments?.getString(Constant.EXTRA_TARGET_ADDRESS) // tvCardID.text = ctx.card!!.cidDescription progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY) progressBar.visibility = View.INVISIBLE } override fun onPause() { task?.cancel(true) super.onPause() } override fun onStop() { task?.cancel(true) super.onStop() } override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) { if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) { navigateBackWithResult(resultCode, data) } } 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 data = Bundle() ctx.saveToBundle(data) data.putByteArray(Constant.EXTRA_TX, tx) navigateForResult( Constant.REQUEST_CODE_SEND_TRANSACTION_, R.id.action_signTransactionFragment_to_sendTransactionFragment, data) } } 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 { 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((activity as MainActivity).nfcManager, isoDep), App.localStorage, App.pinStorage, this, tx) task!!.start() } catch (e: CardProtocol.TangemException_WrongAmount) { try { val data = Bundle() data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount)) data.putString(EXTRA_TANGEM_CARD_UID, ctx.card.uid) data.putBundle(EXTRA_TANGEM_CARD, ctx.card.asBundle) navigateBackWithResult(Activity.RESULT_CANCELED, data) } 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 data = Bundle() data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_cannot_sign)) data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid) data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle) navigateBackWithResult(Constant.RESULT_INVALID_PIN_, data) } catch (e: Exception) { e.printStackTrace() } }, 500) } else { if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) { try { val data = Bundle() data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount)) data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid) data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle) navigateBackWithResult(Activity.RESULT_CANCELED, data) } catch (e: Exception) { e.printStackTrace() } } progressBar?.post { if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) { if (!NoExtendedLengthSupportDialog.allReadyShowed) { NoExtendedLengthSupportDialog.message = getText(R.string.dialog_the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.dialog_the_nfc_adapter_length_apdu_advice).toString() NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG) } } else { Toast.makeText(context, R.string.general_notification_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(activity, timeout) } override fun onReadAfterRequest() { LOG.i(TAG, "onReadAfterRequest") WaitSecurityDelayDialog.onReadAfterRequest(activity) } override fun onReadWait(msec: Int) { LOG.i(TAG, "onReadWait msec $msec") WaitSecurityDelayDialog.onReadWait(activity, msec) } }