Updated on 2026-08-14

This commit is contained in:
Tangem 2019-06-13 17:23:45 +03:00
commit bd8d84a85a
680 changed files with 51751 additions and 6799 deletions

View file

@ -0,0 +1,192 @@
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.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.asBundle
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.CreateNewWalletTask
import com.tangem.card_common.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.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
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(EXTRA_TANGEM_CARD_UID, ctx.card!!.uid)
intent.putExtra(EXTRA_TANGEM_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) {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (sUID == ctx.card.uid) {
if (lastReadSuccess)
isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000
else
isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000
createNewWalletTask = CreateNewWalletTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
createNewWalletTask?.start()
} else
nfcManager.ignoreTag(isoDep.tag)
}
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(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_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(Constant.EXTRA_MESSAGE, getString(R.string.cannot_create_wallet))
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card!!.asBundle)
setResult(Constant.RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
progressBar?.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else
Toast.makeText(this, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
rlProgressBar.postDelayed({ rlProgressBar?.visibility = View.GONE }, 500)
progressBar?.postDelayed({
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar?.post { progressBar?.progress = progress }
}
override fun onReadCancel() {
createNewWalletTask = null
progressBar?.postDelayed({
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
}, 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)
}
}

View file

@ -0,0 +1,259 @@
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.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.asBundle
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.VerifyCardTask
import com.tangem.card_common.util.Util
import com.tangem.util.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
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.di.ToastHelper
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
@Inject
internal lateinit var toastHelper: ToastHelper
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)
App.toastHelperComponent.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(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
intent.putExtra(EXTRA_TANGEM_CARD_UID, ctx.card!!.uid)
intent.putExtra(EXTRA_TANGEM_CARD, ctx.card!!.asBundle)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2)
}
btnDetails.setOnClickListener {
if (cardProtocol != null)
navigator.showVerifyCard(this, ctx)
else
toastHelper.showSingleToast(this, getString(R.string.need_attach_card_again))
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
data.putExtra(Constant.EXTRA_MODIFICATION, "updateAndViewCard")
data.putExtra("updateDelay", 0)
setResult(Activity.RESULT_OK, data)
}
finish()
} else {
if (data != 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(baseContext, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
intent.putExtra(EXTRA_TANGEM_CARD_UID, ctx.card!!.uid)
intent.putExtra(EXTRA_TANGEM_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)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (ctx.card.uid != sUID) {
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()
}
}
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)
}
}

View file

@ -0,0 +1,50 @@
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.di.ToastHelper
import com.tangem.wallet.TangemContext
import com.tangem.ui.fragment.LoadedWalletFragment
import com.tangem.wallet.R
import javax.inject.Inject
class LoadedWalletActivity : AppCompatActivity() {
@Inject
lateinit var navigator: Navigator
@Inject
internal lateinit var toastHelper: ToastHelper
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)
App.toastHelperComponent.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 LoadedWalletFragment
fragment.onTagDiscovered(tag)
}
}
}
}

View file

@ -0,0 +1,77 @@
package com.tangem.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.wallet.TangemContext
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() }
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// set beta version name
if (BuildConfig.DEBUG)
tvAppVersion.text = String.format(getString(R.string.version_name_debug), BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
else
tvAppVersion.text = String.format(getString(R.string.version_name_release), BuildConfig.VERSION_NAME)
// set flavor app name
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
tvExtension.visibility = View.VISIBLE
tvExtension.text = getString(R.string.cardano)
}
else -> {
tvExtension.visibility = View.GONE
}
}
if (intent.getBooleanExtra(Constant.EXTRA_AUTO_HIDE, true))
ivLogo.postDelayed(hideRunnable, Constant.MILLIS_AUTO_HIDE.toLong())
}
private fun hide() {
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
navigator.showPrepareTransaction(this, TangemContext())
}
else -> {
navigator.showMain(this)
}
}
finish()
}
}

View file

@ -0,0 +1,88 @@
@file:Suppress("ObsoleteExperimentalCoroutines")
package com.tangem.ui.activity
import android.Manifest
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.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import androidx.navigation.Navigation
import com.scottyab.rootbeer.RootBeer
import com.tangem.App
import com.tangem.Constant
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.di.Navigator
import com.tangem.di.ToastHelper
import com.tangem.ui.dialog.RootFoundDialog
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
companion object {
val TAG: String = MainActivity::class.java.simpleName
fun callingIntent(context: Context) = Intent(context, MainActivity::class.java)
}
lateinit var navController: NavController
@Inject
internal lateinit var navigator: Navigator
@Inject
internal lateinit var toastHelper: ToastHelper
// 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)
navController = Navigation.findNavController(this, R.id.nav_host_fragment)
App.navigatorComponent.inject(this)
App.toastHelperComponent.inject(this)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
verifyPermissions()
// // 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)
}
private fun verifyPermissions() {
NfcManager.verifyPermissions(this)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), Constant.REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS)
}
}
}

View 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.card_android.android.reader.NfcManager
import com.tangem.data.fingerprint.FingerprintHelper
import com.tangem.wallet.TangemContext
import com.tangem.card_android.android.data.PINStorage
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_common.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(Constant.EXTRA_NEW_PIN, pin)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN, 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(Constant.EXTRA_NEW_PIN_2, pin)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN_2, 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 SettingsFragment")
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(Constant.EXTRA_NEW_PIN)) {
tvPin!!.error = getString(R.string.error_pin_confirmation_failed)
focusView = tvPin
cancel = true
}
} else if (mode == Mode.ConfirmNewPIN2) {
if (pin != intent.getStringExtra(Constant.EXTRA_NEW_PIN_2)) {
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(Constant.EXTRA_NEW_PIN, pin)
if (mode == Mode.ConfirmNewPIN)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN, pin)
setResult(Activity.RESULT_OK, resultData)
finish()
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
val resultData = Intent()
resultData.putExtra(Constant.EXTRA_NEW_PIN_2, pin)
if (mode == Mode.ConfirmNewPIN2)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN_2, 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()
}
}
}
}

View 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.card_android.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
}
}

View file

@ -0,0 +1,204 @@
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.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.*
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.SwapPINTask
import com.tangem.card_common.util.Util
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_swap.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
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 lateinit var card: TangemCard;
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)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
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 {
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(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_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(Constant.EXTRA_MESSAGE, "Cannot change PIN(s). Make sure you enter correct PIN2!")
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_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)
}
}

View file

@ -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.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.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)
}
}
}

View file

@ -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.card_android.android.reader.NfcManager
import com.tangem.data.Blockchain
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.card_android.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)
}
}
}

View file

@ -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.card_android.android.reader.NfcManager
import com.tangem.data.Blockchain
import com.tangem.di.Navigator
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.card_android.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()
}
}
}

View file

@ -0,0 +1,236 @@
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.Constant
import com.tangem.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.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.asBundle
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.PurgeTask
import com.tangem.card_common.util.Util
import com.tangem.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
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
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)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
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 {
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(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_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(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
intent.putExtra(Constant.EXTRA_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)
}
}

View 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()
}
}

View file

@ -0,0 +1,105 @@
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.App
import com.tangem.Constant
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.ui.event.TransactionFinishWithError
import com.tangem.ui.event.TransactionFinishWithSuccess
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_common.util.Util
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) {
App.pendingTransactionsStorage.putTransaction(ctx.card, Util.bytesToHex(tx), engine.pendingTransactionTimeoutInSeconds())
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()
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.tangem.wallet.R
class SettingsActivity : AppCompatActivity() {
companion object {
fun callingIntent(context: Context) = Intent(context, SettingsActivity::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
initToolbar()
}
private fun initToolbar() {
val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setTitle(R.string.settings)
toolbar.setNavigationIcon(android.R.drawable.ic_menu_close_clear_cancel)
setSupportActionBar(toolbar)
toolbar.setNavigationOnClickListener { finish() }
}
}

View file

@ -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.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()
}
}

View file

@ -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()
}
}

View file

@ -0,0 +1,54 @@
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.Constant;
import com.tangem.wallet.R;
public class PINSwapWarningDialog extends DialogFragment {
public static final String TAG = PINSwapWarningDialog.class.getSimpleName();
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);
if (getArguments() != null) {
message = getArguments().getString(Constant.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(@NonNull DialogInterface dialog) {
super.onCancel(dialog);
}
}

View 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()
}
}

View file

@ -0,0 +1,80 @@
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 {
public static final String TAG = ShowQRCodeDialog.class.getSimpleName();
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(), TAG);
});
}
}

View file

@ -0,0 +1,175 @@
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.card_common.util.Log;
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) {
Log.e(TAG, "onReadWait callback(" + msec + ")");
activity.runOnUiThread(() -> {
Log.e(TAG, "onReadWait on ui thread(" + msec + ")");
if (timerToShowDelayDialog != null) {
timerToShowDelayDialog.cancel();
timerToShowDelayDialog = null;
}
if (msec == 0) {
if (instance != null) {
if (instance.isAdded()) {
instance.dismiss();
instance = null;
} else {
Log.e(TAG, "onReadWait(0) with not added dialog");
// instance.progressBar.postDelayed(() -> {
// Log.e(TAG, "onReadWait(0) dismiss delayed");
try {
instance.dismiss();
instance = null;
}
catch (Exception e)
{
e.printStackTrace();
}
// }, 10000);
}
}
return;
}
if (instance == null) {
if (msec > delayBeforeShowDialog + 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);
}
});
}
}

View file

@ -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
}
}
}
}

View file

@ -0,0 +1,3 @@
package com.tangem.ui.event
class DeletingWalletFinish

View file

@ -0,0 +1,3 @@
package com.tangem.ui.event
class ReadAfterRequest

View file

@ -0,0 +1,5 @@
package com.tangem.ui.event
class ReadBeforeRequest {
var timeout: Int? = null
}

View file

@ -0,0 +1,5 @@
package com.tangem.ui.event
class ReadWait {
var msec: Int? = null
}

View file

@ -0,0 +1,5 @@
package com.tangem.ui.event
class TransactionFinishWithError {
var message: String? = null
}

View file

@ -0,0 +1,5 @@
package com.tangem.ui.event
class TransactionFinishWithSuccess {
var message: String? = null
}

View file

@ -0,0 +1,779 @@
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.util.Log
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 androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Blockchain
import com.tangem.data.network.ServerApiCommon
import com.tangem.wallet.BalanceValidator
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
import com.tangem.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.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.VerifyCardTask
import com.tangem.card_common.util.Util
import com.tangem.server_android.ServerApiTangem
import com.tangem.server_android.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 LoadedWalletFragment : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener {
companion object {
val TAG: String = LoadedWalletFragment::class.java.simpleName
}
private lateinit var viewModel: LoadedWalletViewModel
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) {
(activity as LoadedWalletActivity).toastHelper.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)
(activity as LoadedWalletActivity).toastHelper.showSingleToast(context, ctx.message)
else if (ctx.card!!.remainingSignatures == 0)
(activity as LoadedWalletActivity).toastHelper.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
(activity as LoadedWalletActivity).toastHelper.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)
refresh()
startVerify(lastTag)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(LoadedWalletViewModel::class.java)
// set rate info to CoinData
viewModel.getRateInfo().observe(this, Observer<Float> { rate ->
ctx.coinData.rate = rate
ctx.coinData.rateAlter = rate
})
viewModel.requestRateInfo(ctx)
}
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() }, 10000)
}
@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(Constant.EXTRA_MESSAGE, getString(R.string.if_you_forget))
else
bundle.putString(Constant.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(Constant.EXTRA_MESSAGE)) {
ctx.error = data.getStringExtra(Constant.EXTRA_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(Constant.EXTRA_MESSAGE)) {
ctx.error = data.getStringExtra(Constant.EXTRA_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 = ""
}
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
}
}
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, Stellar
if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet ||
ctx.blockchain == Blockchain.Litecoin || ctx.blockchain == Blockchain.BitcoinCash ||
ctx.blockchain == Blockchain.Stellar || ctx.blockchain == Blockchain.StellarTestNet ) {
ctx.coinData.setIsBalanceEqual(true)
}
requestVerifyAndGetInfo()
requestBalanceAndUnspentTransactions()
if (::viewModel.isInitialized)
viewModel.requestRateInfo(ctx)
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 startVerify(tag: Tag?) {
try {
val isoDep = IsoDep.get(tag)
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 = Constant.INTENT_TYPE_TEXT_PLAIN
intent.putExtra(Intent.EXTRA_SUBJECT, Constant.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()
}
}
}

View file

@ -0,0 +1,42 @@
package com.tangem.ui.fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.tangem.data.Blockchain
import com.tangem.data.network.ServerApiCommon
import com.tangem.data.network.ServerApiCommon.RateInfoListener
import com.tangem.data.network.model.RateInfoResponse
import com.tangem.wallet.TangemContext
class LoadedWalletViewModel : ViewModel() {
private var serverApiCommon: ServerApiCommon = ServerApiCommon()
private lateinit var ctx: TangemContext
private val rate: MutableLiveData<Float> by lazy {
MutableLiveData<Float>().also {
loadRateInfo()
}
}
fun getRateInfo(): LiveData<Float> {
return rate
}
fun requestRateInfo(ctx: TangemContext) {
this.ctx = ctx
// TODO - move loadRateInfo to CoinEngine
serverApiCommon.requestRateInfo(ctx.blockchain.currency)
}
private fun loadRateInfo() {
serverApiCommon.setRateInfoListener(object: RateInfoListener{
override fun onSuccess(rateInfoResponse: RateInfoResponse?) {
rate.value = rateInfoResponse!!.data!!.quote!!.usd!!.price
}
override fun onFail(message: String?) {
ctx.error = message
}
})
}
}

View file

@ -0,0 +1,346 @@
package com.tangem.ui.fragment
import android.app.Activity
import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.view.*
import android.widget.PopupMenu
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.tangem.App
import com.tangem.Constant
import com.tangem.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_android.data.saveToBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.CustomReadCardTask
import com.tangem.card_common.tasks.ReadCardInfoTask
import com.tangem.data.Logger
import com.tangem.data.network.ServerApiCommon
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.activity.PinRequestActivity
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.util.CommonUtil
import com.tangem.util.LOG
import com.tangem.util.UtilHelper
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.R
import com.tangem.wallet.TangemContext
import kotlinx.android.synthetic.main.layout_touch_card.*
import kotlinx.android.synthetic.main.main_fragment.*
import java.io.File
import java.util.*
class MainFragment : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener {
companion object {
fun newInstance() = MainFragment()
val TAG: String = MainFragment::class.java.simpleName
}
private lateinit var viewModel: MainViewModel
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
private var unsuccessReadCount = 0
private lateinit var nfcManager: NfcManager
private var task: CustomReadCardTask? = null
private var lastTag: Tag? = null
private var zipFile: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nfcManager = NfcManager(activity!!, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
setHasOptionsMenu(true)
return inflater.inflate(R.layout.main_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rippleBackgroundNfc.startRippleAnimation()
// init NFC Antenna
nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, 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))
// set listeners
fab.setOnClickListener { showMenu(it) }
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// show snackbar about new version app
viewModel.getVersionName().observe(this, Observer { text ->
(activity as MainActivity).toastHelper.showSnackbarUpdateVersion(context!!, cl, text)
})
val intent = activity?.intent
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
onTagDiscovered(tag)
}
}
override fun onResume() {
super.onResume()
nfcDeviceAntenna.animate()
}
override fun onPause() {
task?.cancel(true)
super.onPause()
}
override fun onStop() {
task?.cancel(true)
super.onStop()
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
if (unsuccessReadCount < 2)
isoDep.timeout = 2000 + 5000 * unsuccessReadCount
else
isoDep.timeout = 90000
lastTag = tag
task = ReadCardInfoTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
task?.start()
} catch (e: Exception) {
e.printStackTrace()
nfcManager.notifyReadResult(false)
}
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar?.post { rlProgressBar?.visibility = View.VISIBLE }
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
task = 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()
val bCard = Bundle()
cardInfo.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
cardInfo.putBundle(EXTRA_TANGEM_CARD, bCard)
cardProtocol.card.saveToBundle(bCard)
val uid = cardInfo.getString(EXTRA_TANGEM_CARD_UID)
val card = TangemCard(uid)
cardInfo.getBundle(EXTRA_TANGEM_CARD)?.let { card.loadFromBundle(it) }
val ctx = TangemContext(card)
when {
card.status == TangemCard.Status.Loaded -> lastTag?.let {
val engineCoin = CoinEngineFactory.create(ctx)
if (engineCoin != null) {
engineCoin.defineWallet()
// val bundle = Bundle()
// bundle.putParcelable(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag)
// ctx.saveToBundle(bundle)
// (activity as MainActivity).navController.navigate(R.id.loadedWallet, bundle)
(activity as MainActivity).navigator.showLoadedWallet(activity!!, it, ctx)
}
}
card.status == TangemCard.Status.Empty -> (activity as MainActivity).navigator.showEmptyWallet(activity!!, ctx)
card.status == TangemCard.Status.Purged -> Toast.makeText(context, R.string.erased_wallet, Toast.LENGTH_SHORT).show()
card.status == TangemCard.Status.NotPersonalized -> Toast.makeText(context, R.string.not_personalized, Toast.LENGTH_SHORT).show()
else -> {
// val bundle = Bundle()
// bundle.putParcelable(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag)
// ctx.saveToBundle(bundle)
// navController.navigate(R.id.loadedWallet, bundle)
// lastTag?.let { navigator.showLoadedWallet(this, it, ctx) }
}
}
}
} else {
// remove last UIDs because of error and no card read
rlProgressBar.post {
Toast.makeText(context, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
unsuccessReadCount++
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN)
(activity as MainActivity).navigator.showPinRequest(activity!!, PinRequestActivity.Mode.RequestPIN.toString())
else {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
if (!NoExtendedLengthSupportDialog.allReadyShowed)
NoExtendedLengthSupportDialog().show(activity!!.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() {
task = null
ReadCardInfoTask.resetLastReadInfo()
rlProgressBar.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
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 onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_main, menu)
if (BuildConfig.DEBUG) {
for (i in 0 until menu.size()) menu.getItem(i).isVisible = true
}
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.sendLogs -> {
var f: File? = null
try {
f = Logger.collectLogs(activity)
if (f != null) {
LOG.e(TAG, String.format("Collect %d log bytes", f.length()))
CommonUtil.sendEmail(activity, zipFile, TAG, "Logs", UtilHelper.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 -> {
(activity as MainActivity).navigator.showPinSave(activity!!, false)
// (activity as MainActivity).navController.navigate(R.id.pinSaveActivity)
return true
}
R.id.managePIN2 -> {
(activity as MainActivity).navigator.showPinSave(activity!!, true)
// (activity as MainActivity).navController.navigate(R.id.pinSaveActivity)
return true
}
R.id.settings -> {
(activity as MainActivity).navigator.showSettings(activity!!)
// (activity as MainActivity).navController.navigate(R.id.settingsActivity)
return true
}
R.id.about -> {
(activity as MainActivity).navigator.showLogo(activity!!, false)
// (activity as MainActivity).navController.navigate(R.id.logoActivity)
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(Objects.requireNonNull(activity) as AppCompatActivity?, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(Objects.requireNonNull(activity) as AppCompatActivity?, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(Objects.requireNonNull(activity))
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
return onOptionsItemSelected(item!!)
}
private fun showMenu(v: View) {
val popup = PopupMenu(context, 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()
}
}

View file

@ -0,0 +1,38 @@
package com.tangem.ui.fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.tangem.Constant
import com.tangem.data.network.ServerApiCommon
import com.tangem.wallet.BuildConfig
class MainViewModel : ViewModel() {
private var serverApiCommon: ServerApiCommon = ServerApiCommon()
private var versionName = MutableLiveData<String>()
fun getVersionName(): LiveData<String> {
versionName = MutableLiveData()
requestVersionName()
return versionName
}
private fun requestVersionName() {
serverApiCommon.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())
if (BuildConfig.FLAVOR.equals(Constant.FLAVOR_TANGEM_ACCESS))
versionName.value = responseVersionName
} catch (E: Exception) {
E.printStackTrace()
}
}
serverApiCommon.requestLastVersion()
}
}

View file

@ -0,0 +1,25 @@
package com.tangem.ui.fragment
import android.os.Bundle
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_main)
val categoryCommon = findPreference<PreferenceCategory>(getString(R.string.pref_category_common))
val selectNodes = findPreference<Preference>(getString(R.string.pref_select_nodes))
val encryptionModes = findPreference<ListPreference>(getString(R.string.pref_encryption_modes))
if (!BuildConfig.DEBUG) {
categoryCommon?.removePreference(selectNodes)
categoryCommon?.removePreference(encryptionModes)
}
}
}

View file

@ -0,0 +1,497 @@
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 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 androidx.core.content.ContextCompat
import com.tangem.App
import com.tangem.Constant
import com.tangem.card_android.android.data.PINStorage
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.data.Blockchain
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.ui.activity.PinRequestActivity
import com.tangem.ui.activity.VerifyCardActivity
import com.tangem.ui.dialog.PINSwapWarningDialog
import com.tangem.ui.event.DeletingWalletFinish
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, getString(R.string.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)
when (requestCode) {
Constant.REQUEST_CODE_ENTER_NEW_PIN -> if (resultCode == Activity.RESULT_OK) {
if (data != null) {
if (data.extras!!.containsKey(Constant.EXTRA_CONFIRM_PIN)) {
val intent = Intent(context, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
ctx.saveToIntent(intent)
newPIN = data.getStringExtra(Constant.EXTRA_NEW_PIN)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
} else {
val intent = Intent(context, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_NEW_PIN, data.getStringExtra(Constant.EXTRA_NEW_PIN))
intent.putExtra(Constant.EXTRA_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(Constant.EXTRA_CONFIRM_PIN_2)) {
val intent = Intent(context, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
ctx.saveToIntent(intent)
newPIN2 = data.getStringExtra(Constant.EXTRA_NEW_PIN_2)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN)
} else {
val intent = Intent(context, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_NEW_PIN_2, data.getStringExtra(Constant.EXTRA_NEW_PIN_2))
intent.putExtra(Constant.EXTRA_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(Constant.EXTRA_MESSAGE, getString(R.string.if_you_forget))
else
bundle.putString(Constant.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!!.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(context, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_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(Constant.EXTRA_MESSAGE)) {
ctx.error = data.getStringExtra(Constant.EXTRA_MESSAGE)
}
}
}
Constant.REQUEST_CODE_REQUEST_PIN2_FOR_PURGE -> if (resultCode == Activity.RESULT_OK)
(activity as VerifyCardActivity).navigator.showPurge(context as Activity, ctx)
else {
if (data != 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(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(Constant.EXTRA_MESSAGE)) {
ctx.error = data.getStringExtra(Constant.EXTRA_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)
var s=""
for(signingM in ctx.card!!.allowedSigningMethod) {
s += if(signingM==ctx.card!!.signingMethod) {
", <b>${signingM.description}</b>"
} else {
", <small>${signingM.description}</small>"
}
}
s=s.substring(2,s.length)
@Suppress("DEPRECATION")
tvSigningMethod.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(s, Html.FROM_HTML_MODE_LEGACY)
else
Html.fromHtml(s)
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(Constant.EXTRA_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(Constant.EXTRA_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(Constant.EXTRA_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(Constant.EXTRA_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(Constant.EXTRA_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(Constant.EXTRA_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()
}
}