Updated on 2026-08-14
This commit is contained in:
parent
07b30e1f02
commit
a4de111290
31 changed files with 2365 additions and 240 deletions
195
.idea/navEditor.xml
generated
195
.idea/navEditor.xml
generated
|
|
@ -8,13 +8,25 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="cameraFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-397" />
|
||||
<option name="y" value="645" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="confirmTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="516" />
|
||||
<option name="y" value="408" />
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="2390" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -39,8 +51,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="324" />
|
||||
<option name="y" value="-682" />
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="105" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -51,8 +63,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="56" />
|
||||
<option name="y" value="-379" />
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="105" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -77,13 +89,65 @@
|
|||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="idFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-272" />
|
||||
<option name="y" value="1404" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_idFragment_to_issueNewIdFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_idFragment_to_verifyCard">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="issueNewIdFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-394" />
|
||||
<option name="y" value="1004" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_issueNewIdFragment_to_cameraFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_issueNewIdFragment_to_validateIdFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="loadedWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-91" />
|
||||
<option name="y" value="411" />
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="1279" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -128,8 +192,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-642" />
|
||||
<option name="y" value="5" />
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="2390" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -154,8 +218,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-425" />
|
||||
<option name="y" value="250" />
|
||||
<option name="x" value="12" />
|
||||
<option name="y" value="1279" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -165,6 +229,11 @@
|
|||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_idFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_loadedWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
|
|
@ -190,6 +259,11 @@
|
|||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_tagFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -200,8 +274,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="416" />
|
||||
<option name="y" value="-239" />
|
||||
<option name="x" value="988" />
|
||||
<option name="y" value="12" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -212,8 +286,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-274" />
|
||||
<option name="y" value="-387" />
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="461" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -224,8 +298,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="76" />
|
||||
<option name="y" value="811" />
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="1039" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -236,8 +310,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="280" />
|
||||
<option name="y" value="1172" />
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="1992" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -257,8 +331,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="9" />
|
||||
<option name="y" value="1151" />
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="1618" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -269,8 +343,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-214" />
|
||||
<option name="y" value="1097" />
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="1261" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -281,8 +355,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="411" />
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="2348" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -307,8 +381,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="941" />
|
||||
<option name="y" value="15" />
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="682" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -328,8 +402,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="328" />
|
||||
<option name="y" value="807" />
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="2034" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -340,8 +414,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="1011" />
|
||||
<option name="y" value="412" />
|
||||
<option name="x" value="1232" />
|
||||
<option name="y" value="2433" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -352,8 +426,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-496" />
|
||||
<option name="y" value="-375" />
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="923" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
@ -364,8 +438,8 @@
|
|||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="760" />
|
||||
<option name="y" value="412" />
|
||||
<option name="x" value="988" />
|
||||
<option name="y" value="2433" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -380,13 +454,46 @@
|
|||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="tagFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="1635" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="validateIdFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-628" />
|
||||
<option name="y" value="1001" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_validateIdFragment_to_writeIdFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="verifyCard">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="78" />
|
||||
<option name="y" value="32" />
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="860" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
|
|
@ -411,6 +518,18 @@
|
|||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="writeIdFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-661" />
|
||||
<option name="y" value="701" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
|
|
|
|||
|
|
@ -164,4 +164,7 @@ dependencies {
|
|||
implementation 'io.jafka:jeos:0.9.15'
|
||||
// implementation 'one.block:eosiojava:0.1.0'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.61'
|
||||
|
||||
implementation 'io.fotoapparat:fotoapparat:2.6.1'
|
||||
implementation 'com.otaliastudios:cameraview:2.6.1'
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ public enum Blockchain {
|
|||
Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"),
|
||||
BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"),
|
||||
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
|
||||
EthereumId("ETH/ID", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum ID"),
|
||||
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
|
||||
Token("Token", "ETH", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
|
||||
NftToken("NftToken", "", 1.0, R.drawable.tangem2, "Ethereum"),
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
rippleBackgroundNfc.startRippleAnimation()
|
||||
|
||||
// navigateToDestination(R.id.action_main_to_emptyIdFragment)
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
|
@ -274,13 +275,19 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
|
|||
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)
|
||||
navigateForResult(Constant.REQUEST_CODE_SHOW_CARD_ACTIVITY,
|
||||
R.id.action_main_to_loadedWalletFragment, bundle)
|
||||
if (card.isIDCard) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag)
|
||||
ctx.saveToBundle(bundle)
|
||||
navigateToDestination(R.id.action_main_to_idFragment, bundle)
|
||||
} else {
|
||||
engineCoin.defineWallet()
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag)
|
||||
ctx.saveToBundle(bundle)
|
||||
navigateForResult(Constant.REQUEST_CODE_SHOW_CARD_ACTIVITY,
|
||||
R.id.action_main_to_loadedWalletFragment, bundle)
|
||||
}
|
||||
} else {
|
||||
showUnkownBlockchainWarning()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.ui.fragment.id
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import com.otaliastudios.cameraview.CameraListener
|
||||
import com.otaliastudios.cameraview.PictureResult
|
||||
import com.otaliastudios.cameraview.controls.Facing
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_camera.*
|
||||
|
||||
|
||||
class CameraFragment : BaseFragment() {
|
||||
override val layoutId = R.layout.fragment_camera
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
cvCamera?.setLifecycleOwner(viewLifecycleOwner)
|
||||
|
||||
cvCamera?.addCameraListener(object : CameraListener() {
|
||||
override fun onPictureTaken(result: PictureResult) { // Picture was taken!
|
||||
|
||||
result.toBitmap(200, 200) {
|
||||
navigateBackWithResult(Activity.RESULT_OK, bundleOf(PHOTO_KEY to it))
|
||||
}
|
||||
}
|
||||
})
|
||||
ivTakePicture?.setOnClickListener { cvCamera?.takePictureSnapshot() }
|
||||
ivFlipCamera?.setOnClickListener { flipCamera() }
|
||||
}
|
||||
|
||||
private fun flipCamera() {
|
||||
if (cvCamera?.facing == Facing.BACK) {
|
||||
cvCamera?.facing = Facing.FRONT
|
||||
} else {
|
||||
cvCamera?.facing = Facing.BACK
|
||||
}
|
||||
}
|
||||
|
||||
companion object{
|
||||
const val PHOTO_KEY = "photo"
|
||||
}
|
||||
|
||||
}
|
||||
465
app/src/main/java/com/tangem/ui/fragment/id/IdFragment.kt
Normal file
465
app/src/main/java/com/tangem/ui/fragment/id/IdFragment.kt
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
package com.tangem.ui.fragment.id
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.BitmapFactory
|
||||
import android.media.MediaPlayer
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Blockchain
|
||||
import com.tangem.data.network.ServerApiCommon
|
||||
import com.tangem.server_android.ServerApiTangem
|
||||
import com.tangem.server_android.model.CardVerifyAndGetInfo
|
||||
import com.tangem.tangem_card.reader.CardProtocol
|
||||
import com.tangem.tangem_card.tasks.VerifyCardTask
|
||||
import com.tangem.tangem_card.util.Util
|
||||
import com.tangem.tangem_sdk.android.reader.NfcReader
|
||||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.fragment.wallet.LoadedWalletViewModel
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.*
|
||||
import kotlinx.android.synthetic.main.fragment_id.*
|
||||
import kotlinx.android.synthetic.main.layout_btn_details.*
|
||||
import kotlinx.android.synthetic.main.layout_id.*
|
||||
import kotlinx.android.synthetic.main.layout_tangem_card.*
|
||||
import kotlinx.android.synthetic.main.layout_tangem_card.rlProgressBar
|
||||
import net.i2p.crypto.eddsa.Utils
|
||||
import java.util.*
|
||||
import kotlin.concurrent.timerTask
|
||||
|
||||
class IdFragment : BaseFragment(), NfcAdapter.ReaderCallback,
|
||||
CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
companion object {
|
||||
val TAG: String = IdFragment::class.java.simpleName
|
||||
}
|
||||
|
||||
override val layoutId = R.layout.fragment_id
|
||||
|
||||
private lateinit var viewModel: LoadedWalletViewModel
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var mpSecondScanSound: MediaPlayer
|
||||
private var serverApiCommon: ServerApiCommon = ServerApiCommon()
|
||||
private var serverApiTangem: ServerApiTangem = ServerApiTangem()
|
||||
private var lastTag: Tag? = null
|
||||
private var lastReadSuccess = true
|
||||
private var verifyCardTask: VerifyCardTask? = null
|
||||
private var requestPIN2Count = 0
|
||||
private var timerHideErrorAndMessage: Timer? = null
|
||||
private var newPIN = ""
|
||||
private var newPIN2 = ""
|
||||
private var photo: ByteArray? = null
|
||||
private var cardProtocol: CardProtocol? = null
|
||||
private var refreshAction: Runnable? = null
|
||||
private var hasIdInfo: Boolean? = null
|
||||
|
||||
private var requestCounter: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
LOG.i(TAG, "requestCounter, set $field")
|
||||
if (field <= 0) {
|
||||
LOG.e(TAG, "+++++++++++ FINISH REFRESH")
|
||||
if (srl != null && srl.isRefreshing)
|
||||
srl.isRefreshing = false
|
||||
} else if (srl != null && !srl.isRefreshing)
|
||||
srl.isRefreshing = true
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(context, arguments)
|
||||
|
||||
lastTag = activity?.intent?.getParcelableExtra(Constant.EXTRA_LAST_DISCOVERED_TAG)
|
||||
|
||||
if (arguments?.containsKey(NfcAdapter.EXTRA_TAG) == true) {
|
||||
val tag = arguments!!.getParcelable<Tag>(NfcAdapter.EXTRA_TAG)
|
||||
if (tag != null) onTagDiscovered(tag)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mpSecondScanSound = MediaPlayer.create(activity, R.raw.scan_card_sound)
|
||||
|
||||
tvIdNumber?.text = "ID # ${Utils.bytesToHex(ctx.card.cid)}"
|
||||
|
||||
if (hasIdInfo == false) rlToolButtons?.visibility = View.VISIBLE
|
||||
|
||||
// set listeners
|
||||
srl.setOnRefreshListener { refresh() }
|
||||
|
||||
btnDetails.setOnClickListener {
|
||||
if (cardProtocol != null) {
|
||||
val bundle = Bundle().apply { ctx.saveToBundle(this) }
|
||||
navigateForResult(Constant.REQUEST_CODE_VERIFY_CARD,
|
||||
R.id.action_idFragment_to_verifyCard, bundle)
|
||||
} else {
|
||||
(activity as MainActivity).toastHelper
|
||||
.showSingleToast(context, getString(R.string.general_notification_scan_again_to_verify))
|
||||
}
|
||||
}
|
||||
|
||||
btnIssueNewId?.setOnClickListener {
|
||||
val bundle = Bundle().apply { ctx.saveToBundle(this) }
|
||||
navigateToDestination(R.id.action_idFragment_to_issueNewIdFragment, bundle)
|
||||
}
|
||||
|
||||
if (hasIdInfo == null) tvBalanceLine1?.text = "Reading... Hold the card firmly"
|
||||
|
||||
btnNewScan.setOnClickListener {
|
||||
// navigateToDestination(R.id.action_loadedWalletFragment_to_main)
|
||||
navigateUp()
|
||||
}
|
||||
|
||||
// request card verify and get info listener
|
||||
val cardVerifyAndGetInfoListener: ServerApiTangem.CardVerifyAndGetInfoListener = object : ServerApiTangem.CardVerifyAndGetInfoListener {
|
||||
override fun onSuccess(cardVerifyAndGetArtworkResponse: CardVerifyAndGetInfo.Response?) {
|
||||
LOG.i(TAG, "cardVerifyAndGetInfoListener onSuccess")
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return
|
||||
|
||||
val result = cardVerifyAndGetArtworkResponse?.results!![0]
|
||||
if (result.error != null) {
|
||||
ctx.card!!.isOnlineVerified = false
|
||||
return
|
||||
}
|
||||
ctx.card!!.isOnlineVerified = result.passed
|
||||
|
||||
requestCounter--
|
||||
updateViews()
|
||||
|
||||
if (!result.passed) return
|
||||
|
||||
if (App.localStorage.checkBatchInfoChanged(ctx.card!!, result)) {
|
||||
LOG.w(TAG, "Batch ${result.batch} info changed to '$result'")
|
||||
ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card!!))
|
||||
App.localStorage.applySubstitution(ctx.card!!)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(message: String?) {
|
||||
LOG.i(TAG, "cardVerifyAndGetInfoListener onFail")
|
||||
if (activity == null || !UtilHelper.isOnline(activity!!)) return
|
||||
requestCounter--
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
serverApiTangem.setCardVerifyAndGetInfoListener(cardVerifyAndGetInfoListener)
|
||||
refresh()
|
||||
startVerify(lastTag)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (timerHideErrorAndMessage != null) {
|
||||
timerHideErrorAndMessage!!.cancel()
|
||||
timerHideErrorAndMessage = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
srl?.removeCallbacks(refreshAction)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
startVerify(tag)
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar?.post { rlProgressBar?.visibility = View.VISIBLE }
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
verifyCardTask = null
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.error == null) {
|
||||
rlProgressBar?.post {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
this.cardProtocol = cardProtocol
|
||||
if (!cardProtocol.card.isWalletPublicKeyValid)
|
||||
refresh()
|
||||
else
|
||||
updateViews()
|
||||
|
||||
if (cardProtocol.card.isIDCard) {
|
||||
if (cardProtocol.card.hasIDCardData()) {
|
||||
|
||||
Toast.makeText(activity, "ID card: ${cardProtocol.card.idCardData.fullName}", Toast.LENGTH_SHORT).show()
|
||||
ctx.card = cardProtocol.card
|
||||
|
||||
tvBalanceLine1?.visibility = View.VISIBLE
|
||||
|
||||
val idCardData = ctx.card.idCardData
|
||||
tvFullName.text = idCardData.fullName
|
||||
tvBirthDate.text = idCardData.birthday
|
||||
tvGender.text = "Sex: ${idCardData.gender}"
|
||||
ivPhoto.setImageBitmap(BitmapFactory.decodeByteArray(idCardData.photo, 0, idCardData.photo.size))
|
||||
|
||||
val inputs = "${idCardData.fullName};${idCardData.birthday}${idCardData.gender}"
|
||||
val info = inputs.toByteArray() + idCardData.photo
|
||||
ctx.card.idHash = Util.calculateSHA256(info)
|
||||
|
||||
refresh()
|
||||
|
||||
} else {
|
||||
Toast.makeText(activity, "Empty ID card", Toast.LENGTH_SHORT).show()
|
||||
rlToolButtons?.visibility = View.VISIBLE
|
||||
tvBalanceLine1?.text = "Empty ID card".toUpperCase()
|
||||
tvBalanceLine1?.setTextSize(18F)
|
||||
tvBalanceLine1?.visibility = View.VISIBLE
|
||||
hasIdInfo = false
|
||||
}
|
||||
}
|
||||
|
||||
mpSecondScanSound.start()
|
||||
}
|
||||
} else {
|
||||
// remove last UIDs because of error and no card read
|
||||
rlProgressBar?.post {
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed)
|
||||
activity?.supportFragmentManager?.let { NoExtendedLengthSupportDialog().show(it, NoExtendedLengthSupportDialog.TAG) }
|
||||
else
|
||||
Toast.makeText(activity, R.string.general_notification_scan_again, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
verifyCardTask = null
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
WaitSecurityDelayDialog.onReadWait(activity as AppCompatActivity?, msec)
|
||||
}
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(activity as AppCompatActivity?, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(activity)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
|
||||
}
|
||||
|
||||
fun updateViews() {
|
||||
if (activity == null || this.view == null) return
|
||||
|
||||
if (timerHideErrorAndMessage != null) {
|
||||
timerHideErrorAndMessage!!.cancel()
|
||||
timerHideErrorAndMessage = null
|
||||
}
|
||||
|
||||
if (ctx.card?.idCardData?.photo != null && (photo == null)) {
|
||||
photo = ctx.card.idCardData.photo
|
||||
ivPhoto.setImageBitmap(BitmapFactory.decodeByteArray(photo, 0, photo!!.size))
|
||||
|
||||
}
|
||||
|
||||
if (ctx.hasError()) {
|
||||
tvError?.visibility = View.VISIBLE
|
||||
tvError?.text = ctx.error
|
||||
} else {
|
||||
tvError?.visibility = View.GONE
|
||||
tvError?.text = ""
|
||||
}
|
||||
|
||||
if (ctx.message == null || ctx.message.isEmpty()) {
|
||||
tvMessage?.text = ""
|
||||
tvMessage?.visibility = View.GONE
|
||||
} else {
|
||||
tvMessage?.text = ctx.message
|
||||
tvMessage?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (tvError?.visibility == View.VISIBLE || tvMessage?.visibility == View.VISIBLE) {
|
||||
timerHideErrorAndMessage = Timer()
|
||||
timerHideErrorAndMessage?.schedule(
|
||||
timerTask {
|
||||
activity?.runOnUiThread {
|
||||
tvMessage?.visibility = View.GONE
|
||||
tvError?.visibility = View.GONE
|
||||
// clear only already viewed messages
|
||||
if (tvMessage?.text == ctx.message) ctx.message = null
|
||||
if (tvError?.text == ctx.error) ctx.error = null
|
||||
}
|
||||
},
|
||||
5000)
|
||||
}
|
||||
|
||||
if (srl.isRefreshing && hasIdInfo != null && hasIdInfo != false) {
|
||||
tvBalanceLine1.setTextColor(resources.getColor(R.color.primary))
|
||||
tvBalanceLine1.text = getString(R.string.loaded_wallet_verifying_in_blockchain)
|
||||
tvBalanceLine2.text = ""
|
||||
} else if (ctx.card?.hasIDCardData() != false) {
|
||||
|
||||
val validator = BalanceValidator()
|
||||
// TODO why attest=false?
|
||||
validator.check(ctx, false)
|
||||
context?.let { ContextCompat.getColor(it, validator.color) }?.let { tvBalanceLine1?.setTextColor(it) }
|
||||
tvBalanceLine1?.text = getString(validator.firstLine)
|
||||
}
|
||||
|
||||
if (ctx.card.hasIDCardData()) {
|
||||
val idCardData = ctx.card.idCardData
|
||||
tvFullName.text = idCardData.fullName
|
||||
tvBirthDate.text = idCardData.birthday
|
||||
tvGender.text = "Sex: ${idCardData.gender}"
|
||||
ivPhoto.setImageBitmap(BitmapFactory.decodeByteArray(idCardData.photo, 0, idCardData.photo.size))
|
||||
rlToolButtons?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
if (ctx.card == null) return
|
||||
|
||||
// clear all card data and request again
|
||||
ctx.coinData.clearInfo()
|
||||
ctx.error = null
|
||||
ctx.message = null
|
||||
|
||||
LOG.w(TAG, "============= START REFRESH")
|
||||
requestCounter = 0
|
||||
srl?.isRefreshing = true
|
||||
|
||||
updateViews()
|
||||
|
||||
// Bitcoin, Litecoin, BitcoinCash, Stellar
|
||||
if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet ||
|
||||
ctx.blockchain == Blockchain.Litecoin || ctx.blockchain == Blockchain.BitcoinCash ||
|
||||
ctx.blockchain == Blockchain.Stellar || ctx.blockchain == Blockchain.StellarTestNet || ctx.blockchain == Blockchain.StellarAsset) {
|
||||
ctx.coinData.setIsBalanceEqual(true)
|
||||
}
|
||||
|
||||
requestVerifyAndGetInfo()
|
||||
|
||||
if (ctx.card.hasIDCardData()) requestBalanceAndUnspentTransactions()
|
||||
|
||||
if (::viewModel.isInitialized)
|
||||
viewModel.requestRateInfo(ctx)
|
||||
|
||||
if (requestCounter == 0) {
|
||||
// if no connection and no requests posted
|
||||
srl?.isRefreshing = false
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestBalanceAndUnspentTransactions() {
|
||||
if (UtilHelper.isOnline(context as Activity)) {
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
coinEngine?.defineWallet()
|
||||
requestCounter++
|
||||
coinEngine!!.requestBalanceAndUnspentTransactions(
|
||||
object : CoinEngine.BlockchainRequestsCallbacks {
|
||||
|
||||
|
||||
override fun onComplete(success: Boolean) {
|
||||
LOG.i(TAG, "requestBalanceAndUnspentTransactions onComplete: $success, request counter $requestCounter")
|
||||
if (activity == null) return
|
||||
requestCounter--
|
||||
if (!success) {
|
||||
LOG.e(TAG, "requestBalanceAndUnspentTransactions ctx.error: " + ctx.error)
|
||||
}
|
||||
updateViews()
|
||||
|
||||
}
|
||||
|
||||
override fun onProgress() {
|
||||
if (activity == null) return
|
||||
LOG.i(TAG, "requestBalanceAndUnspentTransactions onProgress")
|
||||
updateViews()
|
||||
}
|
||||
|
||||
override fun allowAdvance(): Boolean {
|
||||
return try {
|
||||
context?.let { UtilHelper.isOnline(it) }!!
|
||||
} catch (e: KotlinNullPointerException) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
ctx.error = getString(R.string.general_error_no_connection)
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestVerifyAndGetInfo() {
|
||||
if (UtilHelper.isOnline(context as Activity)) {
|
||||
if ((ctx.card!!.isOnlineVerified == null || !ctx.card!!.isOnlineVerified)) {
|
||||
LOG.i(TAG, "requestVerifyAndGetInfo")
|
||||
requestCounter++
|
||||
serverApiTangem.cardVerifyAndGetInfo(ctx.card)
|
||||
}
|
||||
} else {
|
||||
ctx.error = getString(R.string.general_error_no_connection)
|
||||
updateViews()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startVerify(tag: Tag?) {
|
||||
try {
|
||||
val isoDep = IsoDep.get(tag)
|
||||
val uid = tag?.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
if (ctx.card.uid != sUID || cardProtocol != null) {
|
||||
(activity as MainActivity).nfcManager.ignoreTag(isoDep.tag)
|
||||
return
|
||||
}
|
||||
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = 1000
|
||||
else
|
||||
isoDep.timeout = 65000
|
||||
|
||||
verifyCardTask = VerifyCardTask(ctx.card, NfcReader((activity as MainActivity).nfcManager, isoDep),
|
||||
App.localStorage, App.pinStorage, App.firmwaresStorage, this)
|
||||
verifyCardTask?.start()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
package com.tangem.ui.fragment.id
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.RadioButton
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import com.tangem.tangem_card.util.Util
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.wallet.CoinEngineFactory
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.TangemContext
|
||||
import com.tangem.wallet.ethID.EthIdEngine
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_issue_new_id.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.lang.Exception
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
class IssueNewIdFragment : BaseFragment(), NavigationResultListener {
|
||||
lateinit var ctx: TangemContext
|
||||
private var photo: Bitmap? = null
|
||||
private var photoInBytes: ByteArray? = null
|
||||
private var gender: Gender? = null
|
||||
private var userIdData: UserIdData? = null
|
||||
|
||||
override val layoutId = R.layout.fragment_issue_new_id
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(context, arguments)
|
||||
|
||||
ivPhoto?.setOnClickListener {
|
||||
navigateForResult(GET_PHOTO_REQUEST_CODE, R.id.action_issueNewIdFragment_to_cameraFragment)
|
||||
}
|
||||
|
||||
if (photo != null) ivPhoto?.setImageBitmap(photo)
|
||||
|
||||
btnConfirm?.setOnClickListener {
|
||||
if (!checkIfInfoProvided()) {
|
||||
Toast.makeText(context, "Fill in all data first", Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
try {
|
||||
processData()
|
||||
} catch (exception: Exception) {
|
||||
Toast.makeText(context, "Check for correctness of data", Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
val bundle = bundleOf(USER_ID_DATA to userIdData)
|
||||
ctx.saveToBundle(bundle)
|
||||
navigateToDestination(R.id.action_issueNewIdFragment_to_validateIdFragment, bundle)
|
||||
}
|
||||
|
||||
rbFemale?.setOnClickListener { onRadioButtonClicked(it) }
|
||||
rbMale?.setOnClickListener { onRadioButtonClicked(it) }
|
||||
|
||||
DateInputMask(etBirthDate).listen()
|
||||
// etBirthDate?.addTextChangedListener(MaskWatcher("##/##/####"))
|
||||
|
||||
}
|
||||
|
||||
private fun onRadioButtonClicked(view: View) {
|
||||
if (view is RadioButton) {
|
||||
when (view.getId()) {
|
||||
R.id.rbFemale ->
|
||||
if (view.isChecked) {
|
||||
gender = Gender.F
|
||||
}
|
||||
R.id.rbMale ->
|
||||
if (view.isChecked) {
|
||||
gender = Gender.M
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
if (requestCode == GET_PHOTO_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
photo = data?.getParcelable<Bitmap>(CameraFragment.PHOTO_KEY)
|
||||
ivPhoto?.setImageBitmap(photo)
|
||||
val out = ByteArrayOutputStream()
|
||||
photo!!.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
||||
photoInBytes = out.toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun checkIfInfoProvided(): Boolean {
|
||||
return (etName.text.isNotBlank() && etSurname.text.isNotBlank() && etBirthDate.text.isNotBlank() &&
|
||||
(photo != null) && (gender != null))
|
||||
}
|
||||
|
||||
private fun processData() {
|
||||
val name = etName.text.toString()
|
||||
val lastName = etSurname.text.toString()
|
||||
val birthDate = convertDate(etBirthDate.text)
|
||||
userIdData = UserIdData(name, lastName, birthDate, gender.toString(), photo!!)
|
||||
val issuerExpireDate = processIssueExpireDate()
|
||||
|
||||
ctx.card.setTlvIDCardData(
|
||||
"$name $lastName",
|
||||
birthDate,
|
||||
gender.toString(),
|
||||
photoInBytes,
|
||||
issuerExpireDate.issueDate,
|
||||
issuerExpireDate.expireDate,
|
||||
(CoinEngineFactory.create(ctx) as? EthIdEngine)?.approvalAddress
|
||||
)
|
||||
|
||||
val inputs = "$name $lastName;$birthDate${gender.toString()}"
|
||||
val info = inputs.toByteArray() + photoInBytes!!
|
||||
ctx.card.idHash = Util.calculateSHA256(info)
|
||||
}
|
||||
|
||||
private fun convertDate(inputDate: CharSequence): String {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.clear()
|
||||
val sdf = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH)
|
||||
try {
|
||||
calendar.time = sdf.parse(inputDate.toString())
|
||||
} catch (exception: Error) {
|
||||
etBirthDate.setTextColor(ContextCompat.getColor(requireContext(), R.color.bg_err))
|
||||
}
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0)
|
||||
calendar.timeZone = SimpleTimeZone(0, "UTC")
|
||||
val dateFormat = SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH)
|
||||
dateFormat.timeZone = SimpleTimeZone(0, "UTC")
|
||||
return dateFormat.format(calendar.time)
|
||||
}
|
||||
|
||||
private fun processIssueExpireDate(): IssuerExpireDate {
|
||||
val dt = Date()
|
||||
val issueDate = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(dt)
|
||||
dt.year = dt.year + 10
|
||||
val expireDate = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(dt)
|
||||
return IssuerExpireDate(issueDate, expireDate)
|
||||
}
|
||||
|
||||
data class IssuerExpireDate(val issueDate: String, val expireDate: String)
|
||||
|
||||
companion object {
|
||||
const val SEPARATOR = ";"
|
||||
const val COMPUTED_PUB_KEY = "ComputedPubKey"
|
||||
const val GET_PHOTO_REQUEST_CODE = "GetPhoto"
|
||||
const val USER_ID_DATA = "UserIdData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class Gender { M, F }
|
||||
|
||||
@Parcelize
|
||||
data class UserIdData(
|
||||
val firstName: String,
|
||||
val lastName: String,
|
||||
val birthDate: String,
|
||||
val gender: String,
|
||||
val photo: Bitmap
|
||||
) : Parcelable
|
||||
|
||||
class DateInputMask(val input: EditText) {
|
||||
fun listen() {
|
||||
input.addTextChangedListener(dateEntryWatcher)
|
||||
}
|
||||
|
||||
private val dateEntryWatcher = object : TextWatcher {
|
||||
|
||||
var edited = false
|
||||
val dividerCharacter = "/"
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
if (edited) {
|
||||
edited = false
|
||||
return
|
||||
}
|
||||
|
||||
var working = getEditText()
|
||||
|
||||
working = manageDateDivider(working, 2, start, before)
|
||||
working = manageDateDivider(working, 5, start, before)
|
||||
|
||||
edited = true
|
||||
input.setText(working)
|
||||
input.setSelection(input.text.length)
|
||||
}
|
||||
|
||||
private fun manageDateDivider(working: String, position: Int, start: Int, before: Int): String {
|
||||
if (working.length == position) {
|
||||
return if (before <= position && start < position)
|
||||
working + dividerCharacter
|
||||
else
|
||||
working.dropLast(1)
|
||||
}
|
||||
return working
|
||||
}
|
||||
|
||||
private fun getEditText(): String {
|
||||
return if (input.text.length >= 10)
|
||||
input.text.toString().substring(0, 10)
|
||||
else
|
||||
input.text.toString()
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {}
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
class MaskWatcher(private val mask: String) : TextWatcher {
|
||||
private var isRunning = false
|
||||
private var isDeleting = false
|
||||
override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) {
|
||||
isDeleting = count > after
|
||||
}
|
||||
|
||||
override fun onTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
if (isRunning || isDeleting) {
|
||||
return
|
||||
}
|
||||
isRunning = true
|
||||
val editableLength = editable.length
|
||||
if (editableLength < mask.length) {
|
||||
if (mask[editableLength] != '#') {
|
||||
editable.append(mask[editableLength])
|
||||
} else if (mask[editableLength - 1] != '#') {
|
||||
editable.insert(editableLength - 1, mask, editableLength - 1, editableLength)
|
||||
}
|
||||
}
|
||||
if (editableLength > mask.length) editable.delete(editable.lastIndex, editableLength)
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun buildCpf(): MaskWatcher {
|
||||
return MaskWatcher("###.###.###-##")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
package com.tangem.ui.fragment.id
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.media.MediaPlayer
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.tangem_card.data.TangemCard
|
||||
import com.tangem.tangem_card.reader.CardProtocol
|
||||
import com.tangem.tangem_card.tasks.OneTouchSignTask
|
||||
import com.tangem.tangem_card.tasks.SignTask
|
||||
import com.tangem.tangem_card.util.Util
|
||||
import com.tangem.tangem_sdk.android.data.PINStorage
|
||||
import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.tangem_sdk.android.reader.NfcReader
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID
|
||||
import com.tangem.tangem_sdk.data.asBundle
|
||||
import com.tangem.ui.SignTransactionFragment
|
||||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.util.LOG
|
||||
import com.tangem.wallet.CoinEngine
|
||||
import com.tangem.wallet.CoinEngineFactory
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.TangemContext
|
||||
import kotlinx.android.synthetic.main.layout_progress_horizontal.*
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
|
||||
class ValidateIdFragment : BaseFragment(), NavigationResultListener,
|
||||
NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
|
||||
companion object {
|
||||
val TAG: String = ValidateIdFragment::class.java.simpleName
|
||||
}
|
||||
|
||||
override val layoutId = R.layout.fragment_validate_id
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var mpFinishSignSound: MediaPlayer
|
||||
|
||||
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
|
||||
|
||||
private var signTransactionTask: OneTouchSignTask? = null
|
||||
|
||||
private lateinit var amount: CoinEngine.Amount
|
||||
private lateinit var fee: CoinEngine.Amount
|
||||
private var isIncludeFee = true
|
||||
private var outAddressStr: String? = null
|
||||
private var lastReadSuccess = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(context, arguments)
|
||||
|
||||
val callback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED)
|
||||
}
|
||||
}
|
||||
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound)
|
||||
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
||||
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
signTransactionTask?.cancel(true)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
signTransactionTask?.cancel(true)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) {
|
||||
navigateBackWithResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000
|
||||
else
|
||||
isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000
|
||||
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
coinEngine?.defineWallet()
|
||||
|
||||
coinEngine?.setOnNeedSendTransaction { tx ->
|
||||
if (tx != null) {
|
||||
val data = Bundle()
|
||||
ctx.saveToBundle(data)
|
||||
data.putByteArray(Constant.EXTRA_TX, tx)
|
||||
navigateForResult(
|
||||
Constant.REQUEST_CODE_SEND_TRANSACTION_,
|
||||
R.id.action_validateIdFragment_to_writeIdFragment,
|
||||
data)
|
||||
}
|
||||
}
|
||||
|
||||
coinEngine?.requestBalanceAndUnspentTransactions(object : CoinEngine.BlockchainRequestsCallbacks {
|
||||
override fun onComplete(success: Boolean?) {
|
||||
val transactionToSign = coinEngine.constructTransaction(null, null, true, null)
|
||||
|
||||
val transaction = object : OneTouchSignTask.TransactionToSign{
|
||||
override fun isIssuerCanSignTransaction(card: TangemCard?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isIssuerCanSignData(card: TangemCard?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getHashAlgToSign(card: TangemCard?): String {
|
||||
return transactionToSign.hashAlgToSign
|
||||
}
|
||||
|
||||
override fun getRawDataToSign(card: TangemCard?): ByteArray {
|
||||
return transactionToSign.rawDataToSign
|
||||
}
|
||||
|
||||
override fun isSigningOnCardSupported(card: TangemCard?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSignCompleted(card: TangemCard?, signature: ByteArray?) {
|
||||
transactionToSign.onSignCompleted(signature)
|
||||
}
|
||||
|
||||
override fun getHashesToSign(card: TangemCard?): Array<ByteArray> {
|
||||
return transactionToSign.hashesToSign
|
||||
}
|
||||
|
||||
override fun getIssuerTransactionSignature(card: TangemCard?, dataToSignByIssuer: ByteArray?): ByteArray {
|
||||
return transactionToSign.getIssuerTransactionSignature(dataToSignByIssuer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PINStorage.setPIN2(PINStorage.getDefaultPIN2())
|
||||
signTransactionTask = OneTouchSignTask(NfcReader((activity as MainActivity).nfcManager, isoDep),
|
||||
App.localStorage, App.pinStorage, this@ValidateIdFragment, transaction)
|
||||
signTransactionTask?.start()
|
||||
}
|
||||
|
||||
override fun onProgress() {
|
||||
}
|
||||
|
||||
override fun allowAdvance(): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
} catch (e: CardProtocol.TangemException_WrongAmount) {
|
||||
try {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, ctx.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, ctx.card.asBundle)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar?.progress = 5
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar?.post { progressBar?.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
signTransactionTask = null
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.error == null) {
|
||||
rlProgressBar?.post { rlProgressBar?.visibility = View.GONE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
|
||||
}
|
||||
|
||||
mpFinishSignSound.start()
|
||||
} else {
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
|
||||
progressBar?.post {
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
|
||||
}
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_cannot_sign))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
|
||||
navigateBackWithResult(Constant.RESULT_INVALID_PIN_, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
} else {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) {
|
||||
try {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
progressBar?.post {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
|
||||
NoExtendedLengthSupportDialog.message = getText(R.string.dialog_the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.dialog_the_nfc_adapter_length_apdu_advice).toString()
|
||||
NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, R.string.general_notification_scan_again, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
signTransactionTask = null
|
||||
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
LOG.i(TAG, "onReadBeforeRequest timeout $timeout")
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(activity!!, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
LOG.i(TAG, "onReadAfterRequest")
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(activity!!)
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
LOG.i(TAG, "onReadWait msec $msec")
|
||||
WaitSecurityDelayDialog.onReadWait(activity!!, msec)
|
||||
}
|
||||
|
||||
}
|
||||
285
app/src/main/java/com/tangem/ui/fragment/id/WriteIdFragment.kt
Normal file
285
app/src/main/java/com/tangem/ui/fragment/id/WriteIdFragment.kt
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
package com.tangem.ui.fragment.id
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.media.MediaPlayer
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.tangem_card.reader.CardProtocol
|
||||
import com.tangem.tangem_card.tasks.WriteIDCardTask
|
||||
import com.tangem.tangem_card.util.Util
|
||||
import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.tangem_sdk.android.reader.NfcReader
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID
|
||||
import com.tangem.tangem_sdk.data.asBundle
|
||||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.ui.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.ui.fragment.BaseFragment
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.util.LOG
|
||||
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.main.layout_progress_horizontal.*
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
import kotlinx.android.synthetic.tangemAccess.fragment_sign_transaction.*
|
||||
|
||||
class WriteIdFragment : BaseFragment(), NavigationResultListener,
|
||||
NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
|
||||
companion object {
|
||||
val TAG: String = WriteIdFragment::class.java.simpleName
|
||||
}
|
||||
|
||||
override val layoutId = R.layout.fragment_write_id
|
||||
|
||||
private lateinit var ctx: TangemContext
|
||||
private lateinit var tx: ByteArray
|
||||
private lateinit var mpFinishSignSound: MediaPlayer
|
||||
|
||||
private var idWasWritten = false
|
||||
|
||||
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
|
||||
|
||||
private var writeIDCardTask: WriteIDCardTask? = null
|
||||
|
||||
private var lastReadSuccess = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ctx = TangemContext.loadFromBundle(context, arguments)
|
||||
tx = arguments?.getByteArray(Constant.EXTRA_TX)!!
|
||||
|
||||
val callback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED)
|
||||
}
|
||||
}
|
||||
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound)
|
||||
|
||||
// init NFC Antenna
|
||||
nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
|
||||
nfcDeviceAntenna.init()
|
||||
|
||||
tvCardID.text = ctx.card!!.cidDescription
|
||||
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
tvProgressBar?.text = "Writing..."
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
writeIDCardTask?.cancel(true)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
writeIDCardTask?.cancel(true)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) {
|
||||
navigateBackWithResult(resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTagDiscovered(tag: Tag) {
|
||||
try {
|
||||
// get IsoDep handle and run cardReader thread
|
||||
val isoDep = IsoDep.get(tag)
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
|
||||
if (sUID == ctx.card.uid || !idWasWritten) {
|
||||
if (lastReadSuccess)
|
||||
isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000
|
||||
else
|
||||
isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000
|
||||
|
||||
writeIDCardTask = WriteIDCardTask(ctx.card, NfcReader((activity as MainActivity).nfcManager, isoDep),
|
||||
App.localStorage, App.pinStorage, ctx.card.idCardData, ctx.card.issuer.privateDataKey, this)
|
||||
writeIDCardTask?.start()
|
||||
} else
|
||||
(activity as MainActivity).nfcManager.ignoreTag(isoDep.tag)
|
||||
|
||||
} catch (e: CardProtocol.TangemException_WrongAmount) {
|
||||
try {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, ctx.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, ctx.card.asBundle)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadStart(cardProtocol: CardProtocol) {
|
||||
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar?.progress = 5
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
|
||||
progressBar?.post { progressBar?.progress = progress }
|
||||
}
|
||||
|
||||
override fun onReadFinish(cardProtocol: CardProtocol?) {
|
||||
writeIDCardTask = null
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.error == null) {
|
||||
rlProgressBar?.post { rlProgressBar?.visibility = View.GONE }
|
||||
|
||||
progressBar?.post {
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
|
||||
}
|
||||
|
||||
mpFinishSignSound.start()
|
||||
|
||||
idWasWritten = true
|
||||
|
||||
val coinEngine = CoinEngineFactory.create(ctx)
|
||||
|
||||
coinEngine!!.requestSendTransaction(
|
||||
object : CoinEngine.BlockchainRequestsCallbacks {
|
||||
override fun onComplete(success: Boolean) {
|
||||
if (success) {
|
||||
navigateUp(R.id.main)
|
||||
} else
|
||||
navigateUp(R.id.issueNewIdFragment)
|
||||
}
|
||||
|
||||
override fun onProgress() {
|
||||
}
|
||||
|
||||
override fun allowAdvance(): Boolean {
|
||||
return UtilHelper.isOnline(requireContext())
|
||||
}
|
||||
},
|
||||
tx
|
||||
)
|
||||
|
||||
} else {
|
||||
lastReadSuccess = false
|
||||
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
|
||||
progressBar?.post {
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
|
||||
}
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_cannot_sign))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
|
||||
navigateBackWithResult(Constant.RESULT_INVALID_PIN_, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
} else {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) {
|
||||
try {
|
||||
val data = Bundle()
|
||||
data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount))
|
||||
data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
|
||||
data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
|
||||
navigateBackWithResult(Activity.RESULT_CANCELED, data)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
progressBar?.post {
|
||||
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
|
||||
NoExtendedLengthSupportDialog.message = getText(R.string.dialog_the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.dialog_the_nfc_adapter_length_apdu_advice).toString()
|
||||
NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG)
|
||||
}
|
||||
} else {
|
||||
if (!idWasWritten) Toast.makeText(context, R.string.general_notification_scan_again, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
progressBar?.progress = 100
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlProgressBar?.postDelayed({
|
||||
try {
|
||||
rlProgressBar?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
override fun onReadCancel() {
|
||||
writeIDCardTask = null
|
||||
|
||||
progressBar?.postDelayed({
|
||||
try {
|
||||
progressBar?.progress = 0
|
||||
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
progressBar?.visibility = View.INVISIBLE
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
|
||||
override fun onReadBeforeRequest(timeout: Int) {
|
||||
LOG.i(TAG, "onReadBeforeRequest timeout $timeout")
|
||||
WaitSecurityDelayDialog.onReadBeforeRequest(activity!!, timeout)
|
||||
}
|
||||
|
||||
override fun onReadAfterRequest() {
|
||||
LOG.i(TAG, "onReadAfterRequest")
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(activity!!)
|
||||
}
|
||||
|
||||
override fun onReadWait(msec: Int) {
|
||||
LOG.i(TAG, "onReadWait msec $msec")
|
||||
WaitSecurityDelayDialog.onReadWait(activity!!, msec)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.wallet.cardano.CardanoEngine
|
|||
import com.tangem.wallet.ducatus.DucatusEngine
|
||||
import com.tangem.wallet.eos.EosEngine
|
||||
import com.tangem.wallet.eth.EthEngine
|
||||
import com.tangem.wallet.ethID.EthIdEngine
|
||||
import com.tangem.wallet.ltc.LtcEngine
|
||||
import com.tangem.wallet.matic.MaticTokenEngine
|
||||
import com.tangem.wallet.nftToken.NftTokenEngine
|
||||
|
|
@ -39,6 +40,7 @@ object CoinEngineFactory {
|
|||
Blockchain.Bitcoin, Blockchain.BitcoinTestNet -> BtcEngine()
|
||||
Blockchain.BitcoinCash -> BtcCashEngine()
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestNet -> EthEngine()
|
||||
Blockchain.EthereumId -> EthIdEngine()
|
||||
Blockchain.Token -> TokenEngine()
|
||||
Blockchain.NftToken -> NftTokenEngine()
|
||||
Blockchain.Litecoin -> LtcEngine()
|
||||
|
|
@ -67,6 +69,8 @@ object CoinEngineFactory {
|
|||
BtcEngine(context)
|
||||
else if (Blockchain.Ethereum == context.blockchain || Blockchain.EthereumTestNet == context.blockchain)
|
||||
EthEngine(context)
|
||||
else if (Blockchain.EthereumId == context.blockchain)
|
||||
EthIdEngine(context)
|
||||
else if (Blockchain.Token == context.blockchain)
|
||||
TokenEngine(context)
|
||||
else if (Blockchain.NftToken == context.blockchain)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ public class TangemContext {
|
|||
return Blockchain.Token;
|
||||
}
|
||||
}
|
||||
if (blockchain == Blockchain.Ethereum && card.isIDCard()) {
|
||||
return Blockchain.EthereumId;
|
||||
}
|
||||
if ((blockchain == Blockchain.Rootstock) && card.isToken()) {
|
||||
return Blockchain.RootstockToken;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@ import android.net.Uri;
|
|||
import android.text.InputFilter;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.tangem.App;
|
||||
import com.tangem.Constant;
|
||||
import com.tangem.data.Blockchain;
|
||||
import com.tangem.data.network.ServerApiBlockcypher;
|
||||
import com.tangem.data.network.ServerApiInfura;
|
||||
|
|
@ -18,11 +15,11 @@ import com.tangem.data.network.model.InfuraResponse;
|
|||
import com.tangem.tangem_card.data.TangemCard;
|
||||
import com.tangem.tangem_card.reader.CardProtocol;
|
||||
import com.tangem.tangem_card.tasks.SignTask;
|
||||
import com.tangem.tangem_card.util.Util;
|
||||
import com.tangem.util.CryptoUtil;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.wallet.BTCUtils;
|
||||
import com.tangem.wallet.BalanceValidator;
|
||||
import com.tangem.wallet.BuildConfig;
|
||||
import com.tangem.wallet.CoinData;
|
||||
import com.tangem.wallet.CoinEngine;
|
||||
import com.tangem.wallet.ECDSASignatureETH;
|
||||
|
|
@ -32,7 +29,6 @@ import com.tangem.wallet.R;
|
|||
import com.tangem.wallet.TangemContext;
|
||||
import com.tangem.wallet.eth.EthData;
|
||||
|
||||
import org.bitcoin.Secp256k1Context;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.SignatureDecodeException;
|
||||
import org.bitcoinj.crypto.ChildNumber;
|
||||
|
|
@ -44,21 +40,19 @@ import java.math.BigInteger;
|
|||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
|
||||
import io.github.novacrypto.bip32.ExtendedPublicKey;
|
||||
|
||||
public class EthIdEngine extends CoinEngine {
|
||||
|
||||
private static final String TAG = com.tangem.wallet.eth.EthEngine.class.getSimpleName();
|
||||
public EthIdData coinData = null;
|
||||
String approvalAddress = ""; //TODO
|
||||
byte[] approvalPubKey = new byte[1]; //TODO
|
||||
byte[] approvalPubKey = Util.hexToBytes("04EAD74FEEE4061044F46B19EB654CEEE981E9318F0C8FE99AF5CDB9D779D2E52BB51EA2D14545E0B323F7A90CF4CC72753C973149009C10DB2D83DCEC28487729"); //TODO
|
||||
public String approvalAddress = calculateAddress(approvalPubKey); //TODO
|
||||
|
||||
public EthIdEngine(TangemContext ctx) throws Exception {
|
||||
super(ctx);
|
||||
if (ctx.getCoinData() == null) {
|
||||
coinData = new EthIdData();
|
||||
ctx.setCoinData(coinData);
|
||||
} else if (ctx.getCoinData() instanceof EthData) {
|
||||
} else if (ctx.getCoinData() instanceof EthIdData) {
|
||||
coinData = (EthIdData) ctx.getCoinData();
|
||||
} else {
|
||||
throw new Exception("Invalid type of Blockchain data for " + this.getClass().getSimpleName());
|
||||
|
|
@ -74,29 +68,29 @@ public class EthIdEngine extends CoinEngine {
|
|||
}
|
||||
|
||||
private int getChainIdNum() {
|
||||
return ctx.getBlockchain() == Blockchain.Ethereum ? EthTransaction.ChainEnum.Mainnet.getValue() : EthTransaction.ChainEnum.Rinkeby.getValue();
|
||||
return EthTransaction.ChainEnum.Mainnet.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitingConfirmation() {
|
||||
return !coinData.getUnconfirmedTXCount().equals(coinData.getConfirmedTXCount()) || App.pendingTransactionsStorage.hasTransactions(ctx.getCard());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Amount getBalance() {
|
||||
if (!hasBalanceInfo()) {
|
||||
return null;
|
||||
}
|
||||
return convertToAmount(coinData.getBalanceInInternalUnits());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBalanceHTML() {
|
||||
Amount balance = getBalance();
|
||||
if (balance != null) {
|
||||
return balance.toDescriptionString(getDecimals());
|
||||
if (coinData != null) {
|
||||
if (coinData.isHasApprovalTx()) {
|
||||
return "VERIFIED";
|
||||
} else {
|
||||
return "NOT REGISTERED";
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
return "NOT REGISTERED";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,9 +101,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public boolean isBalanceNotZero() {
|
||||
if (coinData == null) return false;
|
||||
if (coinData.getBalanceInInternalUnits() == null) return false;
|
||||
return coinData.getBalanceInInternalUnits().notZero();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -124,7 +116,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public CoinData createCoinData() {
|
||||
return new EthData();
|
||||
return new EthIdData();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -132,14 +124,6 @@ public class EthIdEngine extends CoinEngine {
|
|||
return "";
|
||||
}
|
||||
|
||||
// BigDecimal convertToEth(String value) {
|
||||
// BigInteger m = new BigInteger(value, 10);
|
||||
// BigDecimal n = new BigDecimal(m);
|
||||
// BigDecimal d = n.divide(new BigDecimal("1000000000000000000"));
|
||||
// d = d.setScale(8, RoundingMode.DOWN);
|
||||
// return d;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean validateAddress(String address) {
|
||||
if (address == null || address.isEmpty()) {
|
||||
|
|
@ -157,43 +141,6 @@ public class EthIdEngine extends CoinEngine {
|
|||
return true;
|
||||
}
|
||||
|
||||
// public String getBalanceValue(TangemCard mCard) {
|
||||
// String dec = coinData.getBalanceInInternalUnits();
|
||||
// BigDecimal d = convertToEth(dec);
|
||||
// String s = d.toString();
|
||||
//
|
||||
// String pattern = "#0.##################"; // If you like 4 zeros
|
||||
// DecimalFormat myFormatter = new DecimalFormat(pattern);
|
||||
// String output = myFormatter.format(d);
|
||||
// return output;
|
||||
// }
|
||||
|
||||
// public static String getAmountEquivalentDescription(Amount amount, double rateValue) {
|
||||
// if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0)
|
||||
// return "";
|
||||
//
|
||||
// if (rateValue > 0) {
|
||||
// BigDecimal biRate = new BigDecimal(rateValue);
|
||||
// BigDecimal exchangeCurs = biRate.multiply(amount);
|
||||
// exchangeCurs = exchangeCurs.setScale(2, RoundingMode.DOWN);
|
||||
// return "≈ USD " + exchangeCurs.toString();
|
||||
// } else {
|
||||
// return "";
|
||||
// }
|
||||
// }
|
||||
|
||||
// public static String getAmountEquivalentDescriptionETH(Double amount, float rate) {
|
||||
// if (amount == 0)
|
||||
// return "";
|
||||
// amount = amount / 100000;
|
||||
// if (rate > 0) {
|
||||
// return String.format("≈ USD %.2f", amount * rate);
|
||||
// } else {
|
||||
// return "";
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public String getBalanceEquivalent() {
|
||||
|
|
@ -232,7 +179,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public boolean hasBalanceInfo() {
|
||||
return coinData.getBalanceInInternalUnits() != null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -273,88 +220,27 @@ public class EthIdEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public boolean checkNewTransactionAmount(Amount amount) {
|
||||
if (BuildConfig.FLAVOR == Constant.FLAVOR_TANGEM_CARDANO) {
|
||||
return true;
|
||||
}
|
||||
Amount balance = getBalance();
|
||||
if (balance == null || amount.compareTo(balance) > 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkNewTransactionAmountAndFee(Amount amount, Amount fee, Boolean isFeeIncluded) {
|
||||
// Long fee = null;
|
||||
// Long amount = null;
|
||||
// try {
|
||||
// amount = mCard.internalUnitsFromString(amountValue);
|
||||
// fee = mCard.internalUnitsFromString(feeValue);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// if (fee == null || amount == null)
|
||||
// return false;
|
||||
//
|
||||
// if (fee == 0 || amount == 0)
|
||||
// return false;
|
||||
//
|
||||
//
|
||||
// if (fee < minFeeInInternalUnits)
|
||||
// return false;
|
||||
|
||||
try {
|
||||
BigDecimal cardBalance = getBalance();
|
||||
|
||||
if (isFeeIncluded && (amount.compareTo(cardBalance) > 0 || amount.compareTo(fee) < 0))
|
||||
return false;
|
||||
|
||||
if (!isFeeIncluded && amount.add(fee).compareTo(cardBalance) > 0)
|
||||
return false;
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateBalance(BalanceValidator balanceValidator) {
|
||||
if (getBalance() == null) {
|
||||
if (coinData != null && coinData.isHasApprovalTx()) {
|
||||
balanceValidator.setScore(100);
|
||||
balanceValidator.setFirstLine(R.string.verified);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
|
||||
return false;
|
||||
} else {
|
||||
balanceValidator.setScore(0);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
|
||||
balanceValidator.setFirstLine(R.string.not_registered);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!coinData.getUnconfirmedTXCount().equals(coinData.getConfirmedTXCount())) {
|
||||
balanceValidator.setScore(0);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (coinData.isBalanceReceived()) {
|
||||
balanceValidator.setScore(100);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
|
||||
if (getBalance().isZero()) {
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
|
||||
balanceValidator.setSecondLine(R.string.empty_string);
|
||||
}
|
||||
}
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && getBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -392,29 +278,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
@Override
|
||||
public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) {
|
||||
|
||||
Log.e(TAG, "Construct transaction " + amountValue.toString() + " with fee " + feeValue.toString() + (IncFee ? " including" : " excluding"));
|
||||
|
||||
BigInteger nonceValue = coinData.getConfirmedTXCount();
|
||||
byte[] pbKey = ctx.getCard().getWalletPublicKey();
|
||||
|
||||
BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact();
|
||||
BigInteger weiAmount = convertToInternalAmount(amountValue).toBigIntegerExact();
|
||||
|
||||
if (IncFee) {
|
||||
weiAmount = weiAmount.subtract(weiFee);
|
||||
}
|
||||
|
||||
BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(21000));
|
||||
BigInteger gasLimit = BigInteger.valueOf(21000);
|
||||
Integer chainId = this.getChainIdNum();
|
||||
|
||||
String to = targetAddress;
|
||||
|
||||
if (to.startsWith("0x") || to.startsWith("0X")) {
|
||||
to = to.substring(2);
|
||||
}
|
||||
|
||||
final EthTransaction tx = EthTransaction.create(to, weiAmount, nonceValue, gasPrice, gasLimit, chainId);
|
||||
final EthTransaction tx = constructIdTxForSign(coinData.getApprovalAddressNonce());
|
||||
|
||||
return new SignTask.TransactionToSign() {
|
||||
@Override
|
||||
|
|
@ -451,14 +315,14 @@ public class EthIdEngine extends CoinEngine {
|
|||
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
|
||||
s = CryptoUtil.toCanonicalised(s);
|
||||
|
||||
boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
|
||||
boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), approvalPubKey);
|
||||
|
||||
if (!f) {
|
||||
Log.e(this.getClass().getSimpleName() + "-CHECK", "sign Failed.");
|
||||
}
|
||||
|
||||
tx.signature = new ECDSASignatureETH(r, s);
|
||||
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
|
||||
int v = tx.BruteRecoveryID2(tx.signature, for_hash, approvalPubKey);
|
||||
if (v != 27 && v != 28) {
|
||||
Log.e(this.getClass().getSimpleName(), "invalid v");
|
||||
throw new Exception("Error in " + this.getClass().getSimpleName() + " - invalid v");
|
||||
|
|
@ -486,7 +350,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
//TODO: change request logic, 2000 tx max
|
||||
if (blockcypherResponse.getTxrefs() != null) {
|
||||
for (BlockcypherTxref txref : blockcypherResponse.getTxrefs()) {
|
||||
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txref.getTx_hash());
|
||||
serverApiBlockcypher.requestData("ETH", ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txref.getTx_hash());
|
||||
}
|
||||
|
||||
if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
|
||||
|
|
@ -494,6 +358,8 @@ public class EthIdEngine extends CoinEngine {
|
|||
} else {
|
||||
blockchainRequestsCallbacks.onProgress();
|
||||
}
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
@ -516,7 +382,11 @@ public class EthIdEngine extends CoinEngine {
|
|||
public void onSuccess(BlockcypherTx response) {
|
||||
Log.i(TAG, "onSuccess: BlockcypherTx");
|
||||
try {
|
||||
if (response.getAddesses().contains(approvalAddress)) {
|
||||
String address = approvalAddress;
|
||||
if (address.startsWith("0x") || address.startsWith("0X")) {
|
||||
address = address.substring(2);
|
||||
}
|
||||
if (response.getAddesses().contains(address)) {
|
||||
coinData.setHasApprovalTx(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -543,7 +413,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
serverApiBlockcypher.setResponseListener(blockcypherListener);
|
||||
serverApiBlockcypher.setTxResponseListener(txListener);
|
||||
|
||||
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), "");
|
||||
serverApiBlockcypher.requestData("ETH", ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), "");
|
||||
requestApprovalAddressNonce();
|
||||
}
|
||||
|
||||
|
|
@ -599,7 +469,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
@Override
|
||||
public void defineWallet() throws CardProtocol.TangemException {
|
||||
try {
|
||||
String wallet = calculateAddress(calculateCKDpub(ctx.getIdHash)); //TODO
|
||||
String wallet = calculateAddress(calculateCKDpub(ctx.getCard().getIdHash()));
|
||||
ctx.getCoinData().setWallet(wallet);
|
||||
} catch (Exception e) {
|
||||
ctx.getCoinData().setWallet("ERROR");
|
||||
|
|
@ -630,7 +500,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
to = to.substring(2);
|
||||
}
|
||||
|
||||
return EthTransaction.create(to, weiAmount, nonceValue, gasPrice, gasLimit, chainId);
|
||||
return EthTransaction.create(to, weiAmount, nonceValue, gasPrice, gasLimit, chainId);
|
||||
}
|
||||
|
||||
private boolean checkSignature(byte[] signature, long nonce) throws SignatureDecodeException {
|
||||
|
|
@ -651,6 +521,7 @@ public class EthIdEngine extends CoinEngine {
|
|||
Long count = Long.valueOf(pending, 16);
|
||||
coinData.setApprovalAddressNonce(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(String method, String message) {
|
||||
Log.e(TAG, "onFail: " + method + " " + message);
|
||||
|
|
@ -658,6 +529,6 @@ public class EthIdEngine extends CoinEngine {
|
|||
};
|
||||
serverApiInfura.setResponseListener(responseListener);
|
||||
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, approvalAddress, "", "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
app/src/main/res/drawable/ic_add_a_photo.xml
Normal file
10
app/src/main/res/drawable/ic_add_a_photo.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#505050"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3,4L3,1h2v3h3v2L5,6v3L3,9L3,6L0,6L0,4h3zM6,10L6,7h3L9,4h7l1.83,2L21,6c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2L5,22c-1.1,0 -2,-0.9 -2,-2L3,10h3zM13,19c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5 -5,2.24 -5,5 2.24,5 5,5zM9.8,14c0,1.77 1.43,3.2 3.2,3.2s3.2,-1.43 3.2,-3.2 -1.43,-3.2 -3.2,-3.2 -3.2,1.43 -3.2,3.2z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_flip_camera.xml
Normal file
9
app/src/main/res/drawable/ic_flip_camera.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/colorAccent"
|
||||
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
|
||||
</vector>
|
||||
4
app/src/main/res/drawable/ic_person.xml
Normal file
4
app/src/main/res/drawable/ic_person.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="100dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="100dp" android:tint="#505050" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_take_picture.xml
Normal file
9
app/src/main/res/drawable/ic_take_picture.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/colorAccent"
|
||||
android:pathData="M9.4,10.5l4.77,-8.26C13.47,2.09 12.75,2 12,2c-2.4,0 -4.6,0.85 -6.32,2.25l3.66,6.35 0.06,-0.1zM21.54,9c-0.92,-2.92 -3.15,-5.26 -6,-6.34L11.88,9h9.66zM21.8,10h-7.49l0.29,0.5 4.76,8.25C21,16.97 22,14.61 22,12c0,-0.69 -0.07,-1.35 -0.2,-2zM8.54,12l-3.9,-6.75C3.01,7.03 2,9.39 2,12c0,0.69 0.07,1.35 0.2,2h7.49l-1.15,-2zM2.46,15c0.92,2.92 3.15,5.26 6,6.34L12.12,15L2.46,15zM13.73,15l-3.9,6.76c0.7,0.15 1.42,0.24 2.17,0.24 2.4,0 4.6,-0.85 6.32,-2.25l-3.66,-6.35 -0.93,1.6z"/>
|
||||
</vector>
|
||||
52
app/src/main/res/layout/fragment_camera.xml
Normal file
52
app/src/main/res/layout/fragment_camera.xml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<com.otaliastudios.cameraview.CameraView
|
||||
android:id="@+id/cvCamera"
|
||||
android:layout_width="350dp"
|
||||
android:layout_height="350dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="40dp"
|
||||
app:cameraAudio="off"
|
||||
app:cameraFlash="auto"
|
||||
app:cameraMode="picture"
|
||||
app:cameraPictureSizeMaxHeight="200"
|
||||
app:cameraPictureSizeMaxWidth="200"
|
||||
app:cameraPictureSizeMinHeight="200"
|
||||
app:cameraPictureSizeMinWidth="200"
|
||||
app:cameraPictureSizeSmallest="true"
|
||||
app:cameraRequestPermissions="true"
|
||||
app:cameraSnapshotMaxHeight="200"
|
||||
app:cameraSnapshotMaxWidth="200"
|
||||
app:cameraUseDeviceOrientation="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivTakePicture"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="80dp"
|
||||
android:src="@drawable/ic_take_picture"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/cvCamera" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivFlipCamera"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/ic_flip_camera"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ivTakePicture" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
242
app/src/main/res/layout/fragment_id.xml
Normal file
242
app/src/main/res/layout/fragment_id.xml
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:padding="0dp"
|
||||
tools:ignore="contentDescription">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rlAuxButtons"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="false"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/loCard"
|
||||
layout="@layout/layout_id" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintHeight_min="wrap"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loCard">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/loBalance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/guideline"
|
||||
app:layout_constraintHeight_min="wrap"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBalanceLine1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:textAlignment="center"
|
||||
android:visibility="visible"
|
||||
tools:text="Verified in blockchain"
|
||||
tools:textColor="@color/confirmed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBalanceLine2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:textAlignment="center"
|
||||
android:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.4" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/loIdentificationData"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical|center_horizontal"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHeight_min="wrap"
|
||||
app:layout_constraintTop_toBottomOf="@+id/guideline">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvIdNumber"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/primary_dark"
|
||||
android:textSize="18dp"
|
||||
android:textStyle="normal"
|
||||
tools:text="ID # 203129301239" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFullName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/primary_dark"
|
||||
android:textSize="28dp"
|
||||
android:textStyle="normal"
|
||||
tools:text="John Doe" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBirthDate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="@dimen/text_size_1_small"
|
||||
tools:text="2000-01-01" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvGender"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="@dimen/text_size_1_small"
|
||||
tools:text="Sex: M" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/rlMessageAndError"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rlAuxButtons">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/msg_okay"
|
||||
android:elevation="2dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:padding="4dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="normal|bold"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvError"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/tvMessage"
|
||||
android:layout_alignParentBottom="false"
|
||||
android:background="@color/msg_err"
|
||||
android:elevation="3dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:padding="4dp"
|
||||
android:shadowColor="@color/cardview_shadow_star"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rlToolButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rlAuxButtons"
|
||||
android:visibility="gone">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnIssueNewId"
|
||||
style="@style/AppTheme.RoundedCornerMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="1"
|
||||
android:enabled="true"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="Issue New Id"
|
||||
android:textSize="@dimen/text_size_large"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rlAuxButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<include layout="@layout/layout_btn_details" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnNewScan"
|
||||
style="@style/AppTheme.RoundedCornerMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableStart="@drawable/ic_nfc_white_24dp"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:text="@string/loaded_wallet_btn_new_scan"
|
||||
android:textColor="@color/primary_dark" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
144
app/src/main/res/layout/fragment_issue_new_id.xml
Normal file
144
app/src/main/res/layout/fragment_issue_new_id.xml
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:padding="0dp"
|
||||
tools:ignore="contentDescription">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivPhoto"
|
||||
app:layout_constraintHorizontal_bias=".46"
|
||||
android:layout_width="180dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/ic_add_a_photo"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etName"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:inputType="textCapWords"
|
||||
android:hint="First Name"
|
||||
android:layout_marginTop="24dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/ivPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSurname"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:inputType="textCapWords"
|
||||
android:hint="Last Name"
|
||||
android:paddingBottom="16dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/etName"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBirthDate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:hint="Enter Date of Birth"
|
||||
android:paddingTop="14dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/etSurname" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etBirthDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:hint="dd/mm/yyyy"
|
||||
android:inputType="date"
|
||||
android:paddingBottom="16dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvBirthDate"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:layout_editor_absoluteX="0dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSelectGender"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:hint="Select Gender"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/etBirthDate" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rgGender"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvSelectGender">
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/rbFemale"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:text="Female"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/rbMale"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:text="Male"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rlToolButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnConfirm"
|
||||
style="@style/AppTheme.RoundedCornerMaterialButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/saira_condensed_bold"
|
||||
android:text="Confirm"
|
||||
android:textSize="@dimen/text_size_large" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
51
app/src/main/res/layout/fragment_validate_id.xml
Normal file
51
app/src/main/res/layout/fragment_validate_id.xml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_touch_card"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="250dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.75">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="Now touch the Issuer Key card"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="to confirm the new ID data"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/layout_progress_horizontal" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
61
app/src/main/res/layout/fragment_write_id.xml
Normal file
61
app/src/main/res/layout/fragment_write_id.xml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_touch_card"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="250dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.75">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="Now touch the ID card"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardID"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
tools:text="CB02 0000 0002 5000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="to write the new ID data"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/text_size_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/layout_progress_horizontal" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
59
app/src/main/res/layout/layout_id.xml
Normal file
59
app/src/main/res/layout/layout_id.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout 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/loCard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:maxHeight="400dp"
|
||||
android:padding="3dp"
|
||||
app:layout_constraintHeight_max="350dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="contentDescription"
|
||||
tools:showIn="@layout/fragment_id">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivPhoto"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="400dp"
|
||||
android:src="@drawable/id_card_default"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/rlProgressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:elevation="3dp"
|
||||
android:padding="5dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0.75"
|
||||
android:background="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:alpha="1"
|
||||
android:fontFamily="@font/maaxmedium"
|
||||
android:text="@string/loaded_wallet_status_verifying"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="12dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="@dimen/size_progress_bar_big"
|
||||
android:layout_height="@dimen/size_progress_bar_big"
|
||||
android:layout_centerInParent="true"
|
||||
android:alpha="1"
|
||||
android:elevation="1dp"
|
||||
android:progressTint="@color/colorPrimary" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
@ -86,6 +86,7 @@
|
|||
android:background="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
<action
|
||||
android:id="@+id/action_main_to_tagFragment"
|
||||
app:destination="@id/tagFragment" />
|
||||
<action
|
||||
android:id="@+id/action_main_to_idFragment"
|
||||
app:destination="@id/idFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
|
@ -196,5 +199,43 @@
|
|||
android:name="com.tangem.ui.fragment.additional.TagFragment"
|
||||
android:label="fragment_tag"
|
||||
tools:layout="@layout/fragment_tag" />
|
||||
<fragment
|
||||
android:id="@+id/issueNewIdFragment"
|
||||
android:name="com.tangem.ui.fragment.id.IssueNewIdFragment"
|
||||
android:label="IssueNewIdFragment" >
|
||||
<action
|
||||
android:id="@+id/action_issueNewIdFragment_to_cameraFragment"
|
||||
app:destination="@id/cameraFragment" />
|
||||
<action
|
||||
android:id="@+id/action_issueNewIdFragment_to_validateIdFragment"
|
||||
app:destination="@id/validateIdFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/idFragment"
|
||||
android:name="com.tangem.ui.fragment.id.IdFragment"
|
||||
android:label="IdFragment" >
|
||||
<action
|
||||
android:id="@+id/action_idFragment_to_issueNewIdFragment"
|
||||
app:destination="@id/issueNewIdFragment" />
|
||||
<action
|
||||
android:id="@+id/action_idFragment_to_verifyCard"
|
||||
app:destination="@id/verifyCard" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/cameraFragment"
|
||||
android:name="com.tangem.ui.fragment.id.CameraFragment"
|
||||
android:label="CameraFragment" />
|
||||
<fragment
|
||||
android:id="@+id/validateIdFragment"
|
||||
android:name="com.tangem.ui.fragment.id.ValidateIdFragment"
|
||||
android:label="ValidateIdFragment" >
|
||||
<action
|
||||
android:id="@+id/action_validateIdFragment_to_writeIdFragment"
|
||||
app:destination="@id/writeIdFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/writeIdFragment"
|
||||
android:name="com.tangem.ui.fragment.id.WriteIdFragment"
|
||||
android:label="WriteIdFragment" />
|
||||
|
||||
</navigation>
|
||||
|
|
@ -270,6 +270,10 @@
|
|||
<string name="tag_not_genuine">NOT GENUINE</string>
|
||||
<string name="tag_claimed">ALREADY CLAIMED</string>
|
||||
|
||||
<!-- Id Card -->
|
||||
<string name="verified">VERIFIED</string>
|
||||
<string name="not_registered">NOT REGISTERED</string>
|
||||
|
||||
<!-- PinSave -->
|
||||
<string name="pin_save_btn_save">Save</string>
|
||||
<string name="pin_save_btn_delete">Delete</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tangem_card.data
|
||||
|
||||
enum class ProductMask(val code: Byte) {
|
||||
Note(0x01),
|
||||
Tag(0x02),
|
||||
Id(0x04);
|
||||
|
||||
companion object {
|
||||
private val values = values()
|
||||
fun byCode(code: Byte): ProductMask? = values.find { it.code == code }
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ public class TangemCard {
|
|||
private byte[] terminalPublicKey;
|
||||
private boolean terminalIsLinked = false;
|
||||
private byte[] tagSignature = null;
|
||||
private byte[] idHash = null;
|
||||
|
||||
public String getBlockchainID() {
|
||||
return blockchainID;
|
||||
|
|
@ -189,6 +190,14 @@ public class TangemCard {
|
|||
this.terminalIsLinked = terminalIsLinked;
|
||||
}
|
||||
|
||||
public byte[] getIdHash() {
|
||||
return idHash;
|
||||
}
|
||||
|
||||
public void setIdHash(byte[] idHash) {
|
||||
this.idHash = idHash;
|
||||
}
|
||||
|
||||
private int health = 0;
|
||||
|
||||
public int getHealth() {
|
||||
|
|
@ -621,7 +630,32 @@ public class TangemCard {
|
|||
}
|
||||
}
|
||||
|
||||
TLVList tlvIDCardData = null;
|
||||
public TLVList tlvIDCardData = null;
|
||||
|
||||
public void setTlvIdCardData(TLVList tlvIDCardData) {
|
||||
this.tlvIDCardData = tlvIDCardData;
|
||||
}
|
||||
|
||||
public void setTlvIDCardData(
|
||||
String fullName,
|
||||
String birthday,
|
||||
String gender,
|
||||
byte[] photo,
|
||||
String issueDate,
|
||||
String expireDate,
|
||||
String trustedAddress) {
|
||||
|
||||
IDCardData data = new IDCardData();
|
||||
data.fullName = fullName;
|
||||
data.birthday = birthday;
|
||||
data.gender = gender;
|
||||
data.photo = photo;
|
||||
data.issueDate = issueDate;
|
||||
data.expireDate = expireDate;
|
||||
data.trustedAddress = trustedAddress;
|
||||
|
||||
this.tlvIDCardData = data.toTLVList();
|
||||
}
|
||||
|
||||
public boolean hasIDCardData() {
|
||||
if (tlvIDCardData == null) {
|
||||
|
|
@ -638,8 +672,7 @@ public class TangemCard {
|
|||
&& tlvIDCardData.hasTag(TLV.Tag.TAG_IssueDate) && tlvIDCardData.hasTag(TLV.Tag.TAG_ExpireDate) && tlvIDCardData.hasTag(TLV.Tag.TAG_TrustedAddress);
|
||||
}
|
||||
|
||||
public static class IDCardData
|
||||
{
|
||||
public static class IDCardData {
|
||||
public String fullName;
|
||||
public String birthday;
|
||||
public String gender;
|
||||
|
|
@ -649,34 +682,32 @@ public class TangemCard {
|
|||
public String trustedAddress;
|
||||
|
||||
public TLVList toTLVList() {
|
||||
TLVList tlvIDCardData=new TLVList();
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_FullName,fullName.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_Birthday,birthday.getBytes(StandardCharsets.UTF_8)));
|
||||
TLVList tlvIDCardData = new TLVList();
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_FullName, fullName.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_Birthday, birthday.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_Gender, gender.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_Photo, photo));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_IssueDate,issueDate.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_ExpireDate,expireDate.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_TrustedAddress,trustedAddress.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_IssueDate, issueDate.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_ExpireDate, expireDate.getBytes(StandardCharsets.UTF_8)));
|
||||
tlvIDCardData.add(new TLV(TLV.Tag.TAG_TrustedAddress, trustedAddress.getBytes(StandardCharsets.UTF_8)));
|
||||
return tlvIDCardData;
|
||||
}
|
||||
|
||||
public static IDCardData fromTLVList(TLVList tlvIDCardData)
|
||||
{
|
||||
IDCardData result=new IDCardData();
|
||||
result.fullName=tlvIDCardData.getTLV(TLV.Tag.TAG_FullName).getAsString();
|
||||
result.birthday=tlvIDCardData.getTLV(TLV.Tag.TAG_Birthday).getAsString();
|
||||
result.gender=tlvIDCardData.getTLV(TLV.Tag.TAG_Gender).getAsString();
|
||||
result.photo=tlvIDCardData.getTLV(TLV.Tag.TAG_Photo).Value;
|
||||
result.issueDate=tlvIDCardData.getTLV(TLV.Tag.TAG_IssueDate).getAsString();
|
||||
result.expireDate=tlvIDCardData.getTLV(TLV.Tag.TAG_ExpireDate).getAsString();
|
||||
result.trustedAddress=tlvIDCardData.getTLV(TLV.Tag.TAG_TrustedAddress).getAsString();
|
||||
public static IDCardData fromTLVList(TLVList tlvIDCardData) {
|
||||
IDCardData result = new IDCardData();
|
||||
result.fullName = tlvIDCardData.getTLV(TLV.Tag.TAG_FullName).getAsString();
|
||||
result.birthday = tlvIDCardData.getTLV(TLV.Tag.TAG_Birthday).getAsString();
|
||||
result.gender = tlvIDCardData.getTLV(TLV.Tag.TAG_Gender).getAsString();
|
||||
result.photo = tlvIDCardData.getTLV(TLV.Tag.TAG_Photo).Value;
|
||||
result.issueDate = tlvIDCardData.getTLV(TLV.Tag.TAG_IssueDate).getAsString();
|
||||
result.expireDate = tlvIDCardData.getTLV(TLV.Tag.TAG_ExpireDate).getAsString();
|
||||
result.trustedAddress = tlvIDCardData.getTLV(TLV.Tag.TAG_TrustedAddress).getAsString();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public IDCardData getIDCardData()
|
||||
{
|
||||
if( !hasIDCardData() ) return null;
|
||||
public IDCardData getIDCardData() {
|
||||
if (!hasIDCardData()) return null;
|
||||
return IDCardData.fromTLVList(tlvIDCardData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -964,9 +964,11 @@ public class CardProtocol {
|
|||
}
|
||||
|
||||
private void prepareDataForLinkingTerminal(CommandApdu apdu, byte[] data) throws Exception {
|
||||
byte[] transactionSignature = CardCrypto.Signature(mCard.getTerminalPrivateKey(), data);
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_TransactionSignature, transactionSignature);
|
||||
addTerminalPublicKeyToApdu(apdu);
|
||||
if (mCard.getTerminalPrivateKey() != null) {
|
||||
byte[] transactionSignature = CardCrypto.Signature(mCard.getTerminalPrivateKey(), data);
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_TransactionSignature, transactionSignature);
|
||||
addTerminalPublicKeyToApdu(apdu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ import com.tangem.tangem_card.util.Util;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class TLVList extends ArrayList<TLV> {
|
||||
public class TLVList extends ArrayList<TLV> implements Serializable {
|
||||
public String getParsedTLVs(String Prefix) {
|
||||
String parsed = "";
|
||||
for (int i = 0; i < size(); i++) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tangem_sdk.data
|
|||
import android.os.Bundle
|
||||
import com.tangem.tangem_card.data.Manufacturer
|
||||
import com.tangem.tangem_card.data.TangemCard
|
||||
import com.tangem.tangem_card.reader.TLVList
|
||||
import com.tangem.tangem_card.util.Log
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -93,6 +94,8 @@ fun TangemCard.loadFromBundle(B: Bundle) {
|
|||
terminalIsLinked = B.getBoolean("terminalIsLinked")
|
||||
|
||||
if (B.containsKey("tagSignature")) tagSignature = B.getByteArray("tagSignature")
|
||||
if (B.containsKey("idHash")) idHash = B.getByteArray("idHash")
|
||||
if (B.containsKey("tlvIDCardData")) tlvIDCardData = B.getSerializable("tlvIDCardData") as TLVList
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +184,9 @@ fun TangemCard.saveToBundle(B: Bundle) {
|
|||
|
||||
if (tagSignature != null) B.putByteArray("tagSignature", tagSignature)
|
||||
|
||||
if (idHash != null) B.putByteArray("idHash", idHash)
|
||||
if (tlvIDCardData != null) B.putSerializable("tlvIDCardData", tlvIDCardData)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("Can't save to bundle ", e.message)
|
||||
}
|
||||
|
|
|
|||
6
tangem-sdk-old/src/main/res/drawable/id_card_default.xml
Normal file
6
tangem-sdk-old/src/main/res/drawable/id_card_default.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector android:height="36dp" android:tint="#505050"
|
||||
android:viewportHeight="36.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue