Updated on 2026-08-14
This commit is contained in:
parent
449840473a
commit
64ddaa4541
63 changed files with 128 additions and 164 deletions
|
|
@ -0,0 +1,296 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
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.data.Blockchain
|
||||
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.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.data.loadFromBundle
|
||||
import com.tangem.cardcommon.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.*
|
||||
|
||||
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.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.INVISIBLE
|
||||
}
|
||||
|
||||
// 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.create(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_)
|
||||
} else
|
||||
Toast.makeText(this, getString(R.string.no_connection), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
coinEngine!!.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("UID") && data.extras!!.containsKey("Card")) {
|
||||
val updatedCard = TangemCard(data.getStringExtra("UID"))
|
||||
updatedCard.loadFromBundle(data.getBundleExtra("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("message", message)
|
||||
setResult(errorCode, intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.cardandroid.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.asBundle
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.CreateNewWalletTask
|
||||
import com.tangem.cardcommon.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_create_new_wallet.*
|
||||
import kotlinx.android.synthetic.main.layout_progress_horizontal.*
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
|
||||
class CreateNewWalletActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
companion object {
|
||||
fun callingIntent(context: Context, ctx: TangemContext): Intent {
|
||||
val intent = Intent(context, CreateNewWalletActivity::class.java)
|
||||
intent.putExtra("UID", ctx.card!!.uid)
|
||||
intent.putExtra("Card", ctx.card!!.asBundle)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var ctx: TangemContext
|
||||
|
||||
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
|
||||
|
||||
private var createNewWalletTask: CreateNewWalletTask? = null
|
||||
private var lastReadSuccess = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_create_new_wallet)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
||||
tvCardId.text = ctx.card!!.cidDescription
|
||||
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
// Log.v(TAG, "UID: " + sUID);
|
||||
|
||||
if (sUID == ctx.card!!.uid) {
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 5000
|
||||
else
|
||||
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 65000
|
||||
|
||||
createNewWalletTask = CreateNewWalletTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
|
||||
createNewWalletTask!!.start()
|
||||
} else {
|
||||
// Log.d(TAG, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")");
|
||||
nfcManager.ignoreTag(isoDep.tag)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
createNewWalletTask?.cancel(true)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar?.progress = 5
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
createNewWalletTask = 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)
|
||||
val intent = Intent()
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("Card", cardProtocol.card.asBundle)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
}
|
||||
} else {
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
|
||||
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("message", "Cannot create wallet. Make sure you enter correct PIN2!")
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("Card", cardProtocol.card!!.asBundle)
|
||||
setResult(Constant.RESULT_INVALID_PIN, intent)
|
||||
finish()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
} else {
|
||||
progressBar!!.post {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
|
||||
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_SHORT).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 onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar?.post { progressBar?.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
createNewWalletTask = 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 onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(this, msec)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(this)
|
||||
}
|
||||
|
||||
}
|
||||
257
app/src/main/java/com/tangem/ui/activity/EmptyWalletActivity.kt
Normal file
257
app/src/main/java/com/tangem/ui/activity/EmptyWalletActivity.kt
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.text.Html
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.asBundle
|
||||
import com.tangem.cardandroid.data.loadFromBundle
|
||||
import com.tangem.cardcommon.data.TangemCard
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.VerifyCardTask
|
||||
import com.tangem.cardcommon.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.*
|
||||
import kotlinx.android.synthetic.main.layout_tangem_card.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class EmptyWalletActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
companion object {
|
||||
val TAG: String = EmptyWalletActivity::class.java.simpleName
|
||||
fun callingIntent(context: Context, ctx: TangemContext): Intent {
|
||||
val intent = Intent(context, EmptyWalletActivity::class.java)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
internal lateinit var navigator: Navigator
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var ctx: TangemContext
|
||||
|
||||
private var lastReadSuccess = true
|
||||
private var verifyCardTask: VerifyCardTask? = null
|
||||
private var requestPIN2Count = 0
|
||||
|
||||
private var cardProtocol: CardProtocol? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_empty_wallet)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
tvIssuer.text = ctx.card!!.issuerDescription
|
||||
|
||||
if (ctx.card!!.tokenSymbol.length > 1) {
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(ctx.blockchainName, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(ctx.blockchainName)
|
||||
tvBlockchain.text = html
|
||||
} else
|
||||
tvBlockchain.text = ctx.blockchainName
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card))
|
||||
|
||||
// set listeners
|
||||
btnNewWallet.setOnClickListener {
|
||||
requestPIN2Count = 0
|
||||
val intent = Intent(baseContext, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
intent.putExtra("UID", ctx.card!!.uid)
|
||||
intent.putExtra("Card", ctx.card!!.asBundle)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2)
|
||||
}
|
||||
|
||||
btnDetails.setOnClickListener {
|
||||
if (cardProtocol != null)
|
||||
navigator.showVerifyCard(this, ctx)
|
||||
else
|
||||
UtilHelper.showSingleToast(this, getString(R.string.need_attach_card_again))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == Constant.REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (data != null) {
|
||||
data.putExtra("modification", "updateAndViewCard")
|
||||
data.putExtra("updateDelay", 0)
|
||||
setResult(Activity.RESULT_OK, data)
|
||||
}
|
||||
finish()
|
||||
} else {
|
||||
if (data != null && data.extras!!.containsKey("UID") && data.extras!!.containsKey("Card")) {
|
||||
val updatedCard = TangemCard(data.getStringExtra("UID"))
|
||||
updatedCard.loadFromBundle(data.getBundleExtra("Card"))
|
||||
ctx.card = updatedCard
|
||||
}
|
||||
if (resultCode == Constant.RESULT_INVALID_PIN && requestPIN2Count < 2) {
|
||||
requestPIN2Count++
|
||||
val intent = Intent(baseContext, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
intent.putExtra("UID", ctx.card!!.uid)
|
||||
intent.putExtra("Card", ctx.card!!.asBundle)
|
||||
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) {
|
||||
navigator.showCreateNewWallet(this, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
val isoDep = IsoDep.get(tag)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
if (ctx.card!!.uid != sUID) {
|
||||
LOG.d(TAG, "Invalid UID: $sUID")
|
||||
nfcManager.ignoreTag(isoDep.tag)
|
||||
return
|
||||
} else {
|
||||
LOG.d(TAG, "UID: $sUID")
|
||||
}
|
||||
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = 1000
|
||||
else
|
||||
isoDep.timeout = 65000
|
||||
|
||||
verifyCardTask = VerifyCardTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, App.firmwaresStorage, this)
|
||||
verifyCardTask?.start()
|
||||
} 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 onReadFinish(cardProtocol: CardProtocol?) {
|
||||
verifyCardTask = 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)
|
||||
this.cardProtocol = cardProtocol
|
||||
}
|
||||
} else {
|
||||
// remove last UIDs because of error and no card read
|
||||
progressBar?.post {
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed)
|
||||
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
|
||||
} else
|
||||
Toast.makeText(this@EmptyWalletActivity, 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 onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar?.post { progressBar!!.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
verifyCardTask = null
|
||||
|
||||
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 onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(this, msec)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Bundle
|
||||
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.ui.fragment.LoadedWallet
|
||||
import com.tangem.wallet.R
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoadedWalletActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var navigator: Navigator
|
||||
|
||||
companion object {
|
||||
fun callingIntent(context: Context, lastTag: Tag, ctx: TangemContext): Intent {
|
||||
val intent = Intent(context, LoadedWalletActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_loaded_wallet)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
if (intent.extras!!.containsKey(NfcAdapter.EXTRA_TAG)) {
|
||||
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
|
||||
if (tag != null) {
|
||||
val fragment = supportFragmentManager.findFragmentById(R.id.loaded_wallet_fragment) as LoadedWallet
|
||||
fragment.onTagDiscovered(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
57
app/src/main/java/com/tangem/ui/activity/LogoActivity.kt
Normal file
57
app/src/main/java/com/tangem/ui/activity/LogoActivity.kt
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_logo.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class LogoActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
fun callingIntent(context: Context, autoHide: Boolean): Intent {
|
||||
val intent = Intent(context, LogoActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_AUTO_HIDE, autoHide)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
internal lateinit var navigator: Navigator
|
||||
|
||||
private val hideRunnable = Runnable { this.hide() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_logo)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
ivLogo.setOnClickListener { hide() }
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
tvAppVersion.text = "BETA v." + BuildConfig.VERSION_NAME + "\n" + "dev" + "\n" + "build " + BuildConfig.VERSION_CODE
|
||||
else
|
||||
tvAppVersion.text = "BETA v." + BuildConfig.VERSION_NAME
|
||||
|
||||
if (intent.getBooleanExtra(Constant.EXTRA_AUTO_HIDE, true))
|
||||
ivLogo.postDelayed(hideRunnable, Constant.MILLIS_AUTO_HIDE.toLong())
|
||||
}
|
||||
|
||||
private fun hide() {
|
||||
navigator.showMain(this)
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
375
app/src/main/java/com/tangem/ui/activity/MainActivity.kt
Normal file
375
app/src/main/java/com/tangem/ui/activity/MainActivity.kt
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.scottyab.rootbeer.RootBeer
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Logger
|
||||
import com.tangem.data.network.ServerApiCommon
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.RootFoundDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.cardandroid.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.loadFromBundle
|
||||
import com.tangem.cardandroid.data.saveToBundle
|
||||
import com.tangem.cardcommon.data.TangemCard
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.ReadCardInfoTask
|
||||
import com.tangem.util.CommonUtil
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.util.PhoneUtility
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
companion object {
|
||||
val TAG: String = MainActivity::class.java.simpleName
|
||||
fun callingIntent(context: Context) = Intent(context, MainActivity::class.java)
|
||||
}
|
||||
|
||||
@Inject
|
||||
internal lateinit var navigator: Navigator
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
|
||||
private var zipFile: File? = null
|
||||
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
|
||||
private var unsuccessReadCount = 0
|
||||
private var lastTag: Tag? = null
|
||||
private var readCardInfoTask: ReadCardInfoTask? = null
|
||||
private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
|
||||
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
|
||||
if (tag != null && onNfcReaderCallback != null)
|
||||
onNfcReaderCallback!!.onTagDiscovered(tag)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
verifyPermissions()
|
||||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||
|
||||
setNfcAdapterReaderCallback(this)
|
||||
|
||||
rippleBackgroundNfc.startRippleAnimation()
|
||||
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
||||
// set phone name
|
||||
if (nfcDeviceAntenna.fullName != "")
|
||||
tvNFCHint.text = String.format(getString(R.string.scan_banknote), nfcDeviceAntenna.fullName)
|
||||
else
|
||||
tvNFCHint.text = String.format(getString(R.string.scan_banknote), getString(R.string.phone))
|
||||
|
||||
// NFC
|
||||
val intent = intent
|
||||
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
|
||||
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
|
||||
if (tag != null && onNfcReaderCallback != null) {
|
||||
onNfcReaderCallback?.onTagDiscovered(tag)
|
||||
}
|
||||
}
|
||||
|
||||
// check if root device
|
||||
val rootBeer = RootBeer(this)
|
||||
if (rootBeer.isRootedWithoutBusyBoxCheck && !BuildConfig.DEBUG)
|
||||
RootFoundDialog().show(supportFragmentManager, RootFoundDialog.TAG)
|
||||
|
||||
// set listeners
|
||||
fab.setOnClickListener { showMenu(it) }
|
||||
|
||||
val apiHelper = ServerApiCommon()
|
||||
apiHelper.setLastVersionListener { response ->
|
||||
try {
|
||||
if (response.isNullOrEmpty()) return@setLastVersionListener
|
||||
val responseVersionName = response.trim(' ', '\n', '\r', '\t')
|
||||
val responseBuildVersion = responseVersionName.split('.').last()
|
||||
val appBuildVersion = BuildConfig.VERSION_NAME.split('.').last()
|
||||
if (responseBuildVersion.toInt() > appBuildVersion.toInt()) Toast.makeText(this, "There is a new application version: $responseVersionName", Toast.LENGTH_LONG).show()
|
||||
} catch (E: Exception) {
|
||||
E.printStackTrace()
|
||||
}
|
||||
}
|
||||
apiHelper.requestLastVersion()
|
||||
}
|
||||
|
||||
private fun verifyPermissions() {
|
||||
NfcManager.verifyPermissions(this)
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.e("QRScanActivity", "User hasn't granted permission to use camera")
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), Constant.REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
Constant.REQUEST_CODE_SEND_EMAIL -> {
|
||||
if (zipFile != null) {
|
||||
zipFile!!.delete()
|
||||
zipFile = null
|
||||
}
|
||||
}
|
||||
Constant.REQUEST_CODE_ENTER_PIN_ACTIVITY -> {
|
||||
if (resultCode == Activity.RESULT_OK && lastTag != null)
|
||||
onTagDiscovered(lastTag!!)
|
||||
else
|
||||
ReadCardInfoTask.resetLastReadInfo()
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
return onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
if (BuildConfig.DEBUG) {
|
||||
for (i in 0 until menu.size()) menu.getItem(i).isVisible = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
when (id) {
|
||||
R.id.sendLogs -> {
|
||||
var f: File? = null
|
||||
try {
|
||||
f = Logger.collectLogs(this)
|
||||
if (f != null) {
|
||||
LOG.e(TAG, String.format("Collect %d log bytes", f.length()))
|
||||
CommonUtil.sendEmail(this, zipFile, TAG, "Logs", PhoneUtility.getDeviceInfo(), arrayOf(f))
|
||||
} else {
|
||||
LOG.e(TAG, "Can't create temporarily log file")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
if (f != null && f.exists())
|
||||
f.delete()
|
||||
}
|
||||
return true
|
||||
}
|
||||
R.id.managePIN -> {
|
||||
navigator.showPinSave(this, false)
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.managePIN2 -> {
|
||||
navigator.showPinSave(this, true)
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.about -> {
|
||||
navigator.showLogo(this, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
|
||||
LOG.e(TAG, "setTimeout(" + (1000 + 3000 * unsuccessReadCount) + ")")
|
||||
if (unsuccessReadCount < 2) {
|
||||
isoDep.timeout = 2000 + 5000 * unsuccessReadCount
|
||||
} else {
|
||||
isoDep.timeout = 90000
|
||||
}
|
||||
lastTag = tag
|
||||
|
||||
readCardInfoTask = ReadCardInfoTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
|
||||
readCardInfoTask?.start()
|
||||
|
||||
LOG.i(TAG, "onTagDiscovered " + Arrays.toString(tag.id))
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
nfcManager.notifyReadResult(false)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onResume() {
|
||||
super.onResume()
|
||||
nfcDeviceAntenna.animate()
|
||||
ReadCardInfoTask.resetLastReadInfo()
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
readCardInfoTask?.cancel(true)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
public override fun onStop() {
|
||||
readCardInfoTask?.cancel(true)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
readCardInfoTask = null
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.error == null) {
|
||||
nfcManager.notifyReadResult(true)
|
||||
rlProgressBar.post {
|
||||
rlProgressBar.visibility = View.GONE
|
||||
|
||||
// TODO - ??? remove save and load???
|
||||
val cardInfo = Bundle()
|
||||
cardInfo.putString("UID", cardProtocol.card.uid)
|
||||
val bCard = Bundle()
|
||||
cardProtocol.card.saveToBundle(bCard)
|
||||
cardInfo.putBundle("Card", bCard)
|
||||
|
||||
val uid = cardInfo.getString("UID")
|
||||
val card = TangemCard(uid)
|
||||
cardInfo.getBundle("Card")?.let { card.loadFromBundle(it) }
|
||||
|
||||
val ctx = TangemContext(card)
|
||||
when {
|
||||
card.status == TangemCard.Status.Loaded -> lastTag?.let {
|
||||
val engineCoin = CoinEngineFactory.create(ctx)
|
||||
?: throw CardProtocol.TangemException("Can't create CoinEngine!")
|
||||
engineCoin.defineWallet()
|
||||
navigator.showLoadedWallet(this, it, ctx)
|
||||
}
|
||||
card.status == TangemCard.Status.Empty -> navigator.showEmptyWallet(this, ctx)
|
||||
card.status == TangemCard.Status.Purged -> Toast.makeText(this, R.string.erased_wallet, Toast.LENGTH_SHORT).show()
|
||||
card.status == TangemCard.Status.NotPersonalized -> Toast.makeText(this, R.string.not_personalized, Toast.LENGTH_SHORT).show()
|
||||
else -> lastTag?.let { navigator.showLoadedWallet(this, it, ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// remove last UIDs because of error and no card read
|
||||
rlProgressBar.post {
|
||||
Toast.makeText(this, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
|
||||
unsuccessReadCount++
|
||||
|
||||
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN)
|
||||
navigator.showPinRequest(this, PinRequestActivity.Mode.RequestPIN.toString())
|
||||
else {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed)
|
||||
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
|
||||
|
||||
lastTag = null
|
||||
ReadCardInfoTask.resetLastReadInfo()
|
||||
nfcManager.notifyReadResult(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlProgressBar.postDelayed({
|
||||
try {
|
||||
rlProgressBar.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
readCardInfoTask = null
|
||||
ReadCardInfoTask.resetLastReadInfo()
|
||||
rlProgressBar.postDelayed({
|
||||
try {
|
||||
rlProgressBar.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(Objects.requireNonNull(this), msec)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(Objects.requireNonNull(this), timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(Objects.requireNonNull(this))
|
||||
}
|
||||
|
||||
private fun setNfcAdapterReaderCallback(callback: NfcAdapter.ReaderCallback) {
|
||||
onNfcReaderCallback = callback
|
||||
}
|
||||
|
||||
private fun showMenu(v: View) {
|
||||
val popup = PopupMenu(this, v)
|
||||
val inflater = popup.menuInflater
|
||||
inflater.inflate(R.menu.menu_main, popup.menu)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
popup.menu.findItem(R.id.managePIN).isEnabled = true
|
||||
popup.menu.findItem(R.id.managePIN2).isEnabled = true
|
||||
popup.menu.findItem(R.id.sendLogs).isVisible = true
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener(this)
|
||||
popup.show()
|
||||
}
|
||||
|
||||
}
|
||||
350
app/src/main/java/com/tangem/ui/activity/PinRequestActivity.kt
Normal file
350
app/src/main/java/com/tangem/ui/activity/PinRequestActivity.kt
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.fingerprint.StartFingerprintReaderTask
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.data.fingerprint.FingerprintHelper
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.cardandroid.android.data.PINStorage
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.data.loadFromBundle
|
||||
import com.tangem.cardandroid.data.EXTRA_TANGEM_CARD
|
||||
import com.tangem.cardandroid.data.EXTRA_TANGEM_CARD_UID
|
||||
import com.tangem.cardcommon.data.TangemCard
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_pin_request.*
|
||||
import kotlinx.android.synthetic.main.layout_pin_buttons.*
|
||||
import java.io.IOException
|
||||
|
||||
class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, FingerprintHelper.FingerprintHelperListener {
|
||||
companion object {
|
||||
val TAG: String = PinRequestActivity::class.java.simpleName
|
||||
|
||||
fun callingIntent(context: Activity, mode: String): Intent {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, mode)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun callingIntentRequestPin(context: Activity, mode: String, ctx: TangemContext, newPIN: String): Intent {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, mode)
|
||||
intent.putExtra(Constant.EXTRA_NEW_PIN, newPIN)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun callingIntentRequestPin2(context: Activity, mode: String, ctx: TangemContext, newPIN2: String): Intent {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, mode)
|
||||
intent.putExtra(Constant.EXTRA_NEW_PIN_2, newPIN2)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun callingIntentRequestPin2(context: Activity, mode: String, ctx: TangemContext): Intent {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, mode)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun callingIntentConfirmPin(context: Activity, mode: String, newPIN: String): Intent {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, mode)
|
||||
intent.putExtra(Constant.EXTRA_NEW_PIN, newPIN)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun callingIntentConfirmPin2(context: Activity, mode: String, newPIN2: String): Intent {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, mode)
|
||||
intent.putExtra(Constant.EXTRA_NEW_PIN_2, newPIN2)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
|
||||
lateinit var mode: Mode
|
||||
private var allowFingerprint = false
|
||||
var startFingerprintReaderTask: StartFingerprintReaderTask? = null
|
||||
private var fingerprintManager: FingerprintManager? = null
|
||||
private var fingerprintHelper: FingerprintHelper? = null
|
||||
|
||||
enum class Mode {
|
||||
RequestPIN, RequestPIN2, RequestNewPIN, RequestNewPIN2, ConfirmNewPIN, ConfirmNewPIN2
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_pin_request)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
mode = Mode.valueOf(intent.getStringExtra(Constant.EXTRA_MODE))
|
||||
|
||||
if (mode == Mode.RequestNewPIN)
|
||||
if (PINStorage.haveEncryptedPIN()) {
|
||||
allowFingerprint = true
|
||||
tvPinPrompt.setText(R.string.enter_new_pin_or_use_fingerprint_scanner)
|
||||
} else
|
||||
tvPinPrompt.setText(R.string.enter_new_pin)
|
||||
else if (mode == Mode.ConfirmNewPIN)
|
||||
tvPinPrompt.setText(R.string.confirm_new_pin)
|
||||
else if (mode == Mode.RequestPIN)
|
||||
if (PINStorage.haveEncryptedPIN()) {
|
||||
allowFingerprint = true
|
||||
tvPinPrompt.setText(R.string.enter_pin_or_use_fingerprint_scanner)
|
||||
} else
|
||||
tvPinPrompt.setText(R.string.enter_pin)
|
||||
else if (mode == Mode.RequestNewPIN2)
|
||||
if (PINStorage.haveEncryptedPIN2()) {
|
||||
allowFingerprint = true
|
||||
tvPinPrompt.setText(R.string.enter_new_pin_2_or_use_fingerprint_scanner)
|
||||
} else
|
||||
tvPinPrompt.setText(R.string.enter_new_pin_2)
|
||||
else if (mode == Mode.ConfirmNewPIN2)
|
||||
tvPinPrompt.setText(R.string.confirm_new_pin_2)
|
||||
else if (mode == Mode.RequestPIN2) {
|
||||
val uid = intent.getStringExtra(EXTRA_TANGEM_CARD_UID)
|
||||
val card = TangemCard(uid)
|
||||
card.loadFromBundle(intent.getBundleExtra(EXTRA_TANGEM_CARD))
|
||||
|
||||
if (card.PIN2 == TangemCard.PIN2_Mode.DefaultPIN2 || card.PIN2 == TangemCard.PIN2_Mode.Unchecked) {
|
||||
// if we know PIN2 or not try default previously - use it
|
||||
PINStorage.setPIN2(PINStorage.getDefaultPIN2())
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
if (PINStorage.haveEncryptedPIN2()) {
|
||||
allowFingerprint = true
|
||||
tvPinPrompt.setText(R.string.enter_pin_2_or_use_fingerprint_scanner)
|
||||
} else
|
||||
tvPinPrompt.setText(R.string.enter_pin_2)
|
||||
}
|
||||
|
||||
if (!allowFingerprint)
|
||||
tvPinPrompt.visibility = View.GONE
|
||||
else
|
||||
tvPinPrompt.visibility = View.VISIBLE
|
||||
|
||||
// set listeners
|
||||
btn0.setOnClickListener { buttonClick(btn0) }
|
||||
btn1.setOnClickListener { buttonClick(btn1) }
|
||||
btn2.setOnClickListener { buttonClick(btn2) }
|
||||
btn3.setOnClickListener { buttonClick(btn3) }
|
||||
btn4.setOnClickListener { buttonClick(btn4) }
|
||||
btn5.setOnClickListener { buttonClick(btn5) }
|
||||
btn6.setOnClickListener { buttonClick(btn6) }
|
||||
btn7.setOnClickListener { buttonClick(btn7) }
|
||||
btn8.setOnClickListener { buttonClick(btn8) }
|
||||
btn9.setOnClickListener { buttonClick(btn9) }
|
||||
btnBackspace.setOnClickListener {
|
||||
val s = tvPin!!.text.toString()
|
||||
if (s.isNotEmpty())
|
||||
tvPin!!.text = s.substring(0, s.length - 1)
|
||||
}
|
||||
btnContinue.setOnClickListener { doContinue() }
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
fingerprintHelper?.cancel()
|
||||
|
||||
if (startFingerprintReaderTask != null) {
|
||||
startFingerprintReaderTask!!.cancel(true)
|
||||
startFingerprintReaderTask = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
fingerprintHelper?.cancel()
|
||||
|
||||
if (startFingerprintReaderTask != null) {
|
||||
startFingerprintReaderTask!!.cancel(true)
|
||||
startFingerprintReaderTask = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (allowFingerprint)
|
||||
startFingerprintReader()
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
Log.w(javaClass.name, "Ignore discovered tag!")
|
||||
nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun authenticationFailed(error: String) {
|
||||
LOG.w(TAG, error)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
override fun authenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
|
||||
LOG.i(TAG, "Authentication succeeded!")
|
||||
val cipher = result.cryptoObject.cipher
|
||||
|
||||
if (mode == Mode.RequestNewPIN || mode == Mode.ConfirmNewPIN) {
|
||||
val resultData = Intent()
|
||||
val pin = PINStorage.loadEncryptedPIN(cipher)
|
||||
resultData.putExtra("newPIN", pin)
|
||||
resultData.putExtra("confirmPIN", pin)
|
||||
setResult(Activity.RESULT_OK, resultData)
|
||||
finish()
|
||||
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
|
||||
val resultData = Intent()
|
||||
val pin = PINStorage.loadEncryptedPIN2(cipher)
|
||||
resultData.putExtra("newPIN2", pin)
|
||||
resultData.putExtra("confirmPIN2", pin)
|
||||
setResult(Activity.RESULT_OK, resultData)
|
||||
finish()
|
||||
} else if (mode == Mode.RequestPIN) {
|
||||
PINStorage.loadEncryptedPIN(cipher)
|
||||
setResult(Activity.RESULT_OK)
|
||||
} else if (mode == Mode.RequestPIN2) {
|
||||
PINStorage.loadEncryptedPIN2(cipher)
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun buttonClick(button: Button) {
|
||||
tvPin.text = tvPin.text.toString() + button.text.toString()
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun testFingerPrintSettings(): Boolean {
|
||||
LOG.i(TAG, "Testing Fingerprint Settings")
|
||||
|
||||
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
fingerprintManager = getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager
|
||||
|
||||
if (!keyguardManager.isKeyguardSecure) {
|
||||
LOG.i(TAG, "User hasn't enabled Lock Screen")
|
||||
return false
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
|
||||
LOG.i(TAG, "User hasn't granted permission to use Fingerprint")
|
||||
return false
|
||||
}
|
||||
|
||||
if (!fingerprintManager!!.hasEnrolledFingerprints()) {
|
||||
LOG.i(TAG, "User hasn't registered any fingerprints")
|
||||
return false
|
||||
}
|
||||
|
||||
LOG.i(TAG, "Fingerprint authentication is set.\n")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun startFingerprintReader() {
|
||||
if (!testFingerPrintSettings())
|
||||
return
|
||||
|
||||
if (!allowFingerprint)
|
||||
return
|
||||
|
||||
fingerprintHelper = FingerprintHelper(this@PinRequestActivity)
|
||||
startFingerprintReaderTask = StartFingerprintReaderTask(this, fingerprintManager, fingerprintHelper)
|
||||
startFingerprintReaderTask!!.execute(null as Void?)
|
||||
}
|
||||
|
||||
private fun doContinue() {
|
||||
if (startFingerprintReaderTask != null)
|
||||
return
|
||||
|
||||
// reset errors.
|
||||
tvPin!!.error = null
|
||||
|
||||
// store values at the time of the login attempt.
|
||||
val pin = tvPin!!.text.toString()
|
||||
|
||||
var cancel = false
|
||||
var focusView: View? = null
|
||||
|
||||
if (mode == Mode.ConfirmNewPIN) {
|
||||
if (pin != intent.getStringExtra("newPIN")) {
|
||||
tvPin!!.error = getString(R.string.error_pin_confirmation_failed)
|
||||
focusView = tvPin
|
||||
cancel = true
|
||||
}
|
||||
} else if (mode == Mode.ConfirmNewPIN2) {
|
||||
if (pin != intent.getStringExtra("newPIN2")) {
|
||||
tvPin!!.error = getString(R.string.error_pin_confirmation_failed)
|
||||
focusView = tvPin
|
||||
cancel = true
|
||||
}
|
||||
} else {
|
||||
if (TextUtils.isEmpty(pin)) {
|
||||
tvPin!!.error = getString(R.string.error_empty_pin)
|
||||
focusView = tvPin
|
||||
cancel = true
|
||||
}
|
||||
}
|
||||
|
||||
if (cancel)
|
||||
focusView!!.requestFocus()
|
||||
else {
|
||||
if (mode == Mode.RequestNewPIN || mode == Mode.ConfirmNewPIN) {
|
||||
val resultData = Intent()
|
||||
resultData.putExtra("newPIN", pin)
|
||||
if (mode == Mode.ConfirmNewPIN)
|
||||
resultData.putExtra("confirmPIN", pin)
|
||||
|
||||
setResult(Activity.RESULT_OK, resultData)
|
||||
finish()
|
||||
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
|
||||
val resultData = Intent()
|
||||
resultData.putExtra("newPIN2", pin)
|
||||
if (mode == Mode.ConfirmNewPIN2)
|
||||
resultData.putExtra("confirmPIN2", pin)
|
||||
|
||||
setResult(Activity.RESULT_OK, resultData)
|
||||
finish()
|
||||
} else if (mode == Mode.RequestPIN) {
|
||||
PINStorage.setUserPIN(pin)
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
} else if (mode == Mode.RequestPIN2) {
|
||||
PINStorage.setPIN2(pin)
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
378
app/src/main/java/com/tangem/ui/activity/PinSaveActivity.kt
Normal file
378
app/src/main/java/com/tangem/ui/activity/PinSaveActivity.kt
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Dialog
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException
|
||||
import android.security.keystore.KeyProperties
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.fingerprint.ConfirmWithFingerprintTask
|
||||
import com.tangem.data.fingerprint.FingerprintHelper
|
||||
import com.tangem.cardandroid.android.data.PINStorage
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_pin_save.*
|
||||
import kotlinx.android.synthetic.main.layout_pin_buttons.*
|
||||
import java.io.IOException
|
||||
import java.security.KeyStore
|
||||
import java.security.KeyStoreException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.cert.CertificateException
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.NoSuchPaddingException
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
|
||||
class PinSaveActivity : AppCompatActivity(), FingerprintHelper.FingerprintHelperListener {
|
||||
companion object {
|
||||
val TAG: String = PinSaveActivity::class.java.simpleName
|
||||
|
||||
fun callingIntent(context: Context, hasPin2: Boolean): Intent {
|
||||
val intent = Intent(context, PinSaveActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_PIN2, hasPin2)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
private var confirmWithFingerprintTask: ConfirmWithFingerprintTask? = null
|
||||
private var keyStore: KeyStore? = null
|
||||
private var cipher: Cipher? = null
|
||||
var fingerprintManager: FingerprintManager? = null
|
||||
var cryptoObject: FingerprintManager.CryptoObject? = null
|
||||
var fingerprintHelper: FingerprintHelper? = null
|
||||
private var usePIN2 = false
|
||||
private var onConfirmAction: OnConfirmAction? = null
|
||||
var dFingerPrintConfirmation: Dialog? = null
|
||||
|
||||
private enum class OnConfirmAction {
|
||||
Save, DeleteEncryptedAndSave, Delete
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_pin_save)
|
||||
|
||||
usePIN2 = intent.getBooleanExtra(Constant.EXTRA_PIN2, false)
|
||||
|
||||
if (usePIN2)
|
||||
tvPinPrompt.text = getString(R.string.enter_pin2_and_use_fingerprint_to_save_it)
|
||||
else
|
||||
tvPinPrompt.text = getString(R.string.enter_pin_and_use_fingerprint_to_save_it)
|
||||
|
||||
if (usePIN2) {
|
||||
cbUseFingerprint.isChecked = true
|
||||
cbUseFingerprint.isEnabled = false
|
||||
} else {
|
||||
cbUseFingerprint.isChecked = PINStorage.haveEncryptedPIN()
|
||||
cbUseFingerprint.isEnabled = true
|
||||
}
|
||||
|
||||
// set listeners
|
||||
btn0.setOnClickListener { buttonClick(btn0) }
|
||||
btn1.setOnClickListener { buttonClick(btn1) }
|
||||
btn2.setOnClickListener { buttonClick(btn2) }
|
||||
btn3.setOnClickListener { buttonClick(btn3) }
|
||||
btn4.setOnClickListener { buttonClick(btn4) }
|
||||
btn5.setOnClickListener { buttonClick(btn5) }
|
||||
btn6.setOnClickListener { buttonClick(btn6) }
|
||||
btn7.setOnClickListener { buttonClick(btn7) }
|
||||
btn8.setOnClickListener { buttonClick(btn8) }
|
||||
btn9.setOnClickListener { buttonClick(btn9) }
|
||||
btnBackspace.setOnClickListener {
|
||||
val s = tvPin.text.toString()
|
||||
if (s.isNotEmpty())
|
||||
tvPin.text = s.substring(0, s.length - 1)
|
||||
}
|
||||
btnSavePIN.setOnClickListener { doSavePIN() }
|
||||
btnDeletePIN.setOnClickListener { doDeletePIN() }
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (fingerprintHelper != null)
|
||||
fingerprintHelper!!.cancel()
|
||||
|
||||
if (confirmWithFingerprintTask != null)
|
||||
confirmWithFingerprintTask!!.cancel(true)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (fingerprintHelper != null)
|
||||
fingerprintHelper!!.cancel()
|
||||
|
||||
if (confirmWithFingerprintTask != null)
|
||||
confirmWithFingerprintTask!!.cancel(true)
|
||||
}
|
||||
|
||||
override fun authenticationFailed(error: String) {
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
override fun authenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
|
||||
|
||||
cipher = result.cryptoObject.cipher
|
||||
|
||||
when (onConfirmAction) {
|
||||
PinSaveActivity.OnConfirmAction.Save -> {
|
||||
val textToEncrypt = tvPin.text.toString()
|
||||
if (usePIN2) {
|
||||
PINStorage.saveEncryptedPIN2(cipher, textToEncrypt)
|
||||
} else {
|
||||
PINStorage.saveEncryptedPIN(cipher, textToEncrypt)
|
||||
}
|
||||
}
|
||||
|
||||
PinSaveActivity.OnConfirmAction.Delete -> {
|
||||
if (usePIN2) {
|
||||
PINStorage.deleteEncryptedPIN2()
|
||||
} else {
|
||||
PINStorage.deleteEncryptedPIN()
|
||||
PINStorage.deletePIN()
|
||||
}
|
||||
tvPin.text = ""
|
||||
}
|
||||
|
||||
PinSaveActivity.OnConfirmAction.DeleteEncryptedAndSave -> if (usePIN2) {
|
||||
PINStorage.deleteEncryptedPIN2()
|
||||
PINStorage.saveEncryptedPIN2(cipher, tvPin.text.toString())
|
||||
} else {
|
||||
PINStorage.deleteEncryptedPIN()
|
||||
PINStorage.savePIN(tvPin.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
dFingerPrintConfirmation!!.dismiss()
|
||||
finish()
|
||||
}
|
||||
|
||||
fun getKeyStore(): Boolean {
|
||||
try {
|
||||
keyStore = KeyStore.getInstance(Constant.KEYSTORE)
|
||||
// create empty keystore
|
||||
keyStore!!.load(null)
|
||||
return true
|
||||
} catch (e: KeyStoreException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: CertificateException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
fun createNewKey(forceCreate: Boolean): Boolean {
|
||||
try {
|
||||
if (forceCreate)
|
||||
keyStore!!.deleteEntry(Constant.KEY_ALIAS)
|
||||
|
||||
if (!keyStore!!.containsAlias(Constant.KEY_ALIAS)) {
|
||||
val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, Constant.KEYSTORE)
|
||||
|
||||
generator.init(KeyGenParameterSpec.Builder(Constant.KEY_ALIAS,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||
.setUserAuthenticationRequired(true)
|
||||
.build()
|
||||
)
|
||||
|
||||
generator.generateKey()
|
||||
}
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
fun getCipher(): Boolean {
|
||||
try {
|
||||
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||
return true
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: NoSuchPaddingException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
fun initCipher(mode: Int): Boolean {
|
||||
try {
|
||||
keyStore!!.load(null)
|
||||
val keySpec = keyStore!!.getKey(Constant.KEY_ALIAS, null) as SecretKey
|
||||
|
||||
if (mode == Cipher.ENCRYPT_MODE) {
|
||||
cipher!!.init(mode, keySpec)
|
||||
} else {
|
||||
val iv = PINStorage.loadEncryptedIV()
|
||||
val ivSpec = IvParameterSpec(iv)
|
||||
cipher!!.init(mode, keySpec, ivSpec)
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (e: KeyPermanentlyInvalidatedException) {
|
||||
e.printStackTrace()
|
||||
// retry after clearing entry
|
||||
createNewKey(true)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
fun initCryptObject(): Boolean {
|
||||
try {
|
||||
cryptoObject = FingerprintManager.CryptoObject(cipher!!)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun buttonClick(button: Button) {
|
||||
tvPin.text = String.format("%s%s", tvPin.text, button.text)
|
||||
}
|
||||
|
||||
private fun doSavePIN() {
|
||||
if (confirmWithFingerprintTask != null) {
|
||||
return
|
||||
}
|
||||
|
||||
tvPin.error = null
|
||||
|
||||
val pin = tvPin.text.toString()
|
||||
|
||||
var cancel = false
|
||||
var focusView: View? = null
|
||||
|
||||
if (TextUtils.isEmpty(pin)) {
|
||||
tvPin.error = getString(R.string.error_empty_pin)
|
||||
focusView = tvPin
|
||||
cancel = true
|
||||
}
|
||||
|
||||
if (cancel) {
|
||||
focusView!!.requestFocus()
|
||||
} else {
|
||||
if (usePIN2) {
|
||||
if (!testFingerPrintSettings()) {
|
||||
tvPin.postDelayed({ this.finish() }, 2000)
|
||||
return
|
||||
}
|
||||
|
||||
onConfirmAction = OnConfirmAction.Save
|
||||
|
||||
confirmWithFingerprintTask = ConfirmWithFingerprintTask(this@PinSaveActivity)
|
||||
confirmWithFingerprintTask!!.execute(null as Void?)
|
||||
|
||||
} else {
|
||||
if (cbUseFingerprint.isChecked || PINStorage.haveEncryptedPIN()) {
|
||||
if (!testFingerPrintSettings()) {
|
||||
tvPin.postDelayed({ this.finish() }, 2000)
|
||||
return
|
||||
}
|
||||
|
||||
onConfirmAction = if (cbUseFingerprint.isChecked) {
|
||||
OnConfirmAction.Save
|
||||
} else {
|
||||
OnConfirmAction.DeleteEncryptedAndSave
|
||||
}
|
||||
|
||||
// show a progress spinner, and kick off a background task to perform the user login attempt
|
||||
confirmWithFingerprintTask = ConfirmWithFingerprintTask(this@PinSaveActivity)
|
||||
confirmWithFingerprintTask!!.execute(null as Void?)
|
||||
} else {
|
||||
PINStorage.savePIN(tvPin.text.toString())
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doDeletePIN() {
|
||||
if (usePIN2) {
|
||||
if (PINStorage.haveEncryptedPIN2()) {
|
||||
if (!testFingerPrintSettings()) {
|
||||
tvPin.postDelayed({ this.finish() }, 2000)
|
||||
return
|
||||
}
|
||||
onConfirmAction = OnConfirmAction.Delete
|
||||
confirmWithFingerprintTask = ConfirmWithFingerprintTask(this@PinSaveActivity)
|
||||
confirmWithFingerprintTask!!.execute(null as Void?)
|
||||
} else {
|
||||
tvPin.text = ""
|
||||
finish()
|
||||
}
|
||||
} else {
|
||||
if (cbUseFingerprint.isChecked || PINStorage.haveEncryptedPIN()) {
|
||||
if (!testFingerPrintSettings()) {
|
||||
tvPin.postDelayed({ this.finish() }, 2000)
|
||||
return
|
||||
}
|
||||
onConfirmAction = OnConfirmAction.Delete
|
||||
confirmWithFingerprintTask = ConfirmWithFingerprintTask(this@PinSaveActivity)
|
||||
confirmWithFingerprintTask!!.execute(null as Void?)
|
||||
} else {
|
||||
tvPin.text = ""
|
||||
PINStorage.deletePIN()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun testFingerPrintSettings(): Boolean {
|
||||
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
fingerprintManager = getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager
|
||||
|
||||
if (!keyguardManager.isKeyguardSecure) {
|
||||
Toast.makeText(baseContext, R.string.user_has_not_enabled_lock_screen, Toast.LENGTH_LONG).show()
|
||||
return false
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(baseContext, R.string.user_has_not_granted_permission_to_use_fingerprint, Toast.LENGTH_LONG).show()
|
||||
return false
|
||||
}
|
||||
|
||||
if (!fingerprintManager!!.hasEnrolledFingerprints()) {
|
||||
Toast.makeText(baseContext, R.string.user_has_not_registered_any_fingerprints, Toast.LENGTH_LONG).show()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
205
app/src/main/java/com/tangem/ui/activity/PinSwapActivity.kt
Normal file
205
app/src/main/java/com/tangem/ui/activity/PinSwapActivity.kt
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.*
|
||||
import com.tangem.cardcommon.data.TangemCard
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.SwapPINTask
|
||||
import com.tangem.cardcommon.util.Util
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_pin_swap.*
|
||||
|
||||
class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
companion object {
|
||||
val TAG: String = PinSwapActivity::class.java.simpleName
|
||||
|
||||
fun callingIntent(context: Context, newPIN: String, newPIN2: String): Intent {
|
||||
val intent = Intent(context, PinSwapActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_NEW_PIN, newPIN)
|
||||
intent.putExtra(Constant.EXTRA_NEW_PIN_2, newPIN2)
|
||||
return intent
|
||||
}
|
||||
|
||||
const val RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER
|
||||
}
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
|
||||
private var card: TangemCard? = null
|
||||
private var newPIN: String? = null
|
||||
private var newPIN2: String? = null
|
||||
|
||||
private var progressBar: ProgressBar? = null
|
||||
|
||||
private var swapPinTask: SwapPINTask? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_pin_swap)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
card = TangemCard(intent.getStringExtra(EXTRA_TANGEM_CARD_UID))
|
||||
card!!.loadFromBundle(intent.extras!!.getBundle(EXTRA_TANGEM_CARD))
|
||||
|
||||
newPIN = intent.getStringExtra(Constant.EXTRA_NEW_PIN)
|
||||
newPIN2 = intent.getStringExtra(Constant.EXTRA_NEW_PIN_2)
|
||||
|
||||
tvCardID.text = card!!.cidDescription
|
||||
|
||||
progressBar = findViewById(R.id.progressBar)
|
||||
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar!!.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
LOG.d(TAG, "UID: $sUID")
|
||||
|
||||
if (sUID == card!!.uid) {
|
||||
isoDep.timeout = card!!.pauseBeforePIN2 + 65000
|
||||
swapPinTask = SwapPINTask(card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, newPIN, newPIN2)
|
||||
swapPinTask!!.start()
|
||||
} else {
|
||||
LOG.d(TAG, "Mismatch card UID (" + sUID + " instead of " + card!!.uid + ")")
|
||||
nfcManager.ignoreTag(isoDep.tag)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
swapPinTask?.cancel(true)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
public override fun onStop() {
|
||||
swapPinTask?.cancel(true)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
progressBar?.post {
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar?.progress = 5
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
swapPinTask = null
|
||||
|
||||
if (cardProtocol != null) {
|
||||
when {
|
||||
cardProtocol.error == null -> progressBar!!.post {
|
||||
progressBar!!.progress = 100
|
||||
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
|
||||
val intent = Intent()
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("Card", cardProtocol.card.asBundle)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
}
|
||||
cardProtocol.error is CardProtocol.TangemException_InvalidPIN -> {
|
||||
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("message", "Cannot change PIN(s). Make sure you enter correct PIN2!")
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("Card", cardProtocol.card.asBundle)
|
||||
setResult(RESULT_INVALID_PIN, intent)
|
||||
finish()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
else -> progressBar!!.post {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
|
||||
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
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
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar!!.post { progressBar!!.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
swapPinTask = 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 onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(this, msec)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.data.network.CryptonitOtherApi
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_prepare_cryptonit_other_api_withdrawal.*
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class PrepareCryptonitOtherApiWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
||||
|
||||
companion object {
|
||||
val TAG: String = PrepareCryptonitOtherApiWithdrawalActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var ctx: TangemContext
|
||||
|
||||
private var cryptonit: CryptonitOtherApi? = null
|
||||
|
||||
@Inject
|
||||
internal lateinit var navigator: Navigator
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_prepare_cryptonit_other_api_withdrawal)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
cryptonit = CryptonitOtherApi(this)
|
||||
|
||||
tvKey.text = cryptonit!!.key
|
||||
tvUserID.text = cryptonit!!.userId
|
||||
tvSecret.text = cryptonit!!.secretDescription
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
tvWallet.text = ctx.coinData!!.wallet
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
tvCurrency.text = engine!!.balanceCurrency
|
||||
|
||||
etAmount.setText(engine.convertToAmount(engine.convertToInternalAmount(ctx.card!!.denomination)).toValueString())
|
||||
etAmount.filters = engine.amountInputFilters
|
||||
|
||||
// set listeners
|
||||
btnLoad.setOnClickListener {
|
||||
try {
|
||||
val strAmount: String = etAmount.text.toString().replace(",", ".")
|
||||
// if (!engine.checkAmount(card, strAmount))
|
||||
// etAmount.error = getString(R.string.unknown_amount_format)
|
||||
val dblAmount: Double = strAmount.toDouble()
|
||||
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.cryptonit_request_withdrawal)
|
||||
|
||||
cryptonit!!.requestCryptoWithdrawal(ctx.blockchain.currency, dblAmount.toString(), ctx.coinData!!.wallet)
|
||||
} catch (e: Exception) {
|
||||
etAmount.error = getString(R.string.unknown_amount_format)
|
||||
}
|
||||
|
||||
//Toast.makeText(this, strAmount, Toast.LENGTH_LONG).show()
|
||||
// val balance = engine.getBalanceLong(card)!! / (card!!.blockchain.multiplier / 1000.0)
|
||||
// if (etAmount.text.toString().replace(",", ".").toDouble() > balance) {
|
||||
// etAmount.error = getString(R.string.not_enough_funds_on_your_account)
|
||||
// return@setOnClickListener
|
||||
// }
|
||||
|
||||
}
|
||||
ivCameraKey.setOnClickListener { navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR_KEY) }
|
||||
|
||||
ivCameraSecret.setOnClickListener { navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR_SECRET) }
|
||||
|
||||
ivCameraUserId.setOnClickListener { navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR_USER_ID) }
|
||||
|
||||
ivRefreshBalance.setOnClickListener { doRequestBalance() }
|
||||
|
||||
cryptonit!!.setBalanceListener { response ->
|
||||
when (ctx.blockchain) {
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestNet -> {
|
||||
tvBalance.text = response.eth_available
|
||||
}
|
||||
Blockchain.Bitcoin, Blockchain.BitcoinTestNet, Blockchain.BitcoinCash -> {
|
||||
tvBalance.text = response.btc_available
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
tvBalanceCurrency.text = ctx.blockchain.currency
|
||||
tvBalance.setTextColor(Color.BLACK)
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
btnLoad.isActivated = true
|
||||
}
|
||||
cryptonit!!.setErrorListener { throwable ->
|
||||
throwable.printStackTrace()
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = throwable.message
|
||||
}
|
||||
cryptonit!!.setWithdrawalListener { response ->
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
if (response.success != null && response.success!!) finish()
|
||||
else {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = response.reason!!.toString()
|
||||
}
|
||||
}
|
||||
btnLoad.isActivated = false
|
||||
doRequestBalance()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && data != null && data.extras!!.containsKey("QRCode")) {
|
||||
when (requestCode) {
|
||||
Constant.REQUEST_CODE_SCAN_QR_KEY -> {
|
||||
cryptonit!!.key = data.getStringExtra("QRCode")
|
||||
tvKey!!.text = cryptonit!!.key
|
||||
}
|
||||
Constant.REQUEST_CODE_SCAN_QR_SECRET -> {
|
||||
cryptonit!!.secret = data.getStringExtra("QRCode")
|
||||
tvSecret!!.text = cryptonit!!.secretDescription
|
||||
}
|
||||
Constant.REQUEST_CODE_SCAN_QR_USER_ID -> {
|
||||
cryptonit!!.userId = data.getStringExtra("QRCode")
|
||||
tvUserID!!.text = cryptonit!!.userId
|
||||
}
|
||||
}
|
||||
cryptonit!!.saveAccountInfo()
|
||||
doRequestBalance()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doRequestBalance() {
|
||||
if (cryptonit!!.havaAccountInfo()) {
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.cryptonit_request_balance)
|
||||
tvError.visibility = View.INVISIBLE
|
||||
cryptonit!!.requestBalance(ctx.blockchain.currency, "USD")
|
||||
} else {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = getString(R.string.cryptonit_not_enough_account_data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.text.InputFilter
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.network.Cryptonit
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.util.DecimalDigitsInputFilter
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_prepare_cryptonit_withdrawal.*
|
||||
import java.io.IOException
|
||||
|
||||
class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
||||
|
||||
companion object {
|
||||
val TAG: String = PrepareCryptonitWithdrawalActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var nfcManager: NfcManager
|
||||
|
||||
private var cryptonit: Cryptonit? = null
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_prepare_cryptonit_withdrawal)
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
cryptonit = Cryptonit(this)
|
||||
|
||||
etUsername.setText(cryptonit!!.username)
|
||||
etPassword.setText(cryptonit!!.password)
|
||||
etFee.setText(cryptonit!!.fee)
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
tvWallet.text = ctx.coinData!!.wallet
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
tvCurrency.text = engine!!.balanceCurrency
|
||||
tvFeeCurrency.text = engine.feeCurrency
|
||||
|
||||
etAmount.setText(engine.convertToAmount(engine.convertToInternalAmount(ctx.card!!.denomination)).toValueString())
|
||||
etAmount.filters = engine.amountInputFilters
|
||||
|
||||
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)
|
||||
true
|
||||
} else
|
||||
false
|
||||
}
|
||||
|
||||
when (ctx.blockchain) {
|
||||
Blockchain.Bitcoin -> {
|
||||
etAmount.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(5))
|
||||
etFee.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(5))
|
||||
}
|
||||
Blockchain.BitcoinCash -> {
|
||||
etAmount.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(8))
|
||||
etFee.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(8))
|
||||
}
|
||||
Blockchain.Ethereum -> {
|
||||
etAmount.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(18))
|
||||
etFee.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(18))
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
// set listeners
|
||||
btnLoad.setOnClickListener {
|
||||
try {
|
||||
val strAmount: String = etAmount.text.toString().replace(",", ".")
|
||||
val strFee: String = etFee.text.toString().replace(",", ".")
|
||||
val dblAmount: Double = strAmount.toDouble()
|
||||
cryptonit!!.fee = strFee
|
||||
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.cryptonit_request_withdrawal)
|
||||
|
||||
cryptonit!!.requestWithdrawCoins(ctx.blockchain.currency, dblAmount, ctx.coinData!!.wallet)
|
||||
} catch (e: Exception) {
|
||||
etAmount.error = getString(R.string.unknown_amount_format)
|
||||
}
|
||||
}
|
||||
|
||||
ivRefreshBalance.setOnClickListener {
|
||||
cryptonit!!.username = etUsername.text.toString()
|
||||
cryptonit!!.password = etPassword.text.toString()
|
||||
cryptonit!!.saveAccountInfo()
|
||||
doRequestBalance()
|
||||
}
|
||||
|
||||
cryptonit!!.setBalanceListener { response ->
|
||||
tvBalance.text = response.results[0].balance.toString()
|
||||
tvBalanceCurrency.text = response.results[0].currency // card!!.blockchain.currency
|
||||
tvBalance.setTextColor(Color.BLACK)
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
btnLoad.visibility = View.VISIBLE
|
||||
}
|
||||
cryptonit!!.setErrorListener { throwable ->
|
||||
throwable.printStackTrace()
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = throwable.message
|
||||
}
|
||||
cryptonit!!.setWithdrawalListener { response ->
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
if (response.success != null && response.success!!) {
|
||||
Toast.makeText(this, R.string.withdrawal_successful, Toast.LENGTH_LONG).show();
|
||||
finish()
|
||||
} else {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = response.errors.toString()
|
||||
}
|
||||
}
|
||||
btnLoad.visibility = View.INVISIBLE
|
||||
doRequestBalance()
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doRequestBalance() {
|
||||
if (cryptonit!!.haveAccountInfo()) {
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.cryptonit_request_balance)
|
||||
tvError.visibility = View.INVISIBLE
|
||||
cryptonit!!.requestBalance(ctx.blockchain.currency)
|
||||
} else {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = getString(R.string.cryptonit_not_enough_account_data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.network.Kraken
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_prepare_kraken_withdrawal.*
|
||||
import java.io.IOException
|
||||
import java.math.BigDecimal
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
||||
|
||||
companion object {
|
||||
val TAG: String = PrepareKrakenWithdrawalActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var nfcManager: NfcManager
|
||||
|
||||
private var kraken: Kraken? = null
|
||||
private var fee: BigDecimal? = null
|
||||
|
||||
@Inject
|
||||
internal lateinit var navigator: Navigator
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_prepare_kraken_withdrawal)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
kraken = Kraken(this)
|
||||
|
||||
tvKey.text = kraken!!.key
|
||||
tvSecret.text = kraken!!.secretDescription
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
tvWallet.text = ctx.coinData!!.wallet
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
tvCurrency.text = engine!!.balanceCurrency
|
||||
|
||||
etAmount.setText(engine.convertToAmount(engine.convertToInternalAmount(ctx.card!!.denomination)).toValueString())
|
||||
etAmount.filters = engine.amountInputFilters
|
||||
|
||||
etAmount.setOnEditorActionListener { lv, actionId, event ->
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// set listeners
|
||||
btnLoad.setOnClickListener {
|
||||
try {
|
||||
val strAmount: String = etAmount.text.toString().replace(",", ".")
|
||||
|
||||
var dblAmount: Double = strAmount.toDouble()
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.kraken_request_withdrawal)
|
||||
|
||||
kraken!!.requestWithdrawInfo(ctx.blockchain.currency, dblAmount.toString(), ctx.coinData!!.wallet)
|
||||
} catch (e: Exception) {
|
||||
etAmount.error = getString(R.string.unknown_amount_format)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ivCamera.setOnClickListener { navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR) }
|
||||
|
||||
ivRefreshBalance.setOnClickListener { doRequestBalance() }
|
||||
|
||||
kraken!!.setBalanceListener { response ->
|
||||
if (response.error != null && response.error.isNotEmpty()) {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = Arrays.toString(response.error)
|
||||
} else {
|
||||
when (ctx.blockchain) {
|
||||
Blockchain.Ethereum -> {
|
||||
tvBalance.text = response.result.XETH.trimEnd('0')
|
||||
}
|
||||
Blockchain.Bitcoin -> {
|
||||
tvBalance.text = response.result.XXBT.trimEnd('0')
|
||||
}
|
||||
Blockchain.BitcoinCash -> {
|
||||
tvBalance.text = response.result.BCH.trimEnd('0')
|
||||
}
|
||||
else -> {
|
||||
tvBalance.text = "???"
|
||||
}
|
||||
}
|
||||
tvBalanceCurrency.text = ctx.blockchain.currency
|
||||
tvBalance.setTextColor(Color.BLACK)
|
||||
btnLoad.visibility = View.VISIBLE
|
||||
}
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
}
|
||||
kraken!!.setErrorListener { throwable ->
|
||||
throwable.printStackTrace()
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = throwable.message
|
||||
}
|
||||
kraken!!.setWithdrawalListener { response ->
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
if (response.error != null && response.error.isNotEmpty()) {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = Arrays.toString(response.error)
|
||||
} else {
|
||||
Toast.makeText(this, "Withdrawal successful!", Toast.LENGTH_LONG).show()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
kraken!!.setWithdrawalInfoListener { response ->
|
||||
rlProgressBar.visibility = View.INVISIBLE
|
||||
if (response.error != null && response.error.isNotEmpty()) {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = Arrays.toString(response.error)
|
||||
} else {
|
||||
fee = BigDecimal(response.result.fee)
|
||||
showConfirmDialog()
|
||||
}
|
||||
}
|
||||
|
||||
btnLoad.visibility = View.INVISIBLE
|
||||
doRequestBalance()
|
||||
}
|
||||
|
||||
// Method to show an alert dialog with yes, no and cancel button
|
||||
private fun showConfirmDialog() {
|
||||
// Late initialize an alert dialog object
|
||||
lateinit var dialog: AlertDialog
|
||||
|
||||
|
||||
// Initialize a new instance of alert dialog builder object
|
||||
val builder = AlertDialog.Builder(this)
|
||||
|
||||
// Set a title for alert dialog
|
||||
builder.setTitle(R.string.please_confirm_withdraw)
|
||||
|
||||
// Set a message for alert dialog
|
||||
builder.setMessage(String.format("Continue with fee %s %s?", fee!!.toString().trimEnd('0'), ctx.blockchain.currency))
|
||||
|
||||
// On click listener for dialog buttons
|
||||
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
|
||||
when (which) {
|
||||
DialogInterface.BUTTON_POSITIVE -> {
|
||||
try {
|
||||
val strAmount: String = etAmount.text.toString().replace(",", ".")
|
||||
|
||||
var dblAmount: Double = strAmount.toDouble()
|
||||
|
||||
dblAmount += fee!!.toDouble()
|
||||
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.kraken_request_withdrawal)
|
||||
|
||||
kraken!!.requestWithdraw(ctx.blockchain.currency, dblAmount.toString(), ctx.coinData!!.wallet)
|
||||
} catch (e: Exception) {
|
||||
etAmount.error = getString(R.string.unknown_amount_format)
|
||||
}
|
||||
}
|
||||
DialogInterface.BUTTON_NEGATIVE -> {
|
||||
Toast.makeText(this, R.string.operation_canceled, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the alert dialog positive/yes button
|
||||
builder.setPositiveButton(R.string.yes, dialogClickListener)
|
||||
|
||||
// Set the alert dialog negative/no button
|
||||
builder.setNegativeButton(R.string.no, dialogClickListener)
|
||||
|
||||
|
||||
// Initialize the AlertDialog using builder object
|
||||
dialog = builder.create()
|
||||
|
||||
// Finally, display the alert dialog
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun doRequestBalance() {
|
||||
if (kraken!!.haveAccountInfo()) {
|
||||
rlProgressBar.visibility = View.VISIBLE
|
||||
tvProgressDescription.text = getString(R.string.kraken_request_balance)
|
||||
tvError.visibility = View.INVISIBLE
|
||||
kraken!!.requestBalance()
|
||||
} else {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = getString(R.string.kraken_not_enough_account_data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && data != null && data.extras!!.containsKey("QRCode")) {
|
||||
when (requestCode) {
|
||||
Constant.REQUEST_CODE_SCAN_QR -> {
|
||||
val uri = URI(data.getStringExtra("QRCode"))
|
||||
val query = uri.query
|
||||
val params = query.split("&")
|
||||
for (param in params) {
|
||||
if (param.startsWith("key=")) kraken!!.key = param.substring(4)
|
||||
else if (param.startsWith("secret=")) kraken!!.secret = param.substring(7)
|
||||
}
|
||||
tvKey!!.text = kraken!!.key
|
||||
tvSecret!!.text = kraken!!.secretDescription
|
||||
}
|
||||
}
|
||||
kraken!!.saveAccountInfo()
|
||||
doRequestBalance()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
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.View
|
||||
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.data.Blockchain
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_prepare_transaction.*
|
||||
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
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var nfcManager: NfcManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_prepare_transaction)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
tvCardID.text = ctx.card?.cidDescription
|
||||
val engine = CoinEngineFactory.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
|
||||
|
||||
if (!engine.allowSelectFeeInclusion()) {
|
||||
rgIncFee.visibility = View.INVISIBLE
|
||||
} else {
|
||||
rgIncFee.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (ctx.card!!.remainingSignatures < 2)
|
||||
etAmount.isEnabled = false
|
||||
|
||||
tvCurrency.text = engine.balance.currency
|
||||
etAmount.setText(engine.balance.toValueString())
|
||||
|
||||
// limit number of symbols after comma
|
||||
etAmount.filters = engine.amountInputFilters
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
val engine1 = CoinEngineFactory.create(ctx)
|
||||
val strAmount: String = etAmount.text.toString().replace(",", ".")
|
||||
val amount = engine1!!.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
|
||||
|
||||
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 (!engine1.validateAddress(etWallet.text.toString())) {
|
||||
etWallet.error = getString(R.string.incorrect_destination_wallet_address)
|
||||
return@setOnClickListener
|
||||
} else
|
||||
etWallet.error = null
|
||||
|
||||
if (etWallet.text.toString() == ctx.coinData!!.wallet) {
|
||||
etWallet.error = getString(R.string.destination_wallet_address_equal_source_address)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (!etAmount.error.isNullOrEmpty() || !etWallet.error.isNullOrEmpty()) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val intent = Intent(baseContext, ConfirmTransactionActivity::class.java)
|
||||
ctx.saveToIntent(intent)
|
||||
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__)
|
||||
}
|
||||
|
||||
ivCamera.setOnClickListener { navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR) }
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
239
app/src/main/java/com/tangem/ui/activity/PurgeActivity.kt
Normal file
239
app/src/main/java/com/tangem/ui/activity/PurgeActivity.kt
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.tangem.App
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialogNew
|
||||
import com.tangem.ui.event.DeletingWalletFinish
|
||||
import com.tangem.cardandroid.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.asBundle
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.PurgeTask
|
||||
import com.tangem.cardcommon.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.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import javax.inject.Inject
|
||||
|
||||
class PurgeActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
companion object {
|
||||
val TAG: String = PurgeActivity::class.java.simpleName
|
||||
|
||||
fun callingIntent(context: Context, ctx: TangemContext): Intent {
|
||||
val intent = Intent(context, PurgeActivity::class.java)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
|
||||
const val RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER
|
||||
}
|
||||
|
||||
@Inject
|
||||
internal lateinit var waitSecurityDelayDialogNew: WaitSecurityDelayDialogNew
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var ctx: TangemContext
|
||||
|
||||
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
|
||||
|
||||
private var purgeTask: PurgeTask? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_purge)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
public override fun onStop() {
|
||||
purgeTask?.cancel(true)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
LOG.d(TAG, "UID: $sUID")
|
||||
|
||||
if (sUID == ctx.card!!.uid) {
|
||||
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 65000
|
||||
purgeTask = PurgeTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
|
||||
purgeTask!!.start()
|
||||
} else {
|
||||
LOG.d(TAG, "Mismatch card UID (" + sUID + " instead of " + ctx.card.uid + ")")
|
||||
nfcManager.ignoreTag(isoDep.tag)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(this, msec)
|
||||
|
||||
// val readWait = ReadWait()
|
||||
// readWait.msec = msec
|
||||
// EventBus.getDefault().post(readWait)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
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() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(this)
|
||||
|
||||
// val readAfterRequest = ReadAfterRequest()
|
||||
// EventBus.getDefault().post(readAfterRequest)
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
|
||||
progressBar.post {
|
||||
progressBar.visibility = View.VISIBLE
|
||||
progressBar.progress = 5
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
purgeTask = 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)
|
||||
|
||||
val intent = Intent()
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("Card", cardProtocol.card.asBundle)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
|
||||
EventBus.getDefault().post(DeletingWalletFinish())
|
||||
|
||||
finish()
|
||||
}
|
||||
} else {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
|
||||
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("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("Card", cardProtocol.card.asBundle)
|
||||
intent.putExtra("message", getString(R.string.cannot_erase_wallet))
|
||||
setResult(RESULT_INVALID_PIN, intent)
|
||||
finish()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
} else {
|
||||
progressBar!!.post {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed)
|
||||
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 onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar?.post { progressBar?.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
purgeTask = null
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
}
|
||||
72
app/src/main/java/com/tangem/ui/activity/QrScanActivity.kt
Normal file
72
app/src/main/java/com/tangem/ui/activity/QrScanActivity.kt
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.zxing.Result
|
||||
import com.tangem.Constant
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||
|
||||
class QrScanActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
|
||||
companion object {
|
||||
fun callingIntent(context: Context): Intent {
|
||||
return Intent(context, QrScanActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private var scannerView: ZXingScannerView? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
|
||||
else
|
||||
runScanner()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
scannerView?.stopCamera()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
scannerView?.startCamera()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
when (requestCode) {
|
||||
1 -> {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)
|
||||
runScanner()
|
||||
else {
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleResult(result: Result) {
|
||||
val data = Intent()
|
||||
data.putExtra(Constant.EXTRA_QR_CODE, result.text)
|
||||
setResult(Activity.RESULT_OK, data)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun runScanner() {
|
||||
// programmatically initialize the scanner view
|
||||
scannerView = ZXingScannerView(this)
|
||||
setContentView(scannerView)
|
||||
|
||||
// register ourselves as a handler for scan results.
|
||||
scannerView?.setResultHandler(this)
|
||||
scannerView?.startCamera()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.KeyEvent
|
||||
import android.widget.Toast
|
||||
import com.tangem.Constant
|
||||
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.ui.event.TransactionFinishWithSuccess
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.R
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.IOException
|
||||
|
||||
class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var ctx: TangemContext
|
||||
|
||||
private var tx: ByteArray? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_send_transaction)
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
ctx = TangemContext.loadFromBundle(this, intent.extras)
|
||||
tx = intent.getByteArrayExtra(Constant.EXTRA_TX)
|
||||
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
engine!!.requestSendTransaction(
|
||||
object : CoinEngine.BlockchainRequestsCallbacks {
|
||||
override fun onComplete(success: Boolean) {
|
||||
if (success)
|
||||
finishWithSuccess()
|
||||
else
|
||||
finishWithError(ctx.error)
|
||||
}
|
||||
|
||||
override fun onProgress() {
|
||||
}
|
||||
|
||||
override fun allowAdvance(): Boolean {
|
||||
return UtilHelper.isOnline(this@SendTransactionActivity)
|
||||
}
|
||||
},
|
||||
tx
|
||||
)
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_BACK -> {
|
||||
Toast.makeText(this, R.string.please_wait, Toast.LENGTH_LONG).show()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
nfcManager.ignoreTag(tag)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishWithSuccess() {
|
||||
val transactionFinishWithSuccess = TransactionFinishWithSuccess()
|
||||
transactionFinishWithSuccess.message = getString(R.string.transaction_has_been_successfully_signed)
|
||||
EventBus.getDefault().post(transactionFinishWithSuccess)
|
||||
|
||||
val intent = Intent()
|
||||
intent.putExtra(Constant.EXTRA_MESSAGE, getString(R.string.transaction_has_been_successfully_signed))
|
||||
setResult(RESULT_OK, intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun finishWithError(message: String) {
|
||||
val transactionFinishWithError = TransactionFinishWithError()
|
||||
transactionFinishWithError.message = String.format(getString(R.string.try_again_failed_to_send_transaction), message)
|
||||
EventBus.getDefault().post(transactionFinishWithError)
|
||||
|
||||
val intent = Intent()
|
||||
intent.putExtra(Constant.EXTRA_MESSAGE, String.format(getString(R.string.try_again_failed_to_send_transaction), message))
|
||||
setResult(RESULT_CANCELED, intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
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.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.cardandroid.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.asBundle
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.SignTask
|
||||
import com.tangem.cardcommon.util.Util
|
||||
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.*
|
||||
|
||||
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)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
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)
|
||||
?: throw CardProtocol.TangemException("Can't create CoinEngine!")
|
||||
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("message", getString(R.string.cannot_sign_transaction_wrong_amount))
|
||||
intent.putExtra("UID", ctx.card.uid)
|
||||
intent.putExtra("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("message", getString(R.string.cannot_sign_transaction_make_sure_you_enter_correct_pin_2))
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("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("message", getString(R.string.cannot_sign_transaction_wrong_amount))
|
||||
intent.putExtra("UID", cardProtocol.card.uid)
|
||||
intent.putExtra("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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.ui.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
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.ui.fragment.VerifyCard
|
||||
import com.tangem.wallet.R
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerifyCardActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var navigator: Navigator
|
||||
|
||||
companion object {
|
||||
fun callingIntent(context: Context, ctx: TangemContext): Intent {
|
||||
val intent = Intent(context, VerifyCardActivity::class.java)
|
||||
ctx.saveToIntent(intent)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_verify_card)
|
||||
|
||||
App.navigatorComponent?.inject(this)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
super.onBackPressed()
|
||||
val verifyCard = supportFragmentManager.findFragmentById(R.id.verify_card_fragment) as VerifyCard
|
||||
val data = verifyCard.prepareResultIntent()
|
||||
data.putExtra(Constant.EXTRA_MODIFICATION, Constant.EXTRA_MODIFICATION_UPDATE)
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.ui.dialog
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
||||
import com.tangem.wallet.R
|
||||
|
||||
class NoExtendedLengthSupportDialog : DialogFragment() {
|
||||
|
||||
companion object {
|
||||
val TAG: String = NoExtendedLengthSupportDialog::class.java.simpleName
|
||||
var allReadyShowed = false
|
||||
var message = ""// = R.string.the_nfc_adapter_length_apdu
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return AlertDialog.Builder(activity)
|
||||
.setIcon(R.drawable.tangem_logo_small_new)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.got_it) { _, _ -> NoExtendedLengthSupportDialog.allReadyShowed = false }
|
||||
.create()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
public class PINSwapWarningDialog extends DialogFragment {
|
||||
public static final String TAG = PINSwapWarningDialog.class.getSimpleName();
|
||||
|
||||
public static final String EXTRA_MESSAGE = "message";
|
||||
private String message;
|
||||
private OnPositiveButton mOnPositiveButton;
|
||||
|
||||
public interface OnPositiveButton {
|
||||
void onRefresh();
|
||||
}
|
||||
|
||||
public void setOnRefreshPage(OnPositiveButton listener) {
|
||||
mOnPositiveButton = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
message = getArguments().getString(EXTRA_MESSAGE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(R.drawable.tangem_logo_small_new)
|
||||
.setTitle(R.string.your_money_is_at_risk)
|
||||
.setMessage(message)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> dismiss())
|
||||
.setPositiveButton(R.string.contin, (dialog, whichButton) -> mOnPositiveButton.onRefresh())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
|
||||
}
|
||||
25
app/src/main/java/com/tangem/ui/dialog/RootFoundDialog.kt
Normal file
25
app/src/main/java/com/tangem/ui/dialog/RootFoundDialog.kt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.ui.dialog
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
||||
import com.tangem.wallet.R
|
||||
|
||||
class RootFoundDialog : DialogFragment() {
|
||||
|
||||
companion object {
|
||||
val TAG: String = RootFoundDialog::class.java.simpleName
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return AlertDialog.Builder(activity)
|
||||
.setIcon(R.drawable.tangem_logo_small_new)
|
||||
.setTitle(R.string.device_is_rooted)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.got_it, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
}
|
||||
78
app/src/main/java/com/tangem/ui/dialog/ShowQRCodeDialog.java
Normal file
78
app/src/main/java/com/tangem/ui/dialog/ShowQRCodeDialog.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.tangem.util.UtilHelper;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
/**
|
||||
* Created by dvol on 06.03.2018.
|
||||
*/
|
||||
public class ShowQRCodeDialog extends DialogFragment {
|
||||
private ImageView ivQR;
|
||||
private TextView tvQRaddress;
|
||||
private Bitmap bmQR;
|
||||
private String addr;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
|
||||
// Inflate and set the layout for the dialog
|
||||
// Pass null as the parent view because its going in the dialog layout
|
||||
View v = inflater.inflate(R.layout.dialog_show_qr, null);
|
||||
ivQR = v.findViewById(R.id.ivQR);
|
||||
ivQR.setImageBitmap(bmQR);
|
||||
tvQRaddress = v.findViewById(R.id.tvQRaddress);
|
||||
tvQRaddress.setText(addr);
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(R.drawable.tangem_logo_small_new)
|
||||
.setTitle(R.string.show_wallet_qr_code)
|
||||
.setView(v)
|
||||
.setPositiveButton(R.string.ok, (dialog,which)->dismiss() )
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
|
||||
public void setup(String content) {
|
||||
try {
|
||||
bmQR = UtilHelper.INSTANCE.generateQrCode(content);
|
||||
addr = content;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void show(final AppCompatActivity activity, final String content) {
|
||||
activity.runOnUiThread(() -> {
|
||||
ShowQRCodeDialog instance = new ShowQRCodeDialog();
|
||||
instance.setup(content);
|
||||
instance.show(activity.getSupportFragmentManager(), "ShowQRCodeDialog");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
package com.tangem.ui.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
/**
|
||||
* Created by dvol on 06.03.2018.
|
||||
*/
|
||||
public class WaitSecurityDelayDialog extends DialogFragment {
|
||||
public static final String TAG = WaitSecurityDelayDialog.class.getSimpleName();
|
||||
|
||||
private ProgressBar progressBar;
|
||||
private int msTimeout = 60000, msProgress = 0;
|
||||
private Timer timer;
|
||||
|
||||
private static Timer timerToShowDelayDialog = null;
|
||||
private static WaitSecurityDelayDialog instance = null;
|
||||
|
||||
private final static int minRemainingDelayToShowDialog = 1000;
|
||||
private final static int delayBeforeShowDialog = 5000;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
|
||||
View v = inflater.inflate(R.layout.dialog_wait_pin2, null);
|
||||
|
||||
progressBar = v.findViewById(R.id.progressBar);
|
||||
progressBar.setMax(msTimeout);
|
||||
progressBar.setProgress(msProgress);
|
||||
|
||||
timer = new Timer();
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressBar.post(() -> {
|
||||
int progress = WaitSecurityDelayDialog.this.progressBar.getProgress();
|
||||
if (progress < WaitSecurityDelayDialog.this.progressBar.getMax()) {
|
||||
WaitSecurityDelayDialog.this.progressBar.setProgress(progress + 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000, 1000);
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(R.drawable.tangem_logo_small_new)
|
||||
.setTitle(R.string.security_delay)
|
||||
.setView(v)
|
||||
.setCancelable(false)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(@NonNull DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
|
||||
public static void onReadBeforeRequest(final AppCompatActivity activity, final int timeout) {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (timerToShowDelayDialog != null || timeout < delayBeforeShowDialog + minRemainingDelayToShowDialog)
|
||||
return;
|
||||
timerToShowDelayDialog = new Timer();
|
||||
timerToShowDelayDialog.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (WaitSecurityDelayDialog.instance != null) return;
|
||||
instance = new WaitSecurityDelayDialog();
|
||||
instance.setup(timeout, delayBeforeShowDialog);
|
||||
instance.setCancelable(false);
|
||||
instance.show(activity.getSupportFragmentManager(), TAG);
|
||||
}
|
||||
}, delayBeforeShowDialog);
|
||||
});
|
||||
}
|
||||
|
||||
public static void onReadAfterRequest(final Activity activity) {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (timerToShowDelayDialog == null) return;
|
||||
timerToShowDelayDialog.cancel();
|
||||
timerToShowDelayDialog = null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void onReadWait(final AppCompatActivity activity, final int msec) {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (timerToShowDelayDialog != null) {
|
||||
timerToShowDelayDialog.cancel();
|
||||
timerToShowDelayDialog = null;
|
||||
}
|
||||
|
||||
if (msec == 0) {
|
||||
if (instance != null && instance.isAdded()) {
|
||||
instance.dismiss();
|
||||
instance = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance == null) {
|
||||
if (msec > minRemainingDelayToShowDialog) {
|
||||
instance = new WaitSecurityDelayDialog();
|
||||
// 1000ms - card delay notification interval
|
||||
instance.setup(msec + 1000, 1000);
|
||||
instance.setCancelable(false);
|
||||
instance.show(activity.getSupportFragmentManager(), TAG);
|
||||
}
|
||||
} else
|
||||
instance.setRemainingTimeout(msec);
|
||||
});
|
||||
}
|
||||
|
||||
private void setup(int msTimeout, int msProgress) {
|
||||
this.msTimeout = msTimeout;
|
||||
this.msProgress = msProgress;
|
||||
}
|
||||
|
||||
private void setRemainingTimeout(final int msec) {
|
||||
progressBar.post(() -> {
|
||||
int progress = WaitSecurityDelayDialog.this.progressBar.getProgress();
|
||||
if (timer != null) {
|
||||
// we get delay latency from card for first time - don't change progress by timer, only by card answer
|
||||
progressBar.setMax(progress + msec);
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
} else {
|
||||
int newProgress = progressBar.getMax() - msec;
|
||||
if (newProgress > progress)
|
||||
progressBar.setProgress(newProgress);
|
||||
else
|
||||
progressBar.setMax(progress + msec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
package com.tangem.ui.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import android.widget.ProgressBar
|
||||
import com.tangem.ui.event.ReadAfterRequest
|
||||
import com.tangem.ui.event.ReadBeforeRequest
|
||||
import com.tangem.ui.event.ReadWait
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.wallet.R
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import java.util.*
|
||||
|
||||
class WaitSecurityDelayDialogNew : AppCompatDialogFragment() {
|
||||
|
||||
companion object {
|
||||
val TAG: String = WaitSecurityDelayDialogNew::class.java.simpleName
|
||||
|
||||
private const val MIN_REMAINING_DELAY_TO_SHOW_DIALOG = 1000
|
||||
private const val DELAY_BEFORE_SHOW_DIALOG = 5000
|
||||
}
|
||||
|
||||
private lateinit var pb: ProgressBar
|
||||
|
||||
private var msTimeout = 60000
|
||||
private var msProgress = 0
|
||||
private var timer: Timer? = null
|
||||
private var timerToShowDelayDialog: Timer? = null
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val inflater = activity!!.layoutInflater
|
||||
val v = inflater.inflate(R.layout.dialog_wait_pin2, null)
|
||||
|
||||
pb = v.findViewById(R.id.progressBar)
|
||||
|
||||
pb.max = msTimeout
|
||||
pb.progress = msProgress
|
||||
|
||||
timer = Timer()
|
||||
timer!!.scheduleAtFixedRate(object : TimerTask() {
|
||||
override fun run() {
|
||||
pb.post {
|
||||
val progress = pb.progress
|
||||
if (progress < pb.max)
|
||||
pb.progress = progress + 1000
|
||||
}
|
||||
}
|
||||
}, 1000, 1000)
|
||||
|
||||
return AlertDialog.Builder(activity)
|
||||
.setIcon(R.drawable.tangem_logo_small_new)
|
||||
.setTitle(R.string.security_delay)
|
||||
.setView(v)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun readBeforeRequest(readBeforeRequest: ReadBeforeRequest) {
|
||||
LOG.i(TAG, "readBeforeRequest 111")
|
||||
|
||||
if (timerToShowDelayDialog != null || readBeforeRequest.timeout!! < DELAY_BEFORE_SHOW_DIALOG + MIN_REMAINING_DELAY_TO_SHOW_DIALOG)
|
||||
return
|
||||
|
||||
timerToShowDelayDialog = Timer()
|
||||
timerToShowDelayDialog!!.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
setup(readBeforeRequest.timeout!!, DELAY_BEFORE_SHOW_DIALOG)
|
||||
isCancelable = false
|
||||
|
||||
// if (!isAdded)
|
||||
show(activity!!.supportFragmentManager, TAG)
|
||||
|
||||
// if (isHidden)
|
||||
// activity?.supportFragmentManager?.let { show(it, TAG) }
|
||||
}
|
||||
}, DELAY_BEFORE_SHOW_DIALOG.toLong())
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun readAfterRequest(readAfterRequest: ReadAfterRequest) {
|
||||
LOG.i(TAG, "readAfterRequest 222")
|
||||
|
||||
if (timerToShowDelayDialog == null)
|
||||
return
|
||||
|
||||
timerToShowDelayDialog!!.cancel()
|
||||
timerToShowDelayDialog = null
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun readWait(readWait: ReadWait) {
|
||||
LOG.i(TAG, "readWait 333")
|
||||
|
||||
if (timerToShowDelayDialog != null) {
|
||||
timerToShowDelayDialog!!.cancel()
|
||||
timerToShowDelayDialog = null
|
||||
}
|
||||
|
||||
if (readWait.msec == 0) {
|
||||
dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
if (readWait.msec!! > MIN_REMAINING_DELAY_TO_SHOW_DIALOG) {
|
||||
// 1000ms - card delay notification interval
|
||||
setup(readWait.msec!! + 1000, 1000)
|
||||
isCancelable = false
|
||||
|
||||
if (isHidden)
|
||||
activity?.supportFragmentManager?.let { show(it, TAG) }
|
||||
|
||||
} else
|
||||
setRemainingTimeout(readWait.msec!!)
|
||||
}
|
||||
|
||||
private fun setup(msTimeout: Int, msProgress: Int) {
|
||||
this.msTimeout = msTimeout
|
||||
this.msProgress = msProgress
|
||||
}
|
||||
|
||||
private fun setRemainingTimeout(msec: Int) {
|
||||
pb.post {
|
||||
val progress = pb.progress
|
||||
if (timer != null) {
|
||||
|
||||
// we get delay latency from card for first time - don't change progress by timer, only by card answer
|
||||
pb.max = progress + msec
|
||||
timer!!.cancel()
|
||||
timer = null
|
||||
} else {
|
||||
val newProgress = pb.max - msec
|
||||
if (pb.max > progress)
|
||||
pb.progress = newProgress
|
||||
else
|
||||
pb.max = progress + msec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.ui.event
|
||||
|
||||
class DeletingWalletFinish
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.ui.event
|
||||
|
||||
class ReadAfterRequest
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.ui.event
|
||||
|
||||
class ReadBeforeRequest {
|
||||
var timeout: Int? = null
|
||||
}
|
||||
5
app/src/main/java/com/tangem/ui/event/ReadWait.kt
Normal file
5
app/src/main/java/com/tangem/ui/event/ReadWait.kt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.ui.event
|
||||
|
||||
class ReadWait {
|
||||
var msec: Int? = null
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.ui.event
|
||||
|
||||
class TransactionFinishWithError {
|
||||
var message: String? = null
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.ui.event
|
||||
|
||||
class TransactionFinishWithSuccess {
|
||||
var message: String? = null
|
||||
}
|
||||
802
app/src/main/java/com/tangem/ui/fragment/LoadedWallet.kt
Normal file
802
app/src/main/java/com/tangem/ui/fragment/LoadedWallet.kt
Normal file
|
|
@ -0,0 +1,802 @@
|
|||
package com.tangem.ui.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.*
|
||||
import android.content.Context.CLIPBOARD_SERVICE
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.media.MediaPlayer
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.data.network.ServerApiCommon
|
||||
import com.tangem.domain.wallet.BalanceValidator
|
||||
import com.tangem.domain.wallet.CoinEngine
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.ui.activity.LoadedWalletActivity
|
||||
import com.tangem.ui.activity.PinRequestActivity
|
||||
import com.tangem.ui.activity.PrepareCryptonitWithdrawalActivity
|
||||
import com.tangem.ui.activity.PrepareKrakenWithdrawalActivity
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.PINSwapWarningDialog
|
||||
import com.tangem.ui.dialog.ShowQRCodeDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.ui.event.DeletingWalletFinish
|
||||
import com.tangem.ui.event.TransactionFinishWithError
|
||||
import com.tangem.ui.event.TransactionFinishWithSuccess
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.android.reader.NfcReader
|
||||
import com.tangem.cardandroid.data.EXTRA_TANGEM_CARD
|
||||
import com.tangem.cardandroid.data.EXTRA_TANGEM_CARD_UID
|
||||
import com.tangem.cardandroid.data.loadFromBundle
|
||||
import com.tangem.cardcommon.data.TangemCard
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.cardcommon.tasks.VerifyCardTask
|
||||
import com.tangem.cardcommon.util.Util
|
||||
import com.tangem.serverandroid.ServerApiTangem
|
||||
import com.tangem.serverandroid.model.CardVerifyAndGetInfo
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fr_loaded_wallet.*
|
||||
import kotlinx.android.synthetic.main.layout_btn_details.*
|
||||
import kotlinx.android.synthetic.main.layout_tangem_card.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import kotlin.concurrent.timerTask
|
||||
|
||||
class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
companion object {
|
||||
val TAG: String = LoadedWallet::class.java.simpleName
|
||||
}
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var mpSecondScanSound: MediaPlayer
|
||||
private var serverApiCommon: ServerApiCommon = ServerApiCommon()
|
||||
private var serverApiTangem: ServerApiTangem = ServerApiTangem()
|
||||
private var lastTag: Tag? = null
|
||||
private var lastReadSuccess = true
|
||||
private var verifyCardTask: VerifyCardTask? = null
|
||||
private var requestPIN2Count = 0
|
||||
private var timerHideErrorAndMessage: Timer? = null
|
||||
private var newPIN = ""
|
||||
private var newPIN2 = ""
|
||||
private var cardProtocol: CardProtocol? = null
|
||||
private val inactiveColor: ColorStateList by lazy {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
resources.getColorStateList(R.color.btn_dark, activity?.theme)
|
||||
else
|
||||
@Suppress("DEPRECATION")
|
||||
resources.getColorStateList(R.color.btn_dark)
|
||||
}
|
||||
private val activeColor: ColorStateList by lazy {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
resources.getColorStateList(R.color.colorAccent, activity?.theme)
|
||||
else
|
||||
@Suppress("DEPRECATION")
|
||||
resources.getColorStateList(R.color.colorAccent)
|
||||
}
|
||||
private var requestCounter: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
LOG.i(TAG, "requestCounter, set $field")
|
||||
if (field <= 0) {
|
||||
LOG.e(TAG, "+++++++++++ FINISH REFRESH")
|
||||
if (srl != null && srl.isRefreshing)
|
||||
srl.isRefreshing = false
|
||||
} else if (srl != null && !srl.isRefreshing)
|
||||
srl.isRefreshing = true
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(activity, activity?.intent?.extras)
|
||||
|
||||
nfcManager = NfcManager(activity!!, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
lastTag = activity?.intent?.getParcelableExtra(Constant.EXTRA_LAST_DISCOVERED_TAG)
|
||||
|
||||
mpSecondScanSound = MediaPlayer.create(activity, R.raw.scan_card_sound)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fr_loaded_wallet, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
tvBalance.setSingleLine(!engine!!.needMultipleLinesForBalance())
|
||||
|
||||
ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card))
|
||||
|
||||
btnExtract.isEnabled = false
|
||||
btnExtract.backgroundTintList = inactiveColor
|
||||
|
||||
tvWallet.text = ctx.coinData.wallet
|
||||
|
||||
// set listeners
|
||||
srl.setOnRefreshListener { refresh() }
|
||||
|
||||
tvWallet.setOnClickListener { doShareWallet(false) }
|
||||
|
||||
btnExplore.setOnClickListener { startActivity(Intent(Intent.ACTION_VIEW, engine?.walletExplorerUri)) }
|
||||
|
||||
btnCopy.setOnClickListener { doShareWallet(false) }
|
||||
|
||||
btnLoad.setOnClickListener {
|
||||
val items = arrayOf<CharSequence>(getString(R.string.in_app), getString(R.string.load_via_share_address), getString(R.string.load_via_qr))//, getString(R.string.via_cryptonit), getString(R.string.via_kraken))
|
||||
val cw = android.view.ContextThemeWrapper(activity, R.style.AlertDialogTheme)
|
||||
val dialog = AlertDialog.Builder(cw).setItems(items
|
||||
) { _, which ->
|
||||
when (items[which]) {
|
||||
getString(R.string.in_app) -> {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, engine.shareWalletUri)
|
||||
intent.addCategory(Intent.CATEGORY_DEFAULT)
|
||||
startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
UtilHelper.showSingleToast(context, getString(R.string.no_compatible_wallet))
|
||||
}
|
||||
}
|
||||
|
||||
getString(R.string.load_via_share_address) -> {
|
||||
doShareWallet(true)
|
||||
}
|
||||
|
||||
getString(R.string.load_via_qr) -> {
|
||||
ShowQRCodeDialog.show(activity as AppCompatActivity?, engine.shareWalletUri.toString())
|
||||
}
|
||||
|
||||
getString(R.string.via_cryptonit) -> {
|
||||
val intent = Intent(activity, PrepareCryptonitWithdrawalActivity::class.java)
|
||||
ctx.saveToIntent(intent)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_RECEIVE_TRANSACTION)
|
||||
}
|
||||
|
||||
getString(R.string.via_kraken) -> {
|
||||
val intent = Intent(activity, PrepareKrakenWithdrawalActivity::class.java)
|
||||
ctx.saveToIntent(intent)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_RECEIVE_TRANSACTION)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
val dlg = dialog.show()
|
||||
val wlp = dlg.window.attributes
|
||||
wlp.gravity = Gravity.BOTTOM
|
||||
dlg.window.attributes = wlp
|
||||
}
|
||||
|
||||
btnExtract.setOnClickListener {
|
||||
if (UtilHelper.isOnline(context as Activity))
|
||||
if (!engine.isExtractPossible)
|
||||
UtilHelper.showSingleToast(context, ctx.message)
|
||||
else if (ctx.card!!.remainingSignatures == 0)
|
||||
UtilHelper.showSingleToast(context, getString(R.string.card_has_no_remaining_signature))
|
||||
else
|
||||
(activity as LoadedWalletActivity).navigator.showPrepareTransaction(context as Activity, ctx)
|
||||
else
|
||||
Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
btnDetails.setOnClickListener {
|
||||
if (cardProtocol != null)
|
||||
(activity as LoadedWalletActivity).navigator.showVerifyCard(context as Activity, ctx)
|
||||
else
|
||||
UtilHelper.showSingleToast(context, getString(R.string.need_attach_card_again))
|
||||
}
|
||||
|
||||
btnNewScan.setOnClickListener { (activity as LoadedWalletActivity).navigator.showMain(context as Activity) }
|
||||
|
||||
// request card verify and get info listener
|
||||
val cardVerifyAndGetInfoListener: ServerApiTangem.CardVerifyAndGetInfoListener = object : ServerApiTangem.CardVerifyAndGetInfoListener {
|
||||
override fun onSuccess(cardVerifyAndGetArtworkResponse: CardVerifyAndGetInfo.Response?) {
|
||||
LOG.i(TAG, "cardVerifyAndGetInfoListener onSuccess")
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return
|
||||
|
||||
val result = cardVerifyAndGetArtworkResponse?.results!![0]
|
||||
if (result.error != null) {
|
||||
ctx.card!!.isOnlineVerified = false
|
||||
return
|
||||
}
|
||||
ctx.card!!.isOnlineVerified = result.passed
|
||||
|
||||
requestCounter--
|
||||
updateViews()
|
||||
|
||||
if (!result.passed) return
|
||||
|
||||
if (App.localStorage.checkBatchInfoChanged(ctx.card!!, result)) {
|
||||
LOG.w(TAG, "Batch ${result.batch} info changed to '$result'")
|
||||
ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card!!))
|
||||
App.localStorage.applySubstitution(ctx.card!!)
|
||||
refresh()
|
||||
}
|
||||
if (result.artwork != null && App.localStorage.checkNeedUpdateArtwork(result.artwork)) {
|
||||
LOG.w(TAG, "Artwork '${result.artwork!!.id}' updated, need download")
|
||||
requestCounter++
|
||||
serverApiTangem.requestArtwork(result.artwork!!.id, result.artwork!!.getUpdateDate(), ctx.card!!)
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(message: String?) {
|
||||
LOG.i(TAG, "cardVerifyAndGetInfoListener onFail")
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return
|
||||
requestCounter--
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
serverApiTangem.setCardVerifyAndGetInfoListener(cardVerifyAndGetInfoListener)
|
||||
|
||||
// request artwork listener
|
||||
val artworkListener: ServerApiTangem.ArtworkListener = object : ServerApiTangem.ArtworkListener {
|
||||
override fun onSuccess(artworkId: String?, inputStream: InputStream?, updateDate: Date?) {
|
||||
LOG.i(TAG, "artworkListener onSuccess")
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return
|
||||
App.localStorage.updateArtwork(artworkId!!, inputStream!!, updateDate!!)
|
||||
requestCounter--
|
||||
ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card!!))
|
||||
updateViews()
|
||||
}
|
||||
|
||||
override fun onFail(message: String?) {
|
||||
LOG.i(TAG, "artworkListener onFail")
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return
|
||||
requestCounter--
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
serverApiTangem.setArtworkListener(artworkListener)
|
||||
|
||||
// request rate info listener
|
||||
serverApiCommon.setRateInfoListener {
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return@setRateInfoListener
|
||||
val rate = it.priceUsd.toFloat()
|
||||
ctx.coinData!!.rate = rate
|
||||
ctx.coinData!!.rateAlter = rate
|
||||
}
|
||||
|
||||
refresh()
|
||||
|
||||
startVerify(lastTag)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (timerHideErrorAndMessage != null) {
|
||||
timerHideErrorAndMessage!!.cancel()
|
||||
timerHideErrorAndMessage = null
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx.message = transactionFinishWithSuccess.message
|
||||
ctx.coinData.clearInfo()
|
||||
updateViews()
|
||||
srl?.isRefreshing = true
|
||||
srl?.postDelayed({ refresh() }, 5000)
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onTransactionFinishWithError(transactionFinishWithError: TransactionFinishWithError) {
|
||||
ctx.error = transactionFinishWithError.message
|
||||
updateViews()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onDeleteWalletFinish(deletingWalletFinish: DeletingWalletFinish) {
|
||||
activity?.finish()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
Constant.REQUEST_CODE_VERIFY_CARD ->
|
||||
// action after erase wallet
|
||||
if (resultCode == Activity.RESULT_OK)
|
||||
activity?.finish()
|
||||
|
||||
Constant.REQUEST_CODE_ENTER_NEW_PIN -> if (resultCode == Activity.RESULT_OK)
|
||||
if (data != null)
|
||||
if (data.extras != null && data.extras!!.containsKey(Constant.EXTRA_CONFIRM_PIN))
|
||||
(activity as LoadedWalletActivity).navigator.showPinRequestRequestPin(context as Activity, PinRequestActivity.Mode.RequestPIN.toString(), ctx, data.getStringExtra(Constant.EXTRA_NEW_PIN))
|
||||
else
|
||||
(activity as LoadedWalletActivity).navigator.showPinRequestConfirmNewPin(context as Activity, PinRequestActivity.Mode.ConfirmNewPIN.toString(), data.getStringExtra(Constant.EXTRA_NEW_PIN))
|
||||
|
||||
|
||||
Constant.REQUEST_CODE_ENTER_NEW_PIN2 -> if (resultCode == Activity.RESULT_OK)
|
||||
if (data != null)
|
||||
if (data.extras != null && data.extras!!.containsKey(Constant.EXTRA_CONFIRM_PIN_2))
|
||||
(activity as LoadedWalletActivity).navigator.showPinRequestRequestPin2(context as Activity, PinRequestActivity.Mode.RequestPIN2.toString(), ctx, data.getStringExtra(Constant.EXTRA_NEW_PIN_2))
|
||||
else
|
||||
(activity as LoadedWalletActivity).navigator.showPinRequestConfirmNewPin2(context as Activity, PinRequestActivity.Mode.ConfirmNewPIN2.toString(), data.getStringExtra(Constant.EXTRA_NEW_PIN_2))
|
||||
|
||||
Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (newPIN == "")
|
||||
newPIN = ctx.card!!.pin
|
||||
|
||||
if (newPIN2 == "")
|
||||
newPIN2 = App.pinStorage.piN2
|
||||
|
||||
val pinSwapWarningDialog = PINSwapWarningDialog()
|
||||
pinSwapWarningDialog.setOnRefreshPage { (activity as LoadedWalletActivity).navigator.showPinSwap(context as Activity, newPIN, newPIN2) }
|
||||
val bundle = Bundle()
|
||||
if (!CardProtocol.isDefaultPIN(newPIN) || !CardProtocol.isDefaultPIN2(newPIN2))
|
||||
bundle.putString(PINSwapWarningDialog.EXTRA_MESSAGE, getString(R.string.if_you_forget))
|
||||
else
|
||||
bundle.putString(PINSwapWarningDialog.EXTRA_MESSAGE, getString(R.string.if_you_use_default))
|
||||
pinSwapWarningDialog.arguments = bundle
|
||||
activity?.supportFragmentManager?.let { pinSwapWarningDialog.show(it, PINSwapWarningDialog.TAG) }
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_SWAP_PIN -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (data == null) {
|
||||
ctx.saveToIntent(data)
|
||||
data?.putExtra(Constant.EXTRA_MODIFICATION, Constant.EXTRA_MODIFICATION_DELETE)
|
||||
} else
|
||||
data.putExtra(Constant.EXTRA_MODIFICATION, Constant.EXTRA_MODIFICATION_UPDATE)
|
||||
|
||||
activity?.setResult(Activity.RESULT_OK, data)
|
||||
activity?.finish()
|
||||
|
||||
} else {
|
||||
if (data != null && data.extras != null && 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++
|
||||
(activity as LoadedWalletActivity).navigator.showPinRequestRequestPin2(context as Activity, PinRequestActivity.Mode.RequestPIN2.toString(), ctx)
|
||||
return
|
||||
} else {
|
||||
if (data != null && data.extras!!.containsKey("message")) {
|
||||
ctx.error = data.getStringExtra("message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_PURGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (data == null) {
|
||||
ctx.saveToIntent(data)
|
||||
data?.putExtra(Constant.EXTRA_MODIFICATION, Constant.EXTRA_MODIFICATION_DELETE)
|
||||
} else
|
||||
data.putExtra(Constant.EXTRA_MODIFICATION, Constant.EXTRA_MODIFICATION_UPDATE)
|
||||
|
||||
activity?.setResult(Activity.RESULT_OK, data)
|
||||
activity?.finish()
|
||||
|
||||
} else {
|
||||
if (data != null && data.extras != null && 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(activity, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_PURGE)
|
||||
|
||||
return
|
||||
} else {
|
||||
if (data != null && data.extras!!.containsKey("message")) {
|
||||
ctx.error = data.getStringExtra("message")
|
||||
}
|
||||
}
|
||||
updateViews()
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_SEND_TRANSACTION, Constant.REQUEST_CODE_RECEIVE_TRANSACTION -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
ctx.coinData?.clearInfo()
|
||||
srl?.postDelayed({ this.refresh() }, 5000)
|
||||
srl?.isRefreshing = true
|
||||
updateViews()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
startVerify(tag)
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
verifyCardTask = null
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.error == null) {
|
||||
rlProgressBar?.post {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
this.cardProtocol = cardProtocol
|
||||
if (!cardProtocol.card.isWalletPublicKeyValid)
|
||||
refresh()
|
||||
else
|
||||
updateViews()
|
||||
|
||||
mpSecondScanSound.start()
|
||||
}
|
||||
} else {
|
||||
// remove last UIDs because of error and no card read
|
||||
rlProgressBar?.post {
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed)
|
||||
activity?.supportFragmentManager?.let { NoExtendedLengthSupportDialog().show(it, NoExtendedLengthSupportDialog.TAG) }
|
||||
else
|
||||
Toast.makeText(activity, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
verifyCardTask = null
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(activity as AppCompatActivity?, msec)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(activity as AppCompatActivity?, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(activity)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
|
||||
}
|
||||
|
||||
fun updateViews() {
|
||||
if (activity == null) return
|
||||
|
||||
if (timerHideErrorAndMessage != null) {
|
||||
timerHideErrorAndMessage!!.cancel()
|
||||
timerHideErrorAndMessage = null
|
||||
}
|
||||
|
||||
if (ctx.hasError()) {
|
||||
tvError?.visibility = View.VISIBLE
|
||||
tvError?.text = ctx.error
|
||||
} else {
|
||||
tvError?.visibility = View.GONE
|
||||
tvError?.text = ""
|
||||
}
|
||||
|
||||
if (ctx.message == null || ctx.message.isEmpty()) {
|
||||
tvMessage.text = ""
|
||||
tvMessage.visibility = View.GONE
|
||||
} else {
|
||||
tvMessage.text = ctx.message
|
||||
tvMessage.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (tvError.visibility == View.VISIBLE || tvMessage.visibility == View.VISIBLE) {
|
||||
timerHideErrorAndMessage = Timer()
|
||||
timerHideErrorAndMessage!!.schedule(
|
||||
timerTask {
|
||||
activity?.runOnUiThread {
|
||||
tvMessage?.visibility = View.GONE
|
||||
tvError?.visibility = View.GONE
|
||||
// clear only already viewed messages
|
||||
if (tvMessage.text == ctx.message) ctx.message = null
|
||||
if (tvError.text == ctx.error) ctx.error = null
|
||||
}
|
||||
},
|
||||
5000)
|
||||
}
|
||||
|
||||
if (srl.isRefreshing) {
|
||||
tvBalanceLine1.setTextColor(resources.getColor(R.color.primary))
|
||||
tvBalanceLine1.text = getString(R.string.verifying_in_blockchain)
|
||||
tvBalanceLine2.text = ""
|
||||
tvBalance.text = ""
|
||||
tvBalanceEquivalent.text = ""
|
||||
} else {
|
||||
val validator = BalanceValidator()
|
||||
// TODO why attest=false?
|
||||
validator.check(ctx, false)
|
||||
context?.let { ContextCompat.getColor(it, validator.color) }?.let { tvBalanceLine1.setTextColor(it) }
|
||||
tvBalanceLine1.text = validator.firstLine
|
||||
tvBalanceLine2.text = validator.getSecondLine(false)
|
||||
}
|
||||
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
when {
|
||||
engine!!.hasBalanceInfo() -> {
|
||||
@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
|
||||
tvBalanceEquivalent.text = engine.balanceEquivalent
|
||||
}
|
||||
|
||||
ctx.card?.offlineBalance != null -> {
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(engine.offlineBalanceHTML, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(engine.offlineBalanceHTML)
|
||||
tvBalance.text = html
|
||||
}
|
||||
|
||||
else -> tvBalance.text = getString(R.string.no_data_string)
|
||||
}
|
||||
|
||||
tvWallet.text = ctx.coinData!!.wallet
|
||||
|
||||
if (ctx.card!!.tokenSymbol.length > 1) {
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(ctx.blockchainName, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(ctx.blockchainName)
|
||||
tvBlockchain.text = html
|
||||
} else
|
||||
tvBlockchain.text = ctx.blockchainName
|
||||
|
||||
if (requestCounter == 0 && engine.hasBalanceInfo()) {
|
||||
btnExtract.isEnabled = true
|
||||
btnExtract.backgroundTintList = activeColor
|
||||
} else {
|
||||
btnExtract.isEnabled = false
|
||||
btnExtract.backgroundTintList = inactiveColor
|
||||
}
|
||||
|
||||
if (engine.isNftToken) {
|
||||
btnLoad.isEnabled = false
|
||||
btnLoad.backgroundTintList = inactiveColor
|
||||
btnExtract.isEnabled = false
|
||||
btnExtract.backgroundTintList = inactiveColor
|
||||
}
|
||||
|
||||
// //TODO why ???
|
||||
// ctx.error = null
|
||||
// ctx.message = null
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
if (ctx.card == null) return
|
||||
|
||||
// clear all card data and request again
|
||||
ctx.coinData.clearInfo()
|
||||
ctx.error = null
|
||||
ctx.message = null
|
||||
|
||||
LOG.w(TAG, "============= START REFRESH")
|
||||
requestCounter = 0
|
||||
srl?.isRefreshing = true
|
||||
|
||||
updateViews()
|
||||
|
||||
// Bitcoin, Litecoin, BitcoinCash
|
||||
if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet || ctx.blockchain == Blockchain.Litecoin || ctx.blockchain == Blockchain.BitcoinCash) {
|
||||
ctx.coinData.setIsBalanceEqual(true)
|
||||
}
|
||||
|
||||
requestVerifyAndGetInfo()
|
||||
|
||||
requestBalanceAndUnspentTransactions()
|
||||
|
||||
requestRateInfo()
|
||||
|
||||
if (requestCounter == 0) {
|
||||
// if no connection and no requests posted
|
||||
srl?.isRefreshing = false
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestBalanceAndUnspentTransactions() {
|
||||
if (UtilHelper.isOnline(context as Activity)) {
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
requestCounter++
|
||||
coinEngine!!.requestBalanceAndUnspentTransactions(
|
||||
object : CoinEngine.BlockchainRequestsCallbacks {
|
||||
|
||||
|
||||
override fun onComplete(success: Boolean) {
|
||||
LOG.i(TAG, "requestBalanceAndUnspentTransactions onComplete: $success, request counter $requestCounter")
|
||||
if (activity == null) return
|
||||
requestCounter--
|
||||
if (!success) {
|
||||
LOG.e(TAG, "requestBalanceAndUnspentTransactions ctx.error: " + ctx.error)
|
||||
}
|
||||
updateViews()
|
||||
}
|
||||
|
||||
override fun onProgress() {
|
||||
if (activity == null) return
|
||||
LOG.i(TAG, "requestBalanceAndUnspentTransactions onProgress")
|
||||
updateViews()
|
||||
}
|
||||
|
||||
override fun allowAdvance(): Boolean {
|
||||
return try {
|
||||
context?.let { UtilHelper.isOnline(it) }!!
|
||||
} catch (e: KotlinNullPointerException) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
ctx.error = getString(R.string.no_connection)
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestVerifyAndGetInfo() {
|
||||
if (UtilHelper.isOnline(context as Activity)) {
|
||||
if ((ctx.card!!.isOnlineVerified == null || !ctx.card!!.isOnlineVerified)) {
|
||||
LOG.i(TAG, "requestVerifyAndGetInfo")
|
||||
requestCounter++
|
||||
serverApiTangem.cardVerifyAndGetInfo(ctx.card)
|
||||
}
|
||||
} else {
|
||||
ctx.error = getString(R.string.no_connection)
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestRateInfo() {
|
||||
if (UtilHelper.isOnline(context as Activity)) {
|
||||
LOG.i(TAG, "requestRateInfo")
|
||||
|
||||
// TODO - move requestRateInfo to CoinEngine
|
||||
val cryptoId: String = when (ctx.blockchain) {
|
||||
Blockchain.Bitcoin -> "bitcoin"
|
||||
Blockchain.BitcoinTestNet -> "bitcoin"
|
||||
Blockchain.Ethereum -> "ethereum"
|
||||
Blockchain.EthereumTestNet -> "ethereum"
|
||||
Blockchain.Token -> "ethereum"
|
||||
Blockchain.NftToken -> "ethereum"
|
||||
Blockchain.BitcoinCash -> "bitcoin-cash"
|
||||
Blockchain.Litecoin -> "litecoin"
|
||||
Blockchain.Rootstock -> "bitcoin"
|
||||
Blockchain.RootstockToken -> "bitcoin"
|
||||
Blockchain.Cardano -> "cardano"
|
||||
else -> {
|
||||
throw Exception("Can''t get rate for blockchain " + ctx.blockchainName)
|
||||
}
|
||||
}
|
||||
serverApiCommon.requestRateInfo(cryptoId)
|
||||
} else {
|
||||
ctx.error = getString(R.string.no_connection)
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startVerify(tag: Tag?) {
|
||||
try {
|
||||
val isoDep = IsoDep.get(tag)
|
||||
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
||||
val uid = tag!!.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
if (ctx.card.uid != sUID || cardProtocol != null) {
|
||||
nfcManager.ignoreTag(isoDep.tag)
|
||||
return
|
||||
}
|
||||
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = 1000
|
||||
else
|
||||
isoDep.timeout = 65000
|
||||
|
||||
|
||||
verifyCardTask = VerifyCardTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, App.firmwaresStorage, this)
|
||||
verifyCardTask?.start()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doShareWallet(useURI: Boolean) {
|
||||
if (useURI) {
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
val txtShare = engine!!.shareWalletUri.toString()
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
intent.type = "text/plain"
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "Wallet address")
|
||||
intent.putExtra(Intent.EXTRA_TEXT, txtShare)
|
||||
|
||||
val packageManager = activity?.packageManager
|
||||
val activities = packageManager?.queryIntentActivities(intent, PackageManager.MATCH_ALL)
|
||||
val isIntentSafe = activities?.size!! > 0
|
||||
|
||||
if (isIntentSafe) {
|
||||
// create intent to show chooser
|
||||
val chooser = Intent.createChooser(intent, getString(R.string.share_wallet_address_with))
|
||||
|
||||
// verify the intent will resolve to at least one activity
|
||||
if (intent.resolveActivity(activity!!.packageManager) != null) {
|
||||
startActivity(chooser)
|
||||
}
|
||||
} else {
|
||||
val clipboard = activity?.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.primaryClip = ClipData.newPlainText(txtShare, txtShare)
|
||||
Toast.makeText(activity, R.string.copied_clipboard, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
} else {
|
||||
val txtShare = ctx.coinData.wallet
|
||||
val clipboard = activity?.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.primaryClip = ClipData.newPlainText(txtShare, txtShare)
|
||||
Toast.makeText(activity, R.string.copied_clipboard, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
493
app/src/main/java/com/tangem/ui/fragment/VerifyCard.kt
Normal file
493
app/src/main/java/com/tangem/ui/fragment/VerifyCard.kt
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
package com.tangem.ui.fragment
|
||||
|
||||
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.core.content.ContextCompat
|
||||
import android.text.Html
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.Toast
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.ui.activity.*
|
||||
import com.tangem.ui.dialog.PINSwapWarningDialog
|
||||
import com.tangem.ui.event.DeletingWalletFinish
|
||||
import com.tangem.cardandroid.android.data.PINStorage
|
||||
import com.tangem.cardandroid.android.nfc.NfcLifecycleObserver
|
||||
import com.tangem.cardandroid.android.reader.NfcManager
|
||||
import com.tangem.cardandroid.data.loadFromBundle
|
||||
import com.tangem.cardcommon.data.TangemCard
|
||||
import com.tangem.cardcommon.reader.CardProtocol
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fr_verify_card.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class VerifyCard : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback {
|
||||
companion object {
|
||||
val TAG: String = VerifyCard::class.java.simpleName
|
||||
}
|
||||
|
||||
private lateinit var nfcManager: NfcManager
|
||||
private lateinit var ctx: TangemContext
|
||||
|
||||
private var requestPIN2Count = 0
|
||||
private var timerHideErrorAndMessage: Timer? = null
|
||||
private var newPIN = ""
|
||||
private var newPIN2 = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(activity, activity?.intent?.extras)
|
||||
|
||||
nfcManager = NfcManager(activity!!, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fr_verify_card, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
updateViews()
|
||||
|
||||
srlVerifyCard.setOnRefreshListener { srlVerifyCard.isRefreshing = false }
|
||||
|
||||
// set listeners
|
||||
fabMenu.setOnClickListener { showMenu(fabMenu) }
|
||||
|
||||
btnOk.setOnClickListener {
|
||||
val data = prepareResultIntent()
|
||||
data.putExtra(Constant.EXTRA_MODIFICATION, "update")
|
||||
activity?.finish()
|
||||
}
|
||||
}
|
||||
|
||||
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 onDeleteWalletFinish(deletingWalletFinish: DeletingWalletFinish) {
|
||||
activity?.finish()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
LOG.i(TAG, "requestCode $requestCode resultCode $resultCode")
|
||||
when (requestCode) {
|
||||
Constant.REQUEST_CODE_ENTER_NEW_PIN -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (data != null) {
|
||||
if (data.extras!!.containsKey("confirmPIN")) {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
newPIN = data.getStringExtra("newPIN")
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
|
||||
} else {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("newPIN", data.getStringExtra("newPIN"))
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.ConfirmNewPIN.toString())
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_ENTER_NEW_PIN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_ENTER_NEW_PIN2 -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (data != null) {
|
||||
if (data.extras!!.containsKey("confirmPIN2")) {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
newPIN2 = data.getStringExtra("newPIN2")
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
|
||||
} else {
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("newPIN2", data.getStringExtra("newPIN2"))
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.ConfirmNewPIN2.toString())
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_ENTER_NEW_PIN2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (newPIN == "") newPIN = ctx.card!!.pin
|
||||
|
||||
if (newPIN2 == "") newPIN2 = App.pinStorage.piN2
|
||||
|
||||
val pinSwapWarningDialog = PINSwapWarningDialog()
|
||||
pinSwapWarningDialog.setOnRefreshPage { (activity as VerifyCardActivity).navigator.showPinSwap(context as Activity, newPIN, newPIN2) }
|
||||
val bundle = Bundle()
|
||||
if (!CardProtocol.isDefaultPIN(newPIN) || !CardProtocol.isDefaultPIN2(newPIN2))
|
||||
bundle.putString(PINSwapWarningDialog.EXTRA_MESSAGE, getString(R.string.if_you_forget))
|
||||
else
|
||||
bundle.putString(PINSwapWarningDialog.EXTRA_MESSAGE, getString(R.string.if_you_use_default))
|
||||
pinSwapWarningDialog.arguments = bundle
|
||||
activity?.supportFragmentManager?.let { pinSwapWarningDialog.show(it, PINSwapWarningDialog.TAG) }
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_SWAP_PIN -> if (resultCode == Activity.RESULT_OK) {
|
||||
if (data == null) {
|
||||
ctx.saveToIntent(data)
|
||||
data?.putExtra("modification", "delete")
|
||||
} else
|
||||
data.putExtra("modification", "update")
|
||||
activity!!.setResult(Activity.RESULT_OK, data)
|
||||
activity!!.finish()
|
||||
} else {
|
||||
if (data != null && data.extras!!.containsKey("UID") && data.extras!!.containsKey("Card")) {
|
||||
val updatedCard = TangemCard(data.getStringExtra("UID"))
|
||||
updatedCard.loadFromBundle(data.getBundleExtra("Card"))
|
||||
ctx.card = updatedCard
|
||||
}
|
||||
if (resultCode == Constant.RESULT_INVALID_PIN && requestPIN2Count < 2) {
|
||||
requestPIN2Count++
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
|
||||
return
|
||||
} else {
|
||||
if (data != null && data.extras!!.containsKey("message")) {
|
||||
ctx.error = data.getStringExtra("message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constant.REQUEST_CODE_REQUEST_PIN2_FOR_PURGE -> if (resultCode == Activity.RESULT_OK)
|
||||
(activity as VerifyCardActivity).navigator.showPurge(context as Activity, ctx)
|
||||
|
||||
// Constant.REQUEST_CODE_PURGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
// if (data == null) {
|
||||
// ctx.saveToIntent(data)
|
||||
// data?.putExtra(Constant.EXTRA_MODIFICATION, "delete")
|
||||
// } else
|
||||
// data.putExtra(Constant.EXTRA_MODIFICATION, "update")
|
||||
//
|
||||
// activity?.setResult(Activity.RESULT_OK, data)
|
||||
// activity?.finish()
|
||||
// }
|
||||
|
||||
else {
|
||||
if (data != null && data.extras!!.containsKey("UID") && data.extras!!.containsKey("Card")) {
|
||||
val updatedCard = TangemCard(data.getStringExtra("UID"))
|
||||
updatedCard.loadFromBundle(data.getBundleExtra("Card"))
|
||||
ctx.card = updatedCard
|
||||
}
|
||||
if (resultCode == Constant.RESULT_INVALID_PIN && requestPIN2Count < 2) {
|
||||
requestPIN2Count++
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_PURGE)
|
||||
return
|
||||
} else {
|
||||
if (data != null && data.extras!!.containsKey("message")) {
|
||||
ctx.error = data.getStringExtra("message")
|
||||
}
|
||||
}
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag?) {
|
||||
try {
|
||||
nfcManager.ignoreTag(tag!!)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateViews() {
|
||||
try {
|
||||
if (timerHideErrorAndMessage != null) {
|
||||
timerHideErrorAndMessage!!.cancel()
|
||||
timerHideErrorAndMessage = null
|
||||
}
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
|
||||
if (ctx.error == null || ctx.error.isEmpty()) {
|
||||
tvError.visibility = View.GONE
|
||||
tvError.text = ""
|
||||
} else {
|
||||
tvError.visibility = View.VISIBLE
|
||||
tvError.text = ctx.error
|
||||
}
|
||||
if (ctx.message == null || ctx.message.isEmpty()) {
|
||||
tvMessage.visibility = View.GONE
|
||||
tvMessage.text = ""
|
||||
} else {
|
||||
tvMessage.visibility = View.VISIBLE
|
||||
tvMessage.text = ctx.message
|
||||
}
|
||||
|
||||
tvManufacturerInfo.text = ctx.card!!.manufacturer.officialName
|
||||
|
||||
if (ctx.card!!.isManufacturerConfirmed && ctx.card!!.isCardPublicKeyValid) {
|
||||
tvCardIdentity.setText(R.string.attested)
|
||||
tvCardIdentity.setTextColor(ContextCompat.getColor(context!!, R.color.confirmed))
|
||||
|
||||
} else {
|
||||
tvCardIdentity.setText(R.string.not_confirmed)
|
||||
tvCardIdentity.setTextColor(ContextCompat.getColor(context!!, R.color.not_confirmed))
|
||||
}
|
||||
|
||||
tvIssuer.text = ctx.card!!.issuerDescription
|
||||
|
||||
tvCardRegistredDate.text = DateUtils.formatDateTime(null, ctx.card!!.personalizationDateTime.time, DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NUMERIC_DATE or DateUtils.FORMAT_SHOW_YEAR)
|
||||
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(ctx.blockchainName, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(ctx.blockchainName)
|
||||
tvBlockchain.text = html
|
||||
|
||||
tvValidationNode.text = ctx.coinData!!.validationNodeDescription
|
||||
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
|
||||
tvInputs.text = engine!!.unspentInputsDescription
|
||||
|
||||
ivBlockchain.setImageResource(Blockchain.getLogoImageResource(ctx.card!!.blockchainID, ctx.card!!.tokenSymbol))
|
||||
|
||||
if (ctx.card!!.isReusable!!)
|
||||
tvReusable.setText(R.string.reusable)
|
||||
else
|
||||
tvReusable.setText(R.string.one_off_banknote)
|
||||
|
||||
tvSigningMethod.text = ctx.card!!.signingMethod.description
|
||||
|
||||
if (ctx.card!!.status == TangemCard.Status.Loaded || ctx.card!!.status == TangemCard.Status.Purged) {
|
||||
|
||||
when {
|
||||
ctx.card!!.remainingSignatures == 0 -> {
|
||||
tvRemainingSignatures.setTextColor(ContextCompat.getColor(context!!, R.color.not_confirmed))
|
||||
tvRemainingSignatures.setText(R.string.none)
|
||||
}
|
||||
ctx.card!!.remainingSignatures == 1 -> {
|
||||
tvRemainingSignatures.setTextColor(ContextCompat.getColor(context!!, R.color.not_confirmed))
|
||||
tvRemainingSignatures.setText(R.string.last_one)
|
||||
}
|
||||
ctx.card!!.remainingSignatures > 1000 -> {
|
||||
tvRemainingSignatures.setTextColor(ContextCompat.getColor(context!!, R.color.confirmed))
|
||||
tvRemainingSignatures.setText(R.string.unlimited)
|
||||
}
|
||||
else -> {
|
||||
tvRemainingSignatures.setTextColor(ContextCompat.getColor(context!!, R.color.confirmed))
|
||||
tvRemainingSignatures.text = ctx.card!!.remainingSignatures.toString()
|
||||
}
|
||||
}
|
||||
tvSignedTx.text = (ctx.card!!.maxSignatures - ctx.card!!.remainingSignatures).toString()
|
||||
} else {
|
||||
tvLastSigned.text = ""
|
||||
tvRemainingSignatures.text = ""
|
||||
tvSignedTx.text = ""
|
||||
}
|
||||
|
||||
tvFirmware.text = ctx.card!!.firmwareVersion
|
||||
|
||||
var features = ""
|
||||
|
||||
features += if (ctx.card!!.allowSwapPIN()!! && ctx.card!!.allowSwapPIN2()!!) {
|
||||
"Allows change PIN1 and PIN2\n"
|
||||
} else if (ctx.card!!.allowSwapPIN()!!) {
|
||||
"Allows change PIN1\n"
|
||||
} else if (ctx.card!!.allowSwapPIN2()!!) {
|
||||
"Allows change PIN2\n"
|
||||
} else {
|
||||
"Fixed PIN1 and PIN2\n"
|
||||
}
|
||||
|
||||
if (ctx.card!!.needCVC()!!)
|
||||
features += "Requires CVC\n"
|
||||
|
||||
|
||||
if (ctx.card!!.supportDynamicNDEF()!!) {
|
||||
features += "Dynamic NDEF for iOS\n"
|
||||
} else if (ctx.card!!.supportNDEF()!!)
|
||||
features += "NDEF\n"
|
||||
|
||||
if (ctx.card!!.supportBlock()!!)
|
||||
features += "Blockable\n"
|
||||
|
||||
|
||||
if (ctx.card!!.supportOnlyOneCommandAtTime()!!)
|
||||
features += "Atomic command mode"
|
||||
|
||||
if (features.endsWith("\n"))
|
||||
features = features.substring(0, features.length - 1)
|
||||
|
||||
tvFeatures.text = features
|
||||
|
||||
if (ctx.card!!.useDefaultPIN1()) {
|
||||
imgPIN.setImageResource(R.drawable.unlock_pin1)
|
||||
imgPIN.setOnClickListener { Toast.makeText(context, R.string.this_banknote_protected_default_PIN1_code, Toast.LENGTH_LONG).show() }
|
||||
} else {
|
||||
imgPIN.setImageResource(R.drawable.lock_pin1)
|
||||
imgPIN.setOnClickListener { Toast.makeText(context, R.string.this_banknote_protected_user_PIN1_code, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
||||
if (ctx.card!!.pauseBeforePIN2 > 0 && (ctx.card!!.useDefaultPIN2()!! || !ctx.card!!.useSmartSecurityDelay())) {
|
||||
imgPIN2orSecurityDelay.setImageResource(R.drawable.timer)
|
||||
imgPIN2orSecurityDelay.setOnClickListener { Toast.makeText(context, String.format(getString(R.string.this_banknote_will_enforce), ctx.card!!.pauseBeforePIN2 / 1000.0), Toast.LENGTH_LONG).show() }
|
||||
} else if (ctx.card!!.useDefaultPIN2()!!) {
|
||||
imgPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2)
|
||||
imgPIN2orSecurityDelay.setOnClickListener { Toast.makeText(context, R.string.this_banknote_protected_default_PIN2_code, Toast.LENGTH_LONG).show() }
|
||||
} else {
|
||||
imgPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2)
|
||||
imgPIN2orSecurityDelay.setOnClickListener { Toast.makeText(context, R.string.this_banknote_protected_user_PIN2_code, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
||||
if (ctx.card!!.useDevelopersFirmware()!!) {
|
||||
imgDeveloperVersion.setImageResource(R.drawable.ic_developer_version)
|
||||
imgDeveloperVersion.visibility = View.VISIBLE
|
||||
imgDeveloperVersion.setOnClickListener { Toast.makeText(context, R.string.unlocked_banknote_only_development_use, Toast.LENGTH_LONG).show() }
|
||||
} else
|
||||
imgDeveloperVersion.visibility = View.INVISIBLE
|
||||
|
||||
if (ctx.card!!.status == TangemCard.Status.Loaded) {
|
||||
tvWallet.text = ctx.coinData!!.shortWalletString
|
||||
if (ctx.card!!.isWalletPublicKeyValid) {
|
||||
tvWalletIdentity.setText(R.string.possession_proved)
|
||||
tvWalletIdentity.setTextColor(ContextCompat.getColor(context!!, R.color.confirmed))
|
||||
} else {
|
||||
tvWalletIdentity.setText(R.string.possession_not_proved)
|
||||
tvWalletIdentity.setTextColor(ContextCompat.getColor(context!!, R.color.not_confirmed))
|
||||
}
|
||||
} else {
|
||||
tvWallet!!.setText(R.string.not_available)
|
||||
tvWalletIdentity.setText(R.string.no_data_string)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doSetPin() {
|
||||
requestPIN2Count = 0
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestNewPIN.toString())
|
||||
newPIN = ""
|
||||
newPIN2 = ""
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_ENTER_NEW_PIN)
|
||||
}
|
||||
|
||||
private fun doResetPin() {
|
||||
requestPIN2Count = 0
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
newPIN = PINStorage.getDefaultPIN()
|
||||
newPIN2 = ""
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
|
||||
}
|
||||
|
||||
private fun doResetPin2() {
|
||||
requestPIN2Count = 0
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
newPIN = ""
|
||||
newPIN2 = PINStorage.getDefaultPIN2()
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
|
||||
}
|
||||
|
||||
private fun doResetPins() {
|
||||
requestPIN2Count = 0
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
newPIN = PINStorage.getDefaultPIN()
|
||||
newPIN2 = PINStorage.getDefaultPIN2()
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
|
||||
}
|
||||
|
||||
private fun doSetPin2() {
|
||||
requestPIN2Count = 0
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestNewPIN2.toString())
|
||||
newPIN = ""
|
||||
newPIN2 = ""
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_ENTER_NEW_PIN2)
|
||||
}
|
||||
|
||||
private fun doPurge() {
|
||||
requestPIN2Count = 0
|
||||
val engine = CoinEngineFactory.create(ctx)
|
||||
if (!engine!!.hasBalanceInfo()) {
|
||||
return
|
||||
} else if (engine.isBalanceNotZero) {
|
||||
Toast.makeText(context, R.string.cannot_erase_wallet_with_non_zero_balance, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
|
||||
val intent = Intent(context, PinRequestActivity::class.java)
|
||||
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
|
||||
ctx.saveToIntent(intent)
|
||||
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_PURGE)
|
||||
}
|
||||
|
||||
fun prepareResultIntent(): Intent {
|
||||
val data = Intent()
|
||||
ctx.saveToIntent(data)
|
||||
return data
|
||||
}
|
||||
|
||||
private fun showMenu(v: View) {
|
||||
val popup = PopupMenu(activity, v)
|
||||
val inflater = popup.menuInflater
|
||||
inflater.inflate(R.menu.menu_loaded_wallet, popup.menu)
|
||||
|
||||
popup.menu.findItem(R.id.action_set_PIN1).isVisible = ctx.card!!.allowSwapPIN()!!
|
||||
popup.menu.findItem(R.id.action_reset_PIN1).isVisible = ctx.card!!.allowSwapPIN()!! && !ctx.card!!.useDefaultPIN1()
|
||||
popup.menu.findItem(R.id.action_set_PIN2).isVisible = ctx.card!!.allowSwapPIN2()!!
|
||||
popup.menu.findItem(R.id.action_reset_PIN2).isVisible = ctx.card!!.allowSwapPIN2()!! && !ctx.card!!.useDefaultPIN2()
|
||||
popup.menu.findItem(R.id.action_reset_PINs).isVisible = ctx.card!!.allowSwapPIN()!! && ctx.card!!.allowSwapPIN2()!! && !ctx.card!!.useDefaultPIN1() && !ctx.card!!.useDefaultPIN2()
|
||||
if (!ctx.card!!.isReusable || ctx.card!!.status != TangemCard.Status.Loaded)
|
||||
popup.menu.findItem(R.id.action_purge).isVisible = false
|
||||
|
||||
popup.setOnMenuItemClickListener { item ->
|
||||
val id = item.itemId
|
||||
when (id) {
|
||||
R.id.action_set_PIN1 -> doSetPin()
|
||||
R.id.action_reset_PIN1 -> doResetPin()
|
||||
R.id.action_set_PIN2 -> doSetPin2()
|
||||
R.id.action_reset_PIN2 -> doResetPin2()
|
||||
R.id.action_reset_PINs -> doResetPins()
|
||||
R.id.action_purge -> doPurge()
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
popup.menu.findItem(R.id.action_set_PIN2).isEnabled = true
|
||||
popup.menu.findItem(R.id.action_reset_PIN2).isEnabled = true
|
||||
}
|
||||
|
||||
popup.show()
|
||||
}
|
||||
|
||||
}
|
||||
34
app/src/main/java/com/tangem/ui/viewmodel/BaseViewModel.kt
Normal file
34
app/src/main/java/com/tangem/ui/viewmodel/BaseViewModel.kt
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.tangem.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.data.network.exception.Failure
|
||||
|
||||
/**
|
||||
* Base ViewModel class with default Failure handling.
|
||||
* @see ViewModel
|
||||
* @see Failure
|
||||
*/
|
||||
abstract class BaseViewModel : ViewModel() {
|
||||
|
||||
var failure: MutableLiveData<Failure> = MutableLiveData()
|
||||
|
||||
protected fun handleFailure(failure: Failure) {
|
||||
this.failure.value = failure
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.tangem.data.network.exception.Failure
|
||||
|
||||
class LoadedWalletViewModel : BaseViewModel() {
|
||||
|
||||
private val state: MutableLiveData<State> = MutableLiveData()
|
||||
|
||||
fun getState() = state
|
||||
|
||||
enum class State {
|
||||
ServerError,
|
||||
Failed,
|
||||
Success
|
||||
}
|
||||
|
||||
fun connectToken(data: String, data2: String) {
|
||||
|
||||
}
|
||||
|
||||
private fun handleTokensSaveFailure(failure: Failure) {
|
||||
state.value = State.Success
|
||||
handleFailure(failure)
|
||||
}
|
||||
|
||||
private fun handleLoginFailure(failure: Failure) {
|
||||
when (failure) {
|
||||
is Failure.ServerError -> state.value = State.ServerError
|
||||
}
|
||||
handleFailure(failure)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue