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

31
.idea/navEditor.xml generated Normal file
View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="navEditor-manualLayoutAlgorithm2">
<option name="myPositions">
<map>
<entry key="main_graph.xml">
<value>
<LayoutPositions>
<option name="myPositions">
<map>
<entry key="main">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="-203" />
<option name="y" value="-292" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</component>
</project>

View file

@ -30,7 +30,6 @@ android {
debuggable true
minifyEnabled false
applicationIdSuffix ".debug"
versionNameSuffix '-DEBUG'
signingConfig signingConfigs.debug
}
}
@ -67,12 +66,15 @@ dependencies {
// implementation 'com.github.TangemCash:card-common:0.1.2'
// implementation 'com.github.TangemCash:card-android:0.1.3'
// implementation 'com.github.TangemCash:server-android:0.1.4'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
implementation "androidx.preference:preference:1.1.0-alpha04"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'android.arch.navigation:navigation-fragment:1.0.0'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.preference:preference:1.1.0-alpha04"
implementation 'androidx.biometric:biometric:1.0.0-alpha04'
implementation 'co.nstant.in:cbor:0.8'
implementation 'com.google.android.material:material:1.1.0-alpha05'
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
@ -90,6 +92,7 @@ dependencies {
implementation 'com.skyfishjy.ripplebackground:library:1.0.1'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation "com.orhanobut:hawk:2.0.1"
implementation 'co.nstant.in:cbor:0.8'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--<string name="app_name">DEBUG</string>-->
<string name="app_name">DEBUG</string>
</resources>

View file

@ -29,12 +29,13 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<!--<provider-->
<!--android:name="com.tangem.data.LogFileProvider"-->
<!--android:authorities="@string/log_file_provider_authorities"-->
<!--android:enabled="true"-->
<!--android:exported="false"-->
<!--android:grantUriPermissions="true" />-->
<provider
android:name="com.tangem.data.LogFileProvider"
android:authorities="@string/log_file_provider_authorities"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true" />
<activity
android:name="com.tangem.ui.activity.LogoActivity"

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) {

View file

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.LoadedWalletActivity">
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/loaded_wallet_fragment"
android:name="com.tangem.ui.fragment.LoadedWallet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fr_loaded_wallet"/>
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.LoadedWalletActivity">
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/loaded_wallet_fragment"
android:name="com.tangem.ui.fragment.LoadedWalletFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fr_loaded_wallet"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,58 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.MainActivity"
tools:ignore="contentDescription">
<LinearLayout
android:id="@+id/llTapPrompt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/actionbar_text"
android:gravity="center_vertical|center_horizontal"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="30dp"
app:srcCompat="@drawable/tangem_logo_full_new" />
<include layout="@layout/layout_touch_card" />
<TextView
android:id="@+id/tvNFCHint"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="12dp"
android:fontFamily="@font/maax"
android:gravity="center"
android:lineSpacingExtra="8dp"
android:minHeight="200dp"
android:paddingLeft="20dp"
android:paddingTop="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp"
android:text="@string/tap_tangem_card"
android:textAlignment="center"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/primary_dark"
android:textSize="@dimen/text_size_medium" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="10dp"
android:src="@drawable/ic_show_menu"
app:backgroundTint="@color/colorPrimary"
app:fabSize="normal" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.MainActivity">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/main_graph" />
</FrameLayout>

View file

@ -20,7 +20,7 @@
<fragment
android:id="@+id/thanks_fragment"
android:name="com.tangem.ui.fragment.Settings"
android:name="com.tangem.ui.fragment.SettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/abl"

View file

@ -7,7 +7,7 @@
android:background="@color/white"
android:orientation="vertical"
android:padding="0dp"
tools:context="com.tangem.ui.fragment.LoadedWallet"
tools:context="com.tangem.ui.fragment.LoadedWalletFragment"
tools:ignore="contentDescription"
tools:showIn="@layout/activity_loaded_wallet">

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.activity.MainActivity"
tools:ignore="contentDescription">
<LinearLayout
android:id="@+id/llTapPrompt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/actionbar_text"
android:gravity="center_vertical|center_horizontal"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="30dp"
app:srcCompat="@drawable/tangem_logo_full_new" />
<include layout="@layout/layout_touch_card" />
<TextView
android:id="@+id/tvNFCHint"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="12dp"
android:fontFamily="@font/maax"
android:gravity="center"
android:lineSpacingExtra="8dp"
android:minHeight="200dp"
android:paddingLeft="20dp"
android:paddingTop="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp"
android:text="@string/tap_tangem_card"
android:textAlignment="center"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/primary_dark"
android:textSize="@dimen/text_size_medium" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="10dp"
android:src="@drawable/ic_show_menu"
app:backgroundTint="@color/colorPrimary"
app:fabSize="normal" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,25 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/managePIN"
android:enabled="false"
android:title="@string/manage_user_pin_1" />
<item
android:id="@+id/managePIN2"
android:enabled="false"
android:title="@string/manage_user_pin_2" />
<item
android:id="@+id/sendLogs"
android:title="@string/send_logs"
android:visible="false" />
<item
android:id="@+id/settings"
android:visible="false"
android:title="@string/settings" />
<item
android:id="@+id/about"
android:title="@string/about" />
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/managePIN"
android:enabled="false"
android:title="@string/manage_user_pin_1" />
<item
android:id="@+id/managePIN2"
android:enabled="false"
android:title="@string/manage_user_pin_2" />
<item
android:id="@+id/sendLogs"
android:title="@string/send_logs"
android:visible="false" />
<item
android:id="@+id/settings"
android:title="@string/settings" />
<item
android:id="@+id/about"
android:title="@string/about" />
</menu>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_graph"
app:startDestination="@id/main">
<fragment
android:id="@+id/main"
android:name="com.tangem.ui.fragment.MainFragment"
android:label="fragment_main"
tools:layout="@layout/main_fragment">
</fragment>
</navigation>

View file

@ -58,6 +58,8 @@
<string name="cardano">Cardano</string>
<!-- SettingsActivity -->
<string name="сommon">Common</string>
<string name="manual_editing_fee">Manual editing Fee</string>
<string name="settings">Settings</string>
<string name="encryption_modes">Encryption modes</string>
<string-array name="encryption_modes_entries">
@ -115,7 +117,7 @@
<string name="new_app_version">There is a new application version: %1$s</string>
<string name="update">Update</string>
<!-- LoadedWalletActivity, LoadedWallet -->
<!-- LoadedWalletActivity, LoadedWalletFragment -->
<string name="wallet_empty">The wallet is empty</string>
<string name="no_compatible_wallet">No compatible wallets installed</string>
<string name="please_wait_while_previous">Please wait while previous transaction is confirmed in blockchain</string>
@ -217,6 +219,7 @@
<string name="pin_2_is_required_to_sign_the_transaction">PIN2 is required to sign the payment</string>
<string name="not_enough_funds">Not enough funds</string>
<string name="service_unavailable">Service unavailable</string>
<string name="risk_delaying">You have a risk of delaying transaction</string>
<!-- EmptyWalletActivity -->
<string name="wallet_has_not_been_yet_created">Wallet hasn\'t been yet created</string>

View file

@ -23,4 +23,9 @@
<string name="log_file_provider_authorities">com.tangem.data.LogFileProvider</string>
<string name="ex_cant_create_coinEngine">Can\'t create CoinEngine!</string>
<string name="pref_category_common">category_common</string>
<string name="pref_manual_editing_fee">manual_editing_fee</string>
<string name="pref_select_nodes">select_nodes</string>
<string name="pref_encryption_modes">encryption_modes</string>
</resources>

View file

@ -1,20 +1,25 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Common">
<PreferenceCategory
android:key="@string/pref_category_common"
android:title="@string/сommon">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/pref_manual_editing_fee"
android:title="@string/manual_editing_fee" />
<Preference
android:defaultValue="true"
android:key="select_nodes"
android:key="@string/pref_select_nodes"
android:title="@string/select_nodes" />
<ListPreference
android:defaultValue="0"
android:entries="@array/encryption_modes_entries"
android:entryValues="@array/encryption_mode_values"
android:key="encryption_modes"
android:key="@string/pref_encryption_modes"
android:title="@string/encryption_modes" />
</PreferenceCategory>
</PreferenceScreen>

View file

@ -2,40 +2,49 @@ package com.tangem.ui
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.preference.PreferenceManager
import android.text.Editable
import android.text.Html
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Blockchain
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.ui.event.TransactionFinishWithError
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.data.Blockchain
import com.tangem.di.ToastHelper
import com.tangem.ui.activity.PinRequestActivity
import com.tangem.ui.activity.SignTransactionActivity
import com.tangem.ui.event.TransactionFinishWithError
import com.tangem.util.UtilHelper
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.R
import com.tangem.wallet.TangemContext
import kotlinx.android.synthetic.tangemAccess.activity_confirm_transaction.*
import org.greenrobot.eventbus.EventBus
import java.io.IOException
import java.util.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.ui.activity.PinRequestActivity
import com.tangem.ui.activity.SignTransactionActivity
import kotlinx.android.synthetic.tangemAccess.activity_confirm_transaction.*
import javax.inject.Inject
class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
@Inject
internal lateinit var toastHelper: ToastHelper
private lateinit var sp: SharedPreferences
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private lateinit var amount: CoinEngine.Amount
@ -49,6 +58,10 @@ class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallbac
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_confirm_transaction)
App.toastHelperComponent.inject(this)
sp = PreferenceManager.getDefaultSharedPreferences(this)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
@ -95,6 +108,8 @@ class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallbac
rgFee.visibility = View.INVISIBLE
}
etFee.isEnabled = sp.getBoolean(getString(R.string.pref_manual_editing_fee), false)
// set listeners
rgFee.setOnCheckedChangeListener { _, checkedId -> doSetFee(checkedId) }
etFee.addTextChangedListener(object : TextWatcher {
@ -114,6 +129,9 @@ class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallbac
} else
tvFeeEquivalent.error = null
if (sp.getBoolean(getString(R.string.pref_manual_editing_fee), false))
toastHelper.showSingleToast(this@ConfirmTransactionActivity, getString(R.string.risk_delaying))
} catch (e: Exception) {
e.printStackTrace()
tvFeeEquivalent.text = ""

View file

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tangem.ui.ConfirmTransactionActivity"
@ -148,35 +149,36 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent">
<EditText
android:id="@+id/etAmount"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:enabled="false"
android:digits="0123456789.,"
android:fontFamily="@font/maax"
android:hint="@string/payment_amount"
android:hint="@string/enter_amount"
android:imeOptions="actionDone"
android:inputType="numberDecimal"
android:padding="12dp"
android:textColor="@color/black"
android:textColorLink="@color/colorPrimary"
android:textSize="@dimen/text_size_1_large"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tvCurrency"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="150dp"
tools:text="1.34343434343443434343434" />
<TextView
android:id="@+id/tvCurrency"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:fontFamily="@font/maax"
android:text="@string/m_btc"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_1_large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@ -266,7 +268,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:enabled="false"
android:fontFamily="@font/maax"
android:hint="@string/fee_amount"
android:inputType="numberDecimal"

View file

@ -180,6 +180,7 @@
<EditText
android:id="@+id/etAmount"
tools:text="1.34343434343443434343434"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:digits="0123456789.,"

View file

@ -152,35 +152,36 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent">
<EditText
android:id="@+id/etAmount"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:enabled="false"
android:digits="0123456789.,"
android:fontFamily="@font/maax"
android:hint="@string/payment_amount"
android:hint="@string/enter_amount"
android:imeOptions="actionDone"
android:inputType="numberDecimal"
android:padding="12dp"
android:textColor="@color/black"
android:textColorLink="@color/colorPrimary"
android:textSize="@dimen/text_size_1_large"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tvCurrency"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="150dp"
tools:text="1.34343434343443434343434" />
<TextView
android:id="@+id/tvCurrency"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:fontFamily="@font/maax"
android:text="@string/m_btc"
android:textColor="@color/primary"
android:textSize="@dimen/text_size_1_large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -29,7 +29,7 @@ dependencies {
implementation project(':card-common')
// implementation 'com.github.TangemCash.card_android-common:card_android-android:0.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.scottyab:rootbeer-lib:0.0.7'
testImplementation 'junit:junit:4.12'

View file

@ -1,4 +1,4 @@
#Thu Apr 18 15:14:06 MSK 2019
#Thu Apr 18 12:21:12 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View file

@ -29,7 +29,7 @@ dependencies {
implementation project(':card-common')
// implementation 'com.github.TangemCash.card_android-common:card_android-android:0.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'