426 lines
No EOL
16 KiB
Kotlin
426 lines
No EOL
16 KiB
Kotlin
package com.tangem.presentation.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.support.v4.app.ActivityCompat
|
|
import android.support.v4.content.ContextCompat
|
|
import android.support.v7.app.AppCompatActivity
|
|
import android.support.v7.widget.PopupMenu
|
|
import android.util.Log
|
|
import android.view.Menu
|
|
import android.view.MenuItem
|
|
import android.view.View
|
|
import android.view.animation.Animation
|
|
import android.view.animation.DecelerateInterpolator
|
|
import android.view.animation.Transformation
|
|
import android.widget.RelativeLayout
|
|
import android.widget.Toast
|
|
import com.scottyab.rootbeer.RootBeer
|
|
import com.tangem.App
|
|
import com.tangem.Constant
|
|
import com.tangem.data.Logger
|
|
import com.tangem.data.network.ServerApiCommon
|
|
import com.tangem.tangemcard.android.nfc.DeviceNFCAntennaLocation
|
|
import com.tangem.tangemcard.tasks.ReadCardInfoTask
|
|
import com.tangem.di.Navigator
|
|
import com.tangem.domain.wallet.CoinEngineFactory
|
|
import com.tangem.domain.wallet.TangemContext
|
|
import com.tangem.tangemcard.reader.CardProtocol
|
|
import com.tangem.tangemcard.android.reader.NfcManager
|
|
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
|
import com.tangem.presentation.dialog.RootFoundDialog
|
|
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
|
import com.tangem.presentation.dialog.WaitSecurityDelayDialogNew
|
|
import com.tangem.tangemcard.android.reader.NfcReader
|
|
import com.tangem.tangemcard.data.TangemCard
|
|
import com.tangem.tangemcard.data.loadFromBundle
|
|
import com.tangem.tangemcard.data.saveToBundle
|
|
import com.tangem.util.CommonUtil
|
|
import com.tangem.util.LOG
|
|
import com.tangem.util.PhoneUtility
|
|
import com.tangem.wallet.BuildConfig
|
|
import com.tangem.wallet.R
|
|
import kotlinx.android.synthetic.main.activity_main.*
|
|
import java.io.File
|
|
import java.util.*
|
|
import javax.inject.Inject
|
|
|
|
class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, PopupMenu.OnMenuItemClickListener {
|
|
|
|
companion object {
|
|
val TAG: String = MainActivity::class.java.simpleName
|
|
|
|
fun callingIntent(context: Context) = Intent(context, MainActivity::class.java)
|
|
}
|
|
|
|
private lateinit var nfcManager: NfcManager
|
|
private var zipFile: File? = null
|
|
private var antenna: DeviceNFCAntennaLocation? = null
|
|
private var unsuccessReadCount = 0
|
|
private var lastTag: Tag? = null
|
|
private var readCardInfoTask: ReadCardInfoTask? = null
|
|
private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
|
|
|
|
@Inject
|
|
internal lateinit var navigator: Navigator
|
|
|
|
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)
|
|
|
|
// val waitSecurityDelayDialogNew = WaitSecurityDelayDialogNew()
|
|
// waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG)
|
|
|
|
App.getNavigatorComponent().inject(this)
|
|
|
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
|
|
|
nfcManager = NfcManager(this, this)
|
|
|
|
verifyPermissions()
|
|
|
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
|
|
|
setNfcAdapterReaderCallback(this)
|
|
|
|
rippleBackgroundNfc.startRippleAnimation()
|
|
|
|
antenna = DeviceNFCAntennaLocation()
|
|
antenna!!.getAntennaLocation()
|
|
|
|
// set card orientation
|
|
when (antenna!!.orientation) {
|
|
DeviceNFCAntennaLocation.CARD_ORIENTATION_HORIZONTAL -> {
|
|
ivHandCardHorizontal.visibility = View.VISIBLE
|
|
ivHandCardVertical.visibility = View.GONE
|
|
}
|
|
|
|
DeviceNFCAntennaLocation.CARD_ORIENTATION_VERTICAL -> {
|
|
ivHandCardVertical.visibility = View.VISIBLE
|
|
ivHandCardHorizontal.visibility = View.GONE
|
|
}
|
|
}
|
|
|
|
// set card z position
|
|
when (antenna!!.z) {
|
|
DeviceNFCAntennaLocation.CARD_ON_BACK -> llHand.elevation = 0.0f
|
|
DeviceNFCAntennaLocation.CARD_ON_FRONT -> llHand.elevation = 30.0f
|
|
}
|
|
|
|
// set phone name
|
|
if (antenna!!.fullName != "")
|
|
tvNFCHint.text = String.format(getString(R.string.scan_banknote), antenna!!.fullName)
|
|
else
|
|
tvNFCHint.text = String.format(getString(R.string.scan_banknote), getString(R.string.phone))
|
|
|
|
animate()
|
|
|
|
// NFC
|
|
val intent = intent
|
|
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
|
|
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
|
|
if (tag != null && onNfcReaderCallback != null) {
|
|
onNfcReaderCallback!!.onTagDiscovered(tag)
|
|
}
|
|
}
|
|
|
|
// check if root device
|
|
val rootBeer = RootBeer(this)
|
|
if (rootBeer.isRootedWithoutBusyBoxCheck && !BuildConfig.DEBUG)
|
|
RootFoundDialog().show(supportFragmentManager, RootFoundDialog.TAG)
|
|
|
|
// set listeners
|
|
fab.setOnClickListener { showMenu(it) }
|
|
|
|
val apiHelper = ServerApiCommon()
|
|
apiHelper.setLastVersionListener { response ->
|
|
try {
|
|
if (response.isNullOrEmpty()) return@setLastVersionListener
|
|
val responseVersionName = response.trim(' ', '\n', '\r', '\t')
|
|
val responseBuildVersion = responseVersionName.split('.').last()
|
|
val appBuildVersion = BuildConfig.VERSION_NAME.split('.').last()
|
|
if (responseBuildVersion.toInt() > appBuildVersion.toInt()) Toast.makeText(this, "There is a new application version: $responseVersionName", Toast.LENGTH_LONG).show()
|
|
} catch (E: Exception) {
|
|
E.printStackTrace()
|
|
}
|
|
}
|
|
apiHelper.requestLastVersion()
|
|
}
|
|
|
|
private fun verifyPermissions() {
|
|
NfcManager.verifyPermissions(this)
|
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
|
Log.e("QRScanActivity", "User hasn't granted permission to use camera")
|
|
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), Constant.REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS)
|
|
}
|
|
}
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
when (requestCode) {
|
|
Constant.REQUEST_CODE_SEND_EMAIL -> {
|
|
if (zipFile != null) {
|
|
zipFile!!.delete()
|
|
zipFile = null
|
|
}
|
|
}
|
|
Constant.REQUEST_CODE_ENTER_PIN_ACTIVITY -> {
|
|
if (resultCode == Activity.RESULT_OK && lastTag != null)
|
|
onTagDiscovered(lastTag!!)
|
|
else
|
|
ReadCardInfoTask.resetLastReadInfo()
|
|
}
|
|
}
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
}
|
|
|
|
override fun onMenuItemClick(item: MenuItem): Boolean {
|
|
return onOptionsItemSelected(item)
|
|
}
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
menuInflater.inflate(R.menu.menu_main, menu)
|
|
if (BuildConfig.DEBUG) {
|
|
for (i in 0 until menu.size()) menu.getItem(i).isVisible = true
|
|
}
|
|
return true
|
|
}
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
val id = item.itemId
|
|
when (id) {
|
|
R.id.sendLogs -> {
|
|
var f: File? = null
|
|
try {
|
|
f = Logger.collectLogs(this)
|
|
if (f != null) {
|
|
LOG.e(TAG, String.format("Collect %d log bytes", f.length()))
|
|
CommonUtil.sendEmail(this, zipFile, TAG, "Logs", PhoneUtility.getDeviceInfo(), arrayOf(f))
|
|
} else {
|
|
LOG.e(TAG, "Can't create temporarily log file")
|
|
}
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
} finally {
|
|
if (f != null && f.exists())
|
|
f.delete()
|
|
}
|
|
return true
|
|
}
|
|
R.id.managePIN -> {
|
|
navigator.showPinSave(this, false)
|
|
return true
|
|
}
|
|
|
|
R.id.managePIN2 -> {
|
|
navigator.showPinSave(this, true)
|
|
return true
|
|
}
|
|
|
|
R.id.about -> {
|
|
navigator.showLogo(this, false)
|
|
return true
|
|
}
|
|
}
|
|
return super.onOptionsItemSelected(item)
|
|
}
|
|
|
|
override fun onTagDiscovered(tag: Tag) {
|
|
try {
|
|
// get IsoDep handle and run cardReader thread
|
|
val isoDep = IsoDep.get(tag)
|
|
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
|
|
|
|
LOG.e(TAG, "setTimeout(" + (1000 + 3000 * unsuccessReadCount) + ")")
|
|
if (unsuccessReadCount < 2) {
|
|
isoDep.timeout = 2000 + 5000 * unsuccessReadCount
|
|
} else {
|
|
isoDep.timeout = 90000
|
|
}
|
|
lastTag = tag
|
|
|
|
readCardInfoTask = ReadCardInfoTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
|
|
readCardInfoTask!!.start()
|
|
|
|
LOG.i(TAG, "onTagDiscovered " + Arrays.toString(tag.id))
|
|
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
nfcManager.notifyReadResult(false)
|
|
}
|
|
}
|
|
|
|
public override fun onResume() {
|
|
super.onResume()
|
|
animate()
|
|
ReadCardInfoTask.resetLastReadInfo()
|
|
nfcManager.onResume()
|
|
}
|
|
|
|
public override fun onPause() {
|
|
nfcManager.onPause()
|
|
readCardInfoTask?.cancel(true)
|
|
super.onPause()
|
|
}
|
|
|
|
public override fun onStop() {
|
|
// dismiss enable NFC dialog
|
|
nfcManager.onStop()
|
|
readCardInfoTask?.cancel(true)
|
|
super.onStop()
|
|
}
|
|
|
|
override fun onReadStart(cardProtocol: CardProtocol) {
|
|
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
|
|
}
|
|
|
|
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
|
|
|
}
|
|
|
|
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
|
readCardInfoTask = null
|
|
if (cardProtocol != null) {
|
|
if (cardProtocol.error == null) {
|
|
nfcManager.notifyReadResult(true)
|
|
rlProgressBar.post {
|
|
rlProgressBar.visibility = View.GONE
|
|
|
|
// TODO - ??? remove save and load???
|
|
val cardInfo = Bundle()
|
|
cardInfo.putString("UID", cardProtocol.card.uid)
|
|
val bCard = Bundle()
|
|
cardProtocol.card.saveToBundle(bCard)
|
|
cardInfo.putBundle("Card", bCard)
|
|
|
|
val uid = cardInfo.getString("UID")
|
|
val card = TangemCard(uid)
|
|
card.loadFromBundle(cardInfo.getBundle("Card"))
|
|
|
|
val ctx = TangemContext(card)
|
|
when {
|
|
card.status == TangemCard.Status.Loaded -> lastTag?.let {
|
|
val engineCoin = CoinEngineFactory.create(ctx)
|
|
?: throw CardProtocol.TangemException("Can't create CoinEngine!")
|
|
engineCoin.defineWallet()
|
|
//mCard.setWallet(Blockchain.calculateWalletAddress(mCard, pkUncompressed));
|
|
|
|
navigator.showLoadedWallet(this, it, ctx)
|
|
}
|
|
card.status == TangemCard.Status.Empty -> navigator.showEmptyWallet(this, ctx)
|
|
card.status == TangemCard.Status.Purged -> Toast.makeText(this, R.string.erased_wallet, Toast.LENGTH_SHORT).show()
|
|
card.status == TangemCard.Status.NotPersonalized -> Toast.makeText(this, R.string.not_personalized, Toast.LENGTH_SHORT).show()
|
|
else -> lastTag?.let { navigator.showLoadedWallet(this, it, ctx) }
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// remove last UIDs because of error and no card read
|
|
rlProgressBar.post {
|
|
Toast.makeText(this, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
|
|
unsuccessReadCount++
|
|
|
|
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN)
|
|
navigator.showPinRequest(this, PinRequestActivity.Mode.RequestPIN.toString())
|
|
else {
|
|
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
|
|
if (!NoExtendedLengthSupportDialog.allReadyShowed)
|
|
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
|
|
|
|
lastTag = null
|
|
ReadCardInfoTask.resetLastReadInfo()
|
|
nfcManager.notifyReadResult(false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rlProgressBar.postDelayed({
|
|
try {
|
|
rlProgressBar.visibility = View.GONE
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}, 500)
|
|
}
|
|
|
|
override fun onReadCancel() {
|
|
readCardInfoTask = null
|
|
ReadCardInfoTask.resetLastReadInfo()
|
|
rlProgressBar.postDelayed({
|
|
try {
|
|
rlProgressBar.visibility = View.GONE
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}, 500)
|
|
}
|
|
|
|
override fun onReadWait(msec: Int) {
|
|
WaitSecurityDelayDialog.onReadWait(Objects.requireNonNull(this), msec)
|
|
}
|
|
|
|
override fun onReadBeforeRequest(timeout: Int) {
|
|
WaitSecurityDelayDialog.onReadBeforeRequest(Objects.requireNonNull(this), timeout)
|
|
}
|
|
|
|
override fun onReadAfterRequest() {
|
|
WaitSecurityDelayDialog.onReadAfterRequest(Objects.requireNonNull(this))
|
|
}
|
|
|
|
private fun setNfcAdapterReaderCallback(callback: NfcAdapter.ReaderCallback) {
|
|
onNfcReaderCallback = callback
|
|
}
|
|
|
|
private fun animate() {
|
|
val lp = llHand.layoutParams as RelativeLayout.LayoutParams
|
|
val lp2 = llNfc.layoutParams as RelativeLayout.LayoutParams
|
|
val dp = resources.displayMetrics.density
|
|
val lm = dp * (69 + antenna!!.x * 75)
|
|
lp.topMargin = (dp * (-100 + antenna!!.y * 250)).toInt()
|
|
lp2.topMargin = (dp * (-125 + antenna!!.y * 250)).toInt()
|
|
llNfc.layoutParams = lp2
|
|
|
|
val a = object : Animation() {
|
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
|
lp.leftMargin = (lm * interpolatedTime).toInt()
|
|
llHand.layoutParams = lp
|
|
}
|
|
}
|
|
a.duration = 2000
|
|
a.interpolator = DecelerateInterpolator()
|
|
llHand.startAnimation(a)
|
|
}
|
|
|
|
private fun showMenu(v: View) {
|
|
val popup = PopupMenu(this, v)
|
|
val inflater = popup.menuInflater
|
|
inflater.inflate(R.menu.menu_main, popup.menu)
|
|
|
|
if (BuildConfig.DEBUG) {
|
|
popup.menu.findItem(R.id.managePIN).isEnabled = true
|
|
popup.menu.findItem(R.id.managePIN2).isEnabled = true
|
|
popup.menu.findItem(R.id.sendLogs).isVisible = true
|
|
}
|
|
|
|
popup.setOnMenuItemClickListener(this)
|
|
popup.show()
|
|
}
|
|
|
|
} |