Updated on 2026-08-14

This commit is contained in:
Tangem 2019-05-08 13:11:33 +03:00
commit 028a57b6cf
34 changed files with 786 additions and 538 deletions

View file

@ -26,7 +26,7 @@ object Constant {
const val INTENT_TYPE_TEXT_PLAIN = "text/plain"
// LoadedWallet, VerifyCard
// LoadedWalletFragment, VerifyCard
const val REQUEST_CODE_SEND_TRANSACTION = 1
const val REQUEST_CODE_PURGE = 2
const val REQUEST_CODE_REQUEST_PIN2_FOR_PURGE = 3

View file

@ -45,6 +45,13 @@ class ToastHelper {
snackbar.show()
}
fun showSnackbarWarning(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_LONG)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_err))
snackbar.show()
}
private var singleToast: Toast? = null
private var showTime: Date = Date()

View file

@ -1,5 +1,6 @@
package com.tangem.di
import com.tangem.ui.ConfirmTransactionActivity
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.EmptyWalletActivity
import com.tangem.ui.activity.LoadedWalletActivity
@ -15,6 +16,8 @@ interface ToastHelperComponent {
fun inject(activity: LoadedWalletActivity)
fun inject(activity: ConfirmTransactionActivity)
fun inject(activity: PrepareTransactionActivity)
fun inject(activity: EmptyWalletActivity)

View file

@ -11,7 +11,7 @@ import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.di.ToastHelper
import com.tangem.wallet.TangemContext
import com.tangem.ui.fragment.LoadedWallet
import com.tangem.ui.fragment.LoadedWalletFragment
import com.tangem.wallet.R
import javax.inject.Inject
@ -41,7 +41,7 @@ class LoadedWalletActivity : AppCompatActivity() {
if (intent.extras!!.containsKey(NfcAdapter.EXTRA_TAG)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag != null) {
val fragment = supportFragmentManager.findFragmentById(R.id.loaded_wallet_fragment) as LoadedWallet
val fragment = supportFragmentManager.findFragmentById(R.id.loaded_wallet_fragment) as LoadedWalletFragment
fragment.onTagDiscovered(tag)
}
}

View file

@ -3,151 +3,79 @@
package com.tangem.ui.activity
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
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.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.di.Navigator
import com.tangem.di.ToastHelper
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.RootFoundDialog
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.R
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.layout_touch_card.*
import java.io.File
import java.util.*
import javax.inject.Inject
class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, PopupMenu.OnMenuItemClickListener {
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 lateinit var nfcManager: NfcManager
// private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
private var zipFile: File? = null
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
private var unsuccessReadCount = 0
private var lastTag: Tag? = null
private var task: CustomReadCardTask? = null
private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag != null && onNfcReaderCallback != null)
onNfcReaderCallback!!.onTagDiscovered(tag)
}
}
// override fun 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
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
verifyPermissions()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
setNfcAdapterReaderCallback(this)
rippleBackgroundNfc.startRippleAnimation()
// init NFC Antenna
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
nfcDeviceAntenna.init()
// set phone name
if (nfcDeviceAntenna.fullName != "")
tvNFCHint.text = String.format(getString(R.string.scan_banknote), nfcDeviceAntenna.fullName)
else
tvNFCHint.text = String.format(getString(R.string.scan_banknote), getString(R.string.phone))
// NFC
val intent = intent
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag != null && onNfcReaderCallback != null) {
onNfcReaderCallback?.onTagDiscovered(tag)
}
}
// // NFC
// val intent = intent
// if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
// val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
// if (tag != null && onNfcReaderCallback != null) {
// onNfcReaderCallback?.onTagDiscovered(tag)
// }
// }
// check if root device
val rootBeer = RootBeer(this)
if (rootBeer.isRootedWithoutBusyBoxCheck && !BuildConfig.DEBUG)
RootFoundDialog().show(supportFragmentManager, RootFoundDialog.TAG)
// set listeners
fab.setOnClickListener { showMenu(it) }
val apiHelper = ServerApiCommon()
apiHelper.setLastVersionListener { response ->
try {
if (response.isNullOrEmpty())
return@setLastVersionListener
val responseVersionName = response.trim(' ', '\n', '\r', '\t')
val responseBuildVersion = responseVersionName.split('.').last()
val appBuildVersion = BuildConfig.VERSION_NAME.split('.').last()
if (responseBuildVersion.toInt() > appBuildVersion.toInt())
if (BuildConfig.FLAVOR.equals(Constant.FLAVOR_TANGEM_ACCESS))
toastHelper.showSnackbarUpdateVersion(this, cl, responseVersionName)
} catch (E: Exception) {
E.printStackTrace()
}
}
apiHelper.requestLastVersion()
}
private fun verifyPermissions() {
@ -157,231 +85,4 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
Constant.REQUEST_CODE_SEND_EMAIL -> {
if (zipFile != null) {
zipFile!!.delete()
zipFile = null
}
}
Constant.REQUEST_CODE_ENTER_PIN_ACTIVITY -> {
if (resultCode == Activity.RESULT_OK && lastTag != null)
onTagDiscovered(lastTag!!)
else
ReadCardInfoTask.resetLastReadInfo()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onMenuItemClick(item: MenuItem): Boolean {
return onOptionsItemSelected(item)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
if (BuildConfig.DEBUG) {
for (i in 0 until menu.size()) menu.getItem(i).isVisible = true
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
when (id) {
R.id.sendLogs -> {
var f: File? = null
try {
f = Logger.collectLogs(this)
if (f != null) {
LOG.e(TAG, String.format("Collect %d log bytes", f.length()))
CommonUtil.sendEmail(this, zipFile, TAG, "Logs", 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 -> {
navigator.showPinSave(this, false)
return true
}
R.id.managePIN2 -> {
navigator.showPinSave(this, true)
return true
}
R.id.settings -> {
navigator.showSettings(this)
return true
}
R.id.about -> {
navigator.showLogo(this, false)
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
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)
}
}
public override fun onResume() {
super.onResume()
nfcDeviceAntenna.animate()
ReadCardInfoTask.resetLastReadInfo()
}
public override fun onPause() {
task?.cancel(true)
super.onPause()
}
public override fun onStop() {
task?.cancel(true)
super.onStop()
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
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()
navigator.showLoadedWallet(this, it, ctx)
}
}
card.status == TangemCard.Status.Empty -> navigator.showEmptyWallet(this, ctx)
card.status == TangemCard.Status.Purged -> Toast.makeText(this, R.string.erased_wallet, Toast.LENGTH_SHORT).show()
card.status == TangemCard.Status.NotPersonalized -> Toast.makeText(this, R.string.not_personalized, Toast.LENGTH_SHORT).show()
else -> lastTag?.let { navigator.showLoadedWallet(this, it, ctx) }
}
}
} else {
// remove last UIDs because of error and no card read
rlProgressBar.post {
Toast.makeText(this, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
unsuccessReadCount++
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN)
navigator.showPinRequest(this, PinRequestActivity.Mode.RequestPIN.toString())
else {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
if (!NoExtendedLengthSupportDialog.allReadyShowed)
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
lastTag = null
ReadCardInfoTask.resetLastReadInfo()
nfcManager.notifyReadResult(false)
}
}
}
}
rlProgressBar.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadCancel() {
task = null
ReadCardInfoTask.resetLastReadInfo()
rlProgressBar.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(Objects.requireNonNull(this), msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(Objects.requireNonNull(this), timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(Objects.requireNonNull(this))
}
private fun setNfcAdapterReaderCallback(callback: NfcAdapter.ReaderCallback) {
onNfcReaderCallback = callback
}
private fun showMenu(v: View) {
val popup = PopupMenu(this, v)
val inflater = popup.menuInflater
inflater.inflate(R.menu.menu_main, popup.menu)
if (BuildConfig.DEBUG) {
popup.menu.findItem(R.id.managePIN).isEnabled = true
popup.menu.findItem(R.id.managePIN2).isEnabled = true
popup.menu.findItem(R.id.sendLogs).isVisible = true
popup.menu.findItem(R.id.settings).isVisible = true
}
popup.setOnMenuItemClickListener(this)
popup.show()
}
}

View file

@ -246,7 +246,7 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
@SuppressLint("NewApi")
private fun testFingerPrintSettings(): Boolean {
LOG.i(TAG, "Testing Fingerprint Settings")
LOG.i(TAG, "Testing Fingerprint SettingsFragment")
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
fingerprintManager = getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager

View file

@ -13,6 +13,7 @@ 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
@ -20,6 +21,8 @@ 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
@ -63,11 +66,12 @@ import java.io.InputStream
import java.util.*
import kotlin.concurrent.timerTask
class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener {
class LoadedWalletFragment : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener {
companion object {
val TAG: String = LoadedWallet::class.java.simpleName
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
@ -274,19 +278,23 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback
}
serverApiTangem.setArtworkListener(artworkListener)
// request rate info listener
serverApiCommon.setRateInfoListener {
if (activity == null || !UtilHelper.isOnline(activity!!)) return@setRateInfoListener
val rate = it.priceUsd.toFloat()
ctx.coinData!!.rate = rate
ctx.coinData!!.rateAlter = rate
}
refresh()
startVerify(lastTag)
}
override fun 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) {
@ -647,7 +655,8 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback
requestBalanceAndUnspentTransactions()
requestRateInfo()
if (::viewModel.isInitialized)
viewModel.requestRateInfo(ctx)
if (requestCounter == 0) {
// if no connection and no requests posted
@ -709,37 +718,6 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback
}
}
private fun requestRateInfo() {
if (UtilHelper.isOnline(context as Activity)) {
LOG.i(TAG, "requestRateInfo")
// TODO - move requestRateInfo to CoinEngine
val cryptoId: String = when (ctx.blockchain) {
Blockchain.Bitcoin -> "bitcoin"
Blockchain.BitcoinTestNet -> "bitcoin"
Blockchain.Ethereum -> "ethereum"
Blockchain.EthereumTestNet -> "ethereum"
Blockchain.Token -> "ethereum"
Blockchain.NftToken -> "ethereum"
Blockchain.BitcoinCash -> "bitcoin-cash"
Blockchain.Litecoin -> "litecoin"
Blockchain.Rootstock -> "bitcoin"
Blockchain.RootstockToken -> "bitcoin"
Blockchain.Cardano -> "cardano"
Blockchain.Ripple -> "ripple"
Blockchain.Binance -> "binance-coin"
Blockchain.BinanceTestNet -> "binance-coin"
else -> {
throw Exception("Can''t get rate for blockchain " + ctx.blockchainName)
}
}
serverApiCommon.requestRateInfo(cryptoId)
} else {
ctx.error = getString(R.string.no_connection)
updateViews()
}
}
private fun startVerify(tag: Tag?) {
try {
val isoDep = IsoDep.get(tag)

View file

@ -0,0 +1,52 @@
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.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
val cryptoId: String = when (ctx.blockchain) {
Blockchain.Bitcoin -> "bitcoin"
Blockchain.BitcoinTestNet -> "bitcoin"
Blockchain.Ethereum -> "ethereum"
Blockchain.EthereumTestNet -> "ethereum"
Blockchain.Token -> "ethereum"
Blockchain.NftToken -> "ethereum"
Blockchain.BitcoinCash -> "bitcoin-cash"
Blockchain.Litecoin -> "litecoin"
Blockchain.Rootstock -> "bitcoin"
Blockchain.RootstockToken -> "bitcoin"
Blockchain.Cardano -> "cardano"
Blockchain.Ripple -> "ripple"
else -> {
throw Exception("Can''t get rate for blockchain " + ctx.blockchainName)
}
}
serverApiCommon.requestRateInfo(cryptoId)
}
private fun loadRateInfo() {
serverApiCommon.setRateInfoListener {
val rate = it.priceUsd.toFloat()
this.rate.value = rate
}
}
}

View file

@ -0,0 +1,340 @@
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)
})
}
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

@ -1,12 +0,0 @@
package com.tangem.ui.fragment
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import com.tangem.wallet.R
class Settings : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_main)
}
}

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

@ -22,6 +22,7 @@ public class TangemContext {
}
public TangemContext(TangemCard card) {
this.card = card;
}
@ -135,6 +136,21 @@ public class TangemContext {
return tangemContext;
}
public void saveToBundle(Bundle intent) {
if (card != null) {
intent.putString(TangemCardExtensionsKt.EXTRA_TANGEM_CARD_UID, card.getUID());
intent.putBundle(TangemCardExtensionsKt.EXTRA_TANGEM_CARD, TangemCardExtensionsKt.getAsBundle(card));
}
if (coinData != null) {
intent.putBundle(Constant.EXTRA_BLOCKCHAIN_DATA, coinData.asBundle());
}
intent.putString("Error", error);
intent.putString("Message", message);
}
public void saveToIntent(Intent intent) {
if (card != null) {
@ -148,7 +164,6 @@ public class TangemContext {
intent.putExtra("Error", error);
intent.putExtra("Message", message);
}
public String getString(int stringId) {