diff --git a/app/src/main/java/com/tangem/ui/fragment/PurgeFragment.kt b/app/src/main/java/com/tangem/ui/fragment/PurgeFragment.kt index 76d8d97dc6..2130c741a9 100644 --- a/app/src/main/java/com/tangem/ui/fragment/PurgeFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/PurgeFragment.kt @@ -66,7 +66,7 @@ class PurgeFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.No super.onViewCreated(view, savedInstanceState) // init NFC Antenna - nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) + nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) nfcDeviceAntenna.init() tvCardID.text = ctx.card.cidDescription @@ -99,7 +99,7 @@ class PurgeFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.No } override fun onReadWait(msec: Int) { - WaitSecurityDelayDialog.onReadWait(activity, msec) + activity?.let { WaitSecurityDelayDialog.onReadWait(it, msec) } // val readWait = ReadWait() // readWait.msec = msec @@ -107,7 +107,7 @@ class PurgeFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.No } override fun onReadBeforeRequest(timeout: Int) { - WaitSecurityDelayDialog.onReadBeforeRequest(activity, timeout) + activity?.let { WaitSecurityDelayDialog.onReadBeforeRequest(it, timeout) } // if (!waitSecurityDelayDialogNew.isAdded) // waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG) @@ -119,16 +119,16 @@ class PurgeFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.No } override fun onReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(activity) + activity?.let { WaitSecurityDelayDialog.onReadAfterRequest(it) } // val readAfterRequest = ReadAfterRequest() // EventBus.getDefault().post(readAfterRequest) } override fun onReadStart(cardProtocol: CardProtocol) { - rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE } + rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE } - progressBar.post { + progressBar?.post { progressBar.visibility = View.VISIBLE progressBar.progress = 5 } @@ -179,7 +179,7 @@ class PurgeFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.No progressBar?.post { if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) { if (!NoExtendedLengthSupportDialog.allReadyShowed) - NoExtendedLengthSupportDialog().show(activity!!.supportFragmentManager, NoExtendedLengthSupportDialog.TAG) + NoExtendedLengthSupportDialog().show(requireActivity().supportFragmentManager, NoExtendedLengthSupportDialog.TAG) } else Toast.makeText(context, R.string.general_notification_scan_again_to_verify, Toast.LENGTH_LONG).show() diff --git a/app/src/main/java/com/tangem/ui/fragment/id/ValidateIdFragment.kt b/app/src/main/java/com/tangem/ui/fragment/id/ValidateIdFragment.kt index 61019f8ab9..ebbc5c2191 100644 --- a/app/src/main/java/com/tangem/ui/fragment/id/ValidateIdFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/id/ValidateIdFragment.kt @@ -77,7 +77,7 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener, mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound) // init NFC Antenna - nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) + nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) nfcDeviceAntenna.init() progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY) @@ -198,7 +198,7 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener, } override fun onReadStart(cardProtocol: CardProtocol) { - rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE } + rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE } progressBar?.post { progressBar?.visibility = View.VISIBLE @@ -265,7 +265,7 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener, NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG) } } else { - (activity as MainActivity).toastHelper.showSingleToast( + (activity as? MainActivity)?.toastHelper?.showSingleToast( context, getString(R.string.general_notification_scan_again) ) @@ -313,17 +313,17 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener, override fun onReadBeforeRequest(timeout: Int) { LOG.i(TAG, "onReadBeforeRequest timeout $timeout") - WaitSecurityDelayDialog.onReadBeforeRequest(activity!!, timeout) + activity?.let { WaitSecurityDelayDialog.onReadBeforeRequest(it, timeout) } } override fun onReadAfterRequest() { LOG.i(TAG, "onReadAfterRequest") - WaitSecurityDelayDialog.onReadAfterRequest(activity!!) + activity?.let { WaitSecurityDelayDialog.onReadAfterRequest(it) } } override fun onReadWait(msec: Int) { LOG.i(TAG, "onReadWait msec $msec") - WaitSecurityDelayDialog.onReadWait(activity!!, msec) + activity?.let { WaitSecurityDelayDialog.onReadWait(it, msec) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/ui/fragment/id/WriteIdFragment.kt b/app/src/main/java/com/tangem/ui/fragment/id/WriteIdFragment.kt index 03e96331c8..e0baa7b79d 100644 --- a/app/src/main/java/com/tangem/ui/fragment/id/WriteIdFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/id/WriteIdFragment.kt @@ -77,7 +77,7 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener, mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound) // init NFC Antenna - nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) + nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) nfcDeviceAntenna.init() tvCardID.text = ctx.card!!.cidDescription @@ -137,7 +137,7 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener, } override fun onReadStart(cardProtocol: CardProtocol) { - rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE } + rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE } progressBar?.post { progressBar?.visibility = View.VISIBLE @@ -279,17 +279,17 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener, override fun onReadBeforeRequest(timeout: Int) { LOG.i(TAG, "onReadBeforeRequest timeout $timeout") - WaitSecurityDelayDialog.onReadBeforeRequest(activity!!, timeout) + activity?.let { WaitSecurityDelayDialog.onReadBeforeRequest(it, timeout) } } override fun onReadAfterRequest() { LOG.i(TAG, "onReadAfterRequest") - WaitSecurityDelayDialog.onReadAfterRequest(activity!!) + activity?.let { WaitSecurityDelayDialog.onReadAfterRequest(it) } } override fun onReadWait(msec: Int) { LOG.i(TAG, "onReadWait msec $msec") - WaitSecurityDelayDialog.onReadWait(activity!!, msec) + activity?.let { WaitSecurityDelayDialog.onReadWait(it, msec) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt index e1055fbfce..2eca438aa8 100644 --- a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt @@ -30,7 +30,6 @@ import com.tangem.server_android.model.CardVerifyAndGetInfo import com.tangem.tangem_card.data.TangemCard import com.tangem.tangem_card.reader.CardProtocol import com.tangem.tangem_card.tasks.VerifyCardTask -import com.tangem.tangem_card.tasks.WriteIDCardTask import com.tangem.tangem_card.util.Util import com.tangem.tangem_sdk.android.reader.NfcReader import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD @@ -56,7 +55,6 @@ import kotlinx.android.synthetic.main.layout_tangem_card.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import java.io.InputStream -import java.text.SimpleDateFormat import java.util.* import kotlin.concurrent.timerTask @@ -121,7 +119,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte lastTag = activity?.intent?.getParcelableExtra(Constant.EXTRA_LAST_DISCOVERED_TAG) if (arguments?.containsKey(NfcAdapter.EXTRA_TAG) == true) { - val tag = arguments!!.getParcelable(NfcAdapter.EXTRA_TAG) + val tag = requireArguments().getParcelable(NfcAdapter.EXTRA_TAG) if (tag != null) onTagDiscovered(tag) } } @@ -804,71 +802,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte val uid = tag?.id val sUID = Util.byteArrayToHexString(uid) if (ctx.card.uid != sUID || cardProtocol != null) { - - //TODO - remove, only for test - //TODO - begin - if (ctx.card.isIDCard && !ctx.card.hasIDCardData()) { - val idCardData = TangemCard.IDCardData() - idCardData.fullName = "John Doe" - idCardData.birthday = "1980-1-1" - idCardData.gender = "M" - val dt = Date() - idCardData.issueDate = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(dt) - dt.year = dt.year + 10 - idCardData.expireDate = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(dt) - idCardData.trustedAddress = "GCOQLQT32RVH2MTWQGK5K3GIGT3STYG5323KMNTTFHIU2T5TFOGR4KDT" // ask D.Baturin - idCardData.photo = Util.hexToBytes("FFD8FFE000104A46494600010101012C012C0000FFDB0043000A07070807060A0808080B0A0A0B0E18100E0D0D0E1D15161118231F2524221F2221262B372F26293429212230413134393B3E3E3E252E4449433C48373D3E3BFFDB0043010A0B0B0E0D0E1C10101C3B2822283B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3BFFC000110800C8007903012200021101031101FFC4001C0000020203010100000000000000000000000706080103050402FFC4003F100001030301050505060406020300000001020304000511060712213141135161718114223291A115235262B1C1428292D10816333472A2175393D2E1FFC4001801000301010000000000000000000000000002030104FFC4002111000202030100020301000000000000000001021103213112132232415142FFDA000C03010002110311003F007351451400562B35A83ED29F53097505D424294807DE4839C123B8E0FCA8016BB4CD712AC97FB642B72FDF8AA12A4273C179C84A0F811BC7D45306CF768B7BB547B9425EFB0FA3793DE3BC1F10720D579D75297335C5E1D5924A64A9B19EE4FBA3F4AECECDB5B7F96AE2604E708B64B57BC4F2617C82FC8F23E87A54164FB6C771D0FBA2BE50B4AD0149214923208E44566AE219A28A2800A28A2800A28A2800A28A2800A28A2803E5440049E02ABEFF009F26C5DA0C9D44CA94B69C74B6A673C16C03809F900478D580791DAB2B6F38DF494E7CEAA9BAD165E5B4AF89B5149F307151CADAAA1A28ECEB35B0FEAB9D322381C8D31424B4B1D52B483F30720F8835C4A3248009381CBC28AE76ED9444C7496D2AEDA65B44375227C04F04B2E2B0A6C7E45741E07879533AD7B55D2B7040ED662A0B879A2520A47F50C8FAD2028A78E468571459E6F53D85E46FB77AB7A93DE24A3FBD706EBB54D2D6DCA5B98A9CE0FE18A8DE1FD4709FAD57EC03D07CAB34CF3333C967B4E5F19D47638D7561B53687C1FBB5104A48241071E55EE932E3C3614FCA7DB61A4FC4E38A0948F5355F6C1B43BBE9BB02ED36F6D8CA9E538979D05451903202797319E3DFCAB8574BD5CEF6FF006D739CF4B5E78768AC84F90E43D053FCAA8CF2588B36AFB46A0B9C8856A7952BD99016E3C9410D8C9C0009E679F2E1C2BBB4BED8EDA7D8B4A393D69C393DE2A07F227DD4FD778FAD306A916DAB6630A28A298C31466B54A694FC575943CB654B414A5C47C4824731E2290978D5FAE2C976936C977B901E8EB2927711850E8A1EEF22307D69252F26A564CEFF00B49BCE92D48FDB6E36D8F2E383DA30EB6A2D296D9E5DE091C41F2A52DD65353AEF32630D29A6A43EB752DA8E4A428938FAD6FBBDFEEB7E5B4BBACD5CA532084296948290798E0077573AB9A53B2895051582A006490078D7C7B4320E3B44FCE9526CD3651520B268ABA6A0483024DB5448CEE19C82BF54A72457A6E9B37D51698AECA760B6FB2CA0AD6A61E4A8800649C7034530B22D45789570E3EEB7F335F22E0BEADA4F91A7F8A467A47BE83E15E76A634E1C1F70F8D7A291C5AE9A9D8D7B0ED76D36BB544B6AACB2D0DC6692D02D3885E70319C1C73A6258AFE9BEB1DBB76DB8446F190A96C86F7BCB8E4D56C813E55B26B53613CA62432ADE42D38C83EB4E2D15B556EF1218B5DE582CCD748436F3292A43A7C40E293F4F2ABC277A62490C8A2B159AB8862951B68B75B0B712E224B2D5C53F765927DF79BEFC7E53D4F426999738D2E5C25330E7182EAB876C9682D491E00F0CF8F1A40EBEB55AECB75EC5ABFBD75B82944CB5BC527B33D0139E2AF0E953C9C1A3D22F5A24C8EC123032A3CBBAB7020F2391E15BA2C11739B1E0938F68790D820711BCA033F5AE68D5EC77C3970E15C2F32D31E1467A5BEAE4DB482A3F21D2A52C6C8F5A3CD05FD9496F3C92E486D27E59A7BE9ED3569D236AF6580D25A42465E7DC237DC3F894AFDB90AC2F58E986DEEC57A86D895E71BA6523FBD59E47FE5095FD10917671AA23DFE0439B6A931DB7E4210A7D1EF21033C4EF24903866AC9169B2D762520B653BBBA78E538C63E55861F664B21E8EF21D695C96DA82927D452FB5D6D43EC49E6C7608E275D490859DD2A4B4A3FC21238A95E1C878F2A46DCDD1BA479E06C32C2D2D6B9B3A649CA894A1B21B4A46780EA4FCC56ABEEC36D521ADFB24C7A23C07FA7215DA215EB8C8FAD781AD21B56D449126E17E55B82F88695254D91FC8D8C0A1CD27B58D3A3B7817D55C528E3D9A649709FE57471F4AAF99FF004CB42E351E8FBDE977B72E509686C9C21E4FBCDABC94387EF5E78A9712C80E1C9E80F414D381B4C45E62CBD2FACA10B7497DB5305F282942564606FA4F141CE0E797952CD68536B5217F12490AF31CEA7372AA66C523E69F7B33B169F896166E76B57B5497D387A4B806FA55D518FE1C1E9D79E4D2523E9BD473612AE10EC52DF86919ED529F8C0E6523991E40D31F64F11C5155CAC3770EC65283770B74B6F716D9E8A041209EE381919071D371C5A7612636EB358159AE810876D3F53BDA6748BAEC3514CD96B11E3A8734920E543C40071E24579F44681B6E9EB434E4B88D49BA3C90B92FBC80B2147894A73C80E5E278D7236C984CCD2AE3BFEDD370FBDCF2E68E7E80D320F33E751CADF068A10FB53B4C5B56AF262212DA2530878B6848012AC949C01DFBB9F9D462D17362CD798572928538DC5792E96D3CD653C401EB8A97ED817BDACDB4FE086D8FAA8D426CD6A3A8355DBACFBC52890F250B23984F3511E80D4A11F5219BA44D615A358ED764AA6CE99EC1670B3B8307B3183C908CFBE47551F9F4AF5DE767DB39D33B912F5A9673531C00E13BAA207794A50703CE9CD162B16F84DC68ACA5A61840436DA0704A40E005548BD5CA45DEF32EE129654F48754B513D38F01E4070F4AEBAA264F275B6FDB305C7BEE9DBBA6E165998DD71232DAB23805A738F250EA3A72AEF6C36CAD4D72E5A9660EDE507BB1696BE25248DE5ABCCEF019F3EFAD5616C9FF0F574F6FE2CEF3AA8FBDD3DF4EEE3F9F35AF613A8988F26769F7D610B92A1223E7F89406149F3C007D0D0074F52EDBBEC8D46F5BADF6B6E5468AE169D756E9495A81C2B7703000391939CD31B4E5FE16A7B247BAC1512D3C38A55F121438149F106AB56BBB1CAB06AFB846908210E3CA759591C16DA89208EFE783E20D3736131E533A3E53AF052587A6294CE7AE1290A23C3231E8680241AFB41C1D636A5E1B437736924C6918C1CFE051EA93F4E755CD80F34F3B0DE4143CD28A0A55CC107047A1AB7279555FD6FD89DA4DE7D9F1B9ED4BCE3F163DEFF00B66926AE2CD5D2C6DAA208369870C0C7B3B086FE490297D15B1A676EC23C61D9C5BEC52B5B63E10BC28938FF009209FE6358D9AEBF125B458AF2F80F21388D21C57FA807F0289EA0723D478F3F88529BD5FB6F6675BD41E83648A52A7D1C50A5614381EBEF2CE3BF74D47174690D8159AC0ACD748847B5B694635869D76D8EB9D93A141C61DC6771C1C8F910483E06A0D17556BDD22C2605F74BBD7669801089914925491C8920107D403DF4DAAC1AC693E8595A3575FD7A9750BD725C572265296C30E1CA91BA3183E39CD78ECD25FD35A82D1A95D654A862491BC3AEEE02C79EEAABEF51C77226A3B8B0F496E53A990BED1D6F3BAA51393CFC4E3D29A7A2B4BDBB516CB99B7DC992B69F7DD752A4F0536ADE202927A1E15CD19799146AD0C5833A2DCA135321BE87E3BC90A6DC41C850A5DDD7629619B7B76E7EDF222C57165D7632027033C4E147E11E87151FFF00C6DAEF4BBEBFF2B5F7B48CA39DD4BDD913FF00242BDD27C73515D6E35D4211A2EA6B9BCEFB58514474C90A07040C94A78733C2BA1493E13A247AF351C7BF983A0B46321D8ADAD285167E070A79241EA91CCABA919E993D0D47B227A1DAA04ED32E94DD203490E84AB74BEB1C77D27A2B3D3A8C75E732D13A12D9A420A16D35DA5C1D6C07E439C559C64A53DC9CFCFAD4AAA32C8EF43A889DB6ED3AD5706D169DA1D8D0B9118E0BEE460BC1EF5208CA4F7E39F753021EBED12DC34262DF6DECB084E10D85767BA3B827031F2AF6DE74C593502026ED6D625103016A4E163C943047CE92BB50D176BD2371B649B7B0E88127783AD29D2AC292412013C46527E9548E452D18D5135D5BB67B6468AB89A68AA7CD706EA1EECC86DB27A8CF151EE0063F4A80CDD0D3ECFA3D3A8AEFBE89F2E5A53D8B87DE4A141472AFCC4E0E3A0F3A72E9DD17A5ECA96A65A6D8D071690B4487097178232082ACE3D315AF5E69995AAACCCC18D25A8FD9BE1E5A9C04E40491818F3A9CF25E91A908BB346B5CC9E98F769CEC261CE01F4341C4A4FE6191C3C45582D17A6E1699B0B70E1A98782CEFAA4328DDEDF3C94789CF0F1C776290B61F6190B7ED53FB3684C012CCB50FF6EF0CEE927F01CEEABCC1E94E6D9DE96BE69A82A6AE972438CAC6510D037C327A90B3FA018ADC46489AD158ACD5C50A8D6BBD4A9D33A69F94850F6B7BEE62A7BD67AFA0C9F4A92D41B6A3A818B1D89296D0DAAE52829A8CB29054D24FC6B07A70C0F323BA964E91A8439254A2A512493924F326AC2ECF59EC7425A138C6F31BFF00D4A27F7AAF237411BD9DDEB8EEAB31A762A6169CB6C54AD2B0D456D2149E4AF747115C654E8D26F5AA7EDADB7D9ADABE2DB1D82549F0C9715F4A7252727FDCFF0088C8CB74E12B537BB9F16303EB54C7D62C871F3E3DF59AC0E559A98C14BADB6C44BFA25A7F1EF4798820F8282927F6A62D2EB6D935B8FA25B8C4FBF265A02478241513FA7CE9E1F9215F092E8394A99A12CAF2CE55EC88493DFBBEEFED5DD75412DA94790493F4AE0E828AB85A12CCCB80857B225641E9BDEF7EF5EBD513D36BD2F729AA38ECA32F77FE44607D48A57D3570AD8A3BCA51E849A766CAB5A7DAF6FF00B127BB99B111F74A51E2F343F529E47C307BE92206001DD5E9B7CF936AB8313E1BA5A911D616DA8743FD8F235B097960D596A6B35C4D29A9236A8B1B370630959F75E6B3C5A58E63F71E04576EBAD3B247CA88032780155B3596A273536A491394486527B28E83FC2D83C3D4F127CE9F7AC2E3F65692BA4D070A6E32820FE623753F522AB3018E1DD51CCFF43C4CD3E76617B4DDB4830C2D797E01F677013C7747141FE9E1E8690D5DDD21AAA4E93BB898D23B561C1B9219CE37D3E1DC474FFF006B9C72C6528B6C76D936BBCDA757C2202D85A1A59CF25A49520F8E4647A577646D92C0DC7DE8F1273CEE3820A12819F1564FE94B2D57AB2E1ABA6076684B6C3790CC647C280799F127BE9E32A7663563CB4BEAEB5EACB737260BE80F1482EC62A1DA34AEA08EEEE3C8D77307B8FCAAA608AA69C0E47794D287220E08F222BD2A9B7B5A77177896A477190B23E59A6A8BE3336593BDEA8B2E9D8EA76E97165820706F7B79C578048E26945DA4FDB16B76886571EC9031BC09E286C9C9C9FC6BC63872C7852FC420A5153AE2964F3F1F5A9468BD52EE8FBA990D33DAC6753B8FB20E37873041EF1FDE8B8C79D0A6CB148425B42508484A523000E400E94ABDAF6A76D486F4EC570294141D9641F871F0A3CFA9F4AF8BFED8FB786A62C509D61D58C191237728FF008A46727C4FCA960E38E3CEADD756A71C5A8A94B51C9513CC935318F9A28A2B00636CD6DFAAECD3A3DCE2DB1C91699C901EDD751EF273C16015734FE9914EAE3E1503D8F5C7DAF46988A56550A42DBC7E557BC3F53F2A9ED76415224FA2F76CD70F66D28C4249C2A6494823BD281BC7EBBB490A636DA6E3DBEA285012ACA62C6DF50EE52CFF00648A5CD73E4772291E05145153342B5B921B68E16AC1EEC56CAF9536851CA90924778AD55FB035A65B0A380B03CC62B766B52E3B2B182D8F4E15AA3EF32FA982494E32934F49AD196CF551451533428A28A0028A283C050032B62971EC6F770B7295812180EA47E641C1FA2BE94E7CD577D1E6569BD79685CB41692FA90013F0ADB75380A07A8E23E5561BDEEEAEAC4FEB44E5D14ACE8E7B5DEB6B85F26A94DD9848DC694382A4A5184809EE4F0E2AF9782BA404894E840C2438A091DC327156A5C09663AB7404A508380060000555351DE515779CD4F2248D8B31451454470A28A2800A31451400514514005145140051451400E6D2F6585ADB6690187CF6732DEA5B6C4948CA995A5594F98C6EE454DF7F50FF00E9B77FF22FFF00AD2F762170CB374B6A95F0A90FA0798293FA0A6BD76436AC93E9E4B9AFB3B5CB5FE16167FEA6AAEC3892273A962334A75D282ADD4F3C049513E8013566F512FB3D37735E71BB0DD3FF0043506D9368F4C1B3AAF539A1ED13DBDD692A1F0327F7573F2C52CE3E9A46A7484C03919A2BD57582AB65DA64058C18CFADAF92881F4C5796B95940A28A2800A28A2800A28A2800A28A2803D56CB6C8BBDC1B811021521EC86D2B504EF9033BA09E1938E15F1360CBB74A5459B19D8CFA3E26DD4949AF88D25E8729A951D5B8F32B0E36A1D140E47E95635B8D65D75A6A24B9B09A92CC9682D3BC3DE6D5D40573041C8E1DD548414918DD0A2D935C3D8B5CB0D156133195B27CF1BC3EA9FAD3F37877D2AA4ECAE6586FB0EF1A7A47B5371A421D319E2038003C40572570CF3C7AD357753DD57C69A54C9B354B8ACCE88EC5909DF65E414389CFC493C08AD8942508094A425206001C80AFBAC550C2BFED56DDEC1AEA4B813BA898DA1F1E646E9FAA6A1D4DADB75BB2D5AEE694FC2A5C759F31BC9FD15503D21A4A66ADBA88AC65B8CDE15224638369F0EF51E83F6AE49C7ED48A27A38C224930CCC0C39ECC970345EDD3BA16464273DF8AD5565D5A4ED074C2B4EA6304C12DEE6E8F8B3F8F3F8B3C73DF55EF50D865E9BBD3F6C9832A6CE50B0301C41E4A1E7F439144F1F904ECE6515BE14291719CCC288D175F7D610DA075269FB6ED9D5958D22DD866C74C827EF1D7C7059748E2A49E631C8780AC841C8D6E8AF4738E0327A0A91EAAD1772D2A63BB2125D8B21092979238256464A15DC471C778AEF3BB31B95A758DB23A92A976C7E52712529F8520EF10B1D0E0791FA53A2E16F897582EC29CC25F8EF2775685F223F63E34F1C769D8AE4557A9AECE34546D5AEDC173CBA88CC361085B6AC10E2B883E3800F0F115E4D73A1A56919BDA2379FB6BCAFB97C8E293F817DC7C7AD37B67164FB1346C36DC4EEBF247B43BDF957103D13814421F6A66B7A14FAAB66B7AD39BF21A419F053C7B7653EF207E74F31E6323CAA5BB16BF769165D89D5E4B47DA18C9FE13C163D0E0FF0035353151D73455ADBD431EFB6F47B0CD6944B8591843E9230A0A4F2E3DE3AF7D51429DA16F448AB358159AA8A1451450047B5BE9B56A9D3ABB6B4E21A77B66DC42D7C9385713FD2555EDD3F6083A6ED4D5BA037BADA38A967E2715D54A3D49A28ACA5760752A21B43D1C9D5566DF8E948B8C50551D5CB7FBD04F71E9DC71E34514356A80E1ECA345AADB14DFAE2C944B7D2531DB58C169BEA48E855FA79D32A8A2B22A91AC3145145318689B062DC62391263087D874616DAC642856F4A424000600E94514019A28A2800A28A2803FFFD9") - - val writeTask = WriteIDCardTask(ctx.card, cardProtocol!!.reader, App.localStorage, App.pinStorage, idCardData, ctx.card.issuer.privateDataKey, - object : CardProtocol.Notifications { - override fun onReadProgress(cardProtocol: CardProtocol?, progress: Int) { - LOG.i("WriteIDCardTask", "onReadProgress: ${progress}%") - } - - override fun onReadWait(msec: Int) { - } - - override fun onReadStart(cardProtocol: CardProtocol?) { - LOG.i("WriteIDCardTask", "onReadStart") - rlProgressBar?.post { rlProgressBar?.visibility = View.VISIBLE } - - } - - override fun onReadCancel() { - LOG.i("WriteIDCardTask", "onReadCancel") - rlProgressBar?.postDelayed({ - try { - rlProgressBar?.visibility = View.GONE - } catch (e: Exception) { - e.printStackTrace() - } - }, 500) - - } - - override fun onReadFinish(cardProtocol: CardProtocol?) { - LOG.i("WriteIDCardTask", "onReadFinish") - rlProgressBar?.postDelayed({ - try { - rlProgressBar?.visibility = View.GONE - } catch (e: Exception) { - e.printStackTrace() - } - }, 500) - - } - - override fun onReadAfterRequest() { - } - - override fun onReadBeforeRequest(timeout: Int) { - } - }) - writeTask.start() - return - } - //TODO end - (activity as MainActivity).nfcManager.ignoreTag(isoDep.tag) + (activity as? MainActivity)?.nfcManager?.ignoreTag(isoDep.tag) return } diff --git a/app/src/tangemAccess/java/com/tangem/ui/SignTransactionActivity.kt b/app/src/tangemAccess/java/com/tangem/ui/SignTransactionActivity.kt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt b/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt index bb5858b8e3..fdcdc64a72 100644 --- a/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt +++ b/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt @@ -9,7 +9,6 @@ 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.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.crashlytics.FirebaseCrashlytics @@ -80,7 +79,7 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener, mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound) // init NFC Antenna - nfcDeviceAntenna = NfcDeviceAntennaLocation(context!!, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) + nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) nfcDeviceAntenna.init() amount = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_AMOUNT), arguments?.getString(Constant.EXTRA_AMOUNT_CURRENCY)) @@ -165,7 +164,7 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener, } override fun onReadStart(cardProtocol: CardProtocol) { - rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE } + rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE } progressBar?.post { progressBar?.visibility = View.VISIBLE @@ -234,7 +233,9 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener, NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG) } } else { - Toast.makeText(context, R.string.general_notification_scan_again, Toast.LENGTH_LONG).show() + (activity as? MainActivity)?.toastHelper?.showSingleToast( + context, getString(R.string.general_notification_scan_again) + ) } progressBar?.progress = 100 progressBar?.progressTintList = ColorStateList.valueOf(Color.RED) @@ -280,7 +281,7 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener, override fun onReadBeforeRequest(timeout: Int) { LOG.i(TAG, "onReadBeforeRequest timeout $timeout") - WaitSecurityDelayDialog.onReadBeforeRequest(activity!!, timeout) + activity?.let { WaitSecurityDelayDialog.onReadBeforeRequest(it, timeout) } // if (!waitSecurityDelayDialogNew.isAdded) // waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG) @@ -293,7 +294,7 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener, override fun onReadAfterRequest() { LOG.i(TAG, "onReadAfterRequest") - WaitSecurityDelayDialog.onReadAfterRequest(activity!!) + activity?.let { WaitSecurityDelayDialog.onReadAfterRequest(it) } // val readAfterRequest = ReadAfterRequest() // EventBus.getDefault().post(readAfterRequest) @@ -301,7 +302,7 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener, override fun onReadWait(msec: Int) { LOG.i(TAG, "onReadWait msec $msec") - WaitSecurityDelayDialog.onReadWait(activity!!, msec) + activity?.let { WaitSecurityDelayDialog.onReadWait(it, msec) } // val readWait = ReadWait() // readWait.msec = msec diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressService.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressService.kt index d4bf305e42..1483284942 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressService.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressService.kt @@ -1,68 +1,62 @@ package com.tangem.blockchain.blockchains.bitcoin +import com.tangem.blockchain.blockchains.litecoin.LitecoinMainNetParams import com.tangem.blockchain.common.AddressService +import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.calculateRipemd160 import com.tangem.common.extensions.calculateSha256 -import org.bitcoinj.core.AddressFormatException -import org.bitcoinj.core.Base58 -import org.bitcoinj.core.SegwitAddress +import org.bitcoinj.core.* import org.bitcoinj.params.MainNetParams import org.bitcoinj.params.TestNet3Params import java.security.MessageDigest -class BitcoinAddressService(private val testNet: Boolean = false): AddressService { +class BitcoinAddressService(private val blockchain: Blockchain) : AddressService { + + private val networkParameters: NetworkParameters = when (blockchain) { + Blockchain.Bitcoin -> MainNetParams() + Blockchain.BitcoinTestnet -> TestNet3Params() + Blockchain.Litecoin -> LitecoinMainNetParams() + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") + } + override fun makeAddress(walletPublicKey: ByteArray): String { - val netSelectionByte = if (testNet) 0x6f.toByte() else 0x00.toByte() - val hash1 = walletPublicKey.calculateSha256().calculateRipemd160() - val hash2 = byteArrayOf(netSelectionByte).plus(hash1).calculateSha256().calculateSha256() - val result = byteArrayOf(netSelectionByte) + hash1 + hash2[0] + hash2[1] + hash2[2] + hash2[3] - return Base58.encode(result) - } + val publicKeyHash = walletPublicKey.calculateSha256().calculateRipemd160() + val checksum = byteArrayOf(networkParameters.addressHeader.toByte()).plus(publicKeyHash) + .calculateSha256().calculateSha256() + val result = byteArrayOf(networkParameters.addressHeader.toByte()) + publicKeyHash + checksum.copyOfRange(0, 4) + return Base58.encode(result) + } - override fun validate(address: String): Boolean { - if (firstLetters.contains(address.first())) { - if (testNet && firstLettersNonTestNet.contains(address.first())) return false - if (address.length !in 26..35) return false - val decoded = address.decodeBase58() ?: return false - val hash = recursiveSha256(decoded, 0, 21, 2) - return hash.sliceArray(0..3).contentEquals(decoded.sliceArray(21..24)) - } else { - return validateSegwitAddress(address, testNet) + override fun validate(address: String): Boolean { + return validateLegacyAddress(address) || validateSegwitAddress(address) + } + + private fun validateSegwitAddress(address: String): Boolean { + return try { + when (blockchain) { + Blockchain.Bitcoin -> SegwitAddress.fromBech32(MainNetParams(), address) + Blockchain.BitcoinTestnet -> SegwitAddress.fromBech32(TestNet3Params(), address) + Blockchain.Litecoin -> SegwitAddress.fromBech32(LitecoinMainNetParams(), address) + else -> return false } + true + } catch (e: Exception) { + false } + } - private fun recursiveSha256(data: ByteArray, start: Int, len: Int, recursion: Int): ByteArray { - if (recursion == 0) return data - val md = MessageDigest.getInstance("SHA-256") - md.update(data.sliceArray(start until start + len)) - return recursiveSha256(md.digest(), 0, 32, recursion - 1) - } - - private fun String.decodeBase58(): ByteArray? { - return try { - Base58.decode(this) - } catch (exception: AddressFormatException) { - null + private fun validateLegacyAddress(address: String): Boolean { + return try { + when (blockchain) { + Blockchain.Bitcoin -> LegacyAddress.fromBase58(MainNetParams(), address) + Blockchain.BitcoinTestnet -> LegacyAddress.fromBase58(TestNet3Params(), address) + Blockchain.Litecoin -> LegacyAddress.fromBase58(LitecoinMainNetParams(), address) + else -> return false } + true + } catch (e: Exception) { + false } - - private fun validateSegwitAddress(address: String, testNet: Boolean): Boolean { - return try { - if (testNet) { - SegwitAddress.fromBech32(TestNet3Params(), address) - true - } else { - SegwitAddress.fromBech32(MainNetParams(), address) - true - } - } catch (e: Exception) { - false - } - } - - companion object { - private const val firstLetters = "123nm" - private const val firstLettersNonTestNet = "13" } } \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt index ef0cbe5500..ede62d31a4 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinTransactionBuilder.kt @@ -1,21 +1,30 @@ package com.tangem.blockchain.blockchains.bitcoin +import com.tangem.blockchain.blockchains.litecoin.LitecoinMainNetParams +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.extensions.Result import com.tangem.common.extensions.isZero import org.bitcoinj.core.* import org.bitcoinj.crypto.TransactionSignature +import org.bitcoinj.params.MainNetParams +import org.bitcoinj.params.TestNet3Params import org.bitcoinj.script.Script import org.bitcoinj.script.ScriptBuilder import java.math.BigDecimal import java.math.BigInteger open class BitcoinTransactionBuilder( - private val walletPublicKey: ByteArray, private val testNet: Boolean = false + private val walletPublicKey: ByteArray, blockchain: Blockchain ) { private lateinit var transaction: Transaction - protected var networkParameters: NetworkParameters? = null + protected var networkParameters = when (blockchain) { + Blockchain.Bitcoin, Blockchain.BitcoinCash -> MainNetParams() + Blockchain.BitcoinTestnet -> TestNet3Params() + Blockchain.Litecoin -> LitecoinMainNetParams() + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") + } var unspentOutputs: List? = null open fun buildToSign( @@ -25,11 +34,6 @@ open class BitcoinTransactionBuilder( val change: BigDecimal = calculateChange(transactionData, unspentOutputs!!) - networkParameters = if (testNet) { - NetworkParameters.fromID(NetworkParameters.ID_TESTNET) - } else { - NetworkParameters.fromID(NetworkParameters.ID_MAINNET) - } transaction = transactionData.toBitcoinJTransaction(networkParameters, unspentOutputs!!, change) val hashesForSign: MutableList = MutableList(transaction.inputs.size) { byteArrayOf() } diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt index 0670f32ad4..2a723fe5f9 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinWalletManager.kt @@ -74,11 +74,16 @@ open class BitcoinWalletManager( val minFee = feeResult.data.minimalPerKb.calculateFee(transactionSize) val normalFee = feeResult.data.normalPerKb.calculateFee(transactionSize) val priorityFee = feeResult.data.priorityPerKb.calculateFee(transactionSize) - return Result.Success( - listOf(Amount(minFee, blockchain), - Amount(normalFee, blockchain), - Amount(priorityFee, blockchain)) + val fees = listOf(Amount(minFee, blockchain), + Amount(normalFee, blockchain), + Amount(priorityFee, blockchain) ) + + val minimalFee = transactionSize.movePointLeft(blockchain.decimals()) + for (fee in fees) { + if (fee.value!! < minimalFee) fee.value = minimalFee + } + return Result.Success(fees) } } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt index 8483883d82..c6190937a4 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BitcoinNetworkManager.kt @@ -1,26 +1,29 @@ package com.tangem.blockchain.blockchains.bitcoin.network import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput -import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockchainInfoApi -import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherApi -import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi +import com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo.BlockchainInfoApi +import com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo.BlockchainInfoProvider +import com.tangem.blockchain.network.blockcypher.BlockcypherApi +import com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo.EstimatefeeApi +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.network.API_BLOCKCHAIN_INFO import com.tangem.blockchain.network.API_BLOCKCYPHER import com.tangem.blockchain.network.API_ESTIMATEFEE +import com.tangem.blockchain.network.blockcypher.BlockcypherProvider import com.tangem.blockchain.network.createRetrofitInstance import retrofit2.HttpException import java.io.IOException import java.math.BigDecimal -class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinProvider { +class BitcoinNetworkManager(blockchain: Blockchain) : BitcoinProvider { private val blockcypherProvider by lazy { val api = createRetrofitInstance(API_BLOCKCYPHER) .create(BlockcypherApi::class.java) - BlockcypherProvider(api, isTestNet) + BlockcypherProvider(api, blockchain) } private val blockchainInfoProvider by lazy { @@ -31,10 +34,10 @@ class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinPro BlockchainInfoProvider(api, estimateFeeApi) } - private var bitcoinProvider: BitcoinProvider = blockchainInfoProvider + private var provider: BitcoinProvider = blockchainInfoProvider private fun changeProvider() { - bitcoinProvider = if (bitcoinProvider == blockchainInfoProvider) { + provider = if (provider == blockchainInfoProvider) { blockcypherProvider } else { blockchainInfoProvider @@ -42,13 +45,13 @@ class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinPro } override suspend fun getInfo(address: String): Result { - val result = bitcoinProvider.getInfo(address) + val result = provider.getInfo(address) when (result) { is Result.Success -> return result is Result.Failure -> { if (result.error is IOException || result.error is HttpException) { changeProvider() - return bitcoinProvider.getInfo(address) + return provider.getInfo(address) } else { return result } @@ -57,13 +60,13 @@ class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinPro } override suspend fun getFee(): Result { - val result = bitcoinProvider.getFee() + val result = provider.getFee() when (result) { is Result.Success -> return result is Result.Failure -> { if (result.error is IOException || result.error is HttpException) { changeProvider() - return bitcoinProvider.getFee() + return provider.getFee() } else { return result } @@ -72,13 +75,13 @@ class BitcoinNetworkManager(private val isTestNet: Boolean = false) : BitcoinPro } override suspend fun sendTransaction(transaction: String): SimpleResult { - val result = bitcoinProvider.sendTransaction(transaction) + val result = provider.sendTransaction(transaction) when (result) { is SimpleResult.Success -> return result is SimpleResult.Failure -> { if (result.error is IOException || result.error is HttpException) { changeProvider() - return bitcoinProvider.sendTransaction(transaction) + return provider.sendTransaction(transaction) } else { return result } diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/BlockchainInfoApi.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoApi.kt similarity index 64% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/BlockchainInfoApi.kt rename to blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoApi.kt index 294846b58c..2f1884dcb3 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/BlockchainInfoApi.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoApi.kt @@ -1,7 +1,5 @@ -package com.tangem.blockchain.blockchains.bitcoin.network.api +package com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockchainInfoAddress -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockchainInfoUnspents import okhttp3.ResponseBody import retrofit2.http.* diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoProvider.kt similarity index 91% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt rename to blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoProvider.kt index cb9dc12e6a..ee177be908 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockchainInfoProvider.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoProvider.kt @@ -1,8 +1,9 @@ -package com.tangem.blockchain.blockchains.bitcoin.network +package com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput -import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockchainInfoApi -import com.tangem.blockchain.blockchains.bitcoin.network.api.EstimatefeeApi +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinFee +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinProvider import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/response/BlockchainInfoResponse.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoResponse.kt similarity index 93% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/response/BlockchainInfoResponse.kt rename to blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoResponse.kt index e7ec0f2734..38e3035552 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/response/BlockchainInfoResponse.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/BlockchainInfoResponse.kt @@ -1,4 +1,4 @@ -package com.tangem.blockchain.blockchains.bitcoin.network.response +package com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/EstimateFeeApi.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/EstimateFeeApi.kt similarity index 83% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/EstimateFeeApi.kt rename to blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/EstimateFeeApi.kt index 393e03a00b..43d9cc42f9 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/EstimateFeeApi.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/blockchaininfo/EstimateFeeApi.kt @@ -1,4 +1,4 @@ -package com.tangem.blockchain.blockchains.bitcoin.network.api +package com.tangem.blockchain.blockchains.bitcoin.network.blockchaininfo import retrofit2.http.GET diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt index 5a594aa312..d4ad5981bf 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashNetworkManager.kt @@ -12,7 +12,7 @@ import com.tangem.blockchain.network.blockchair.BlockchairProvider import com.tangem.blockchain.network.createRetrofitInstance class BitcoinCashNetworkManager : BitcoinProvider { - private val blockchain: Blockchain = Blockchain.BitcoinCash + private val blockchain = Blockchain.BitcoinCash private val blockchairProvider by lazy { val api = createRetrofitInstance(API_BLOCKCHAIR).create(BlockchairApi::class.java) diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt index 58bec1f6e3..cb3dff45a6 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashTransactionBuilder.kt @@ -2,6 +2,7 @@ package com.tangem.blockchain.blockchains.bitcoincash import com.tangem.blockchain.blockchains.bitcoin.BitcoinTransactionBuilder import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.extensions.Result import com.tangem.common.extensions.isZero @@ -13,8 +14,8 @@ import org.bitcoinj.script.ScriptBuilder import java.math.BigDecimal import java.math.BigInteger -class BitcoinCashTransactionBuilder(private val walletPublicKey: ByteArray) - : BitcoinTransactionBuilder(walletPublicKey) { +class BitcoinCashTransactionBuilder(private val walletPublicKey: ByteArray, blockchain: Blockchain) + : BitcoinTransactionBuilder(walletPublicKey, blockchain) { private lateinit var transaction: BitcoinCashTransaction @@ -25,7 +26,6 @@ class BitcoinCashTransactionBuilder(private val walletPublicKey: ByteArray) val change: BigDecimal = calculateChange(transactionData, unspentOutputs!!) - networkParameters = NetworkParameters.fromID(NetworkParameters.ID_MAINNET) transaction = transactionData.toBitcoinCashTransaction(networkParameters, unspentOutputs!!, change) val hashesForSign: MutableList = MutableList(transaction.inputs.size) { byteArrayOf() } diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt index b0793c6cf9..2d2d310ec2 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoincash/BitcoinCashWalletManager.kt @@ -10,8 +10,8 @@ import java.math.BigDecimal class BitcoinCashWalletManager( cardId: String, wallet: Wallet, - private val transactionBuilder: BitcoinCashTransactionBuilder, - private val networkManager: BitcoinCashNetworkManager + transactionBuilder: BitcoinCashTransactionBuilder, + networkManager: BitcoinCashNetworkManager ) : BitcoinWalletManager(cardId, wallet, transactionBuilder, networkManager), TransactionSender { override suspend fun getFee(amount: Amount, destination: String): Result> { val minimalFee = BigDecimal("0.00001") diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/EthereumTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/EthereumTransactionBuilder.kt index ead68d974b..74d554ccb9 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/EthereumTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/EthereumTransactionBuilder.kt @@ -22,7 +22,7 @@ class EthereumTransactionBuilder(private val walletPublicKey: ByteArray, blockch private val chainId = when (blockchain) { Blockchain.Ethereum -> Chain.Mainnet.id Blockchain.RSK -> Chain.RskMainnet.id - else -> throw Exception("${blockchain.fullName} blockchain is not supported by EthereumTransactionBuilder") + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") } fun buildToSign(transactionData: TransactionData, nonce: BigInteger?): TransactionToSign? { diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/network/EthereumNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/network/EthereumNetworkManager.kt index 0fd3078d40..b5da72dd74 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/network/EthereumNetworkManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/ethereum/network/EthereumNetworkManager.kt @@ -23,7 +23,7 @@ class EthereumNetworkManager(blockchain: Blockchain) { val baseUrl = when (blockchain) { Blockchain.Ethereum -> API_INFURA + infuraPath Blockchain.RSK -> API_RSK - else -> throw Exception("${blockchain.fullName} blockchain is not supported by EthereumNetworkManager") + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") } createRetrofitInstance(baseUrl).create(EthereumApi::class.java) } @@ -31,7 +31,7 @@ class EthereumNetworkManager(blockchain: Blockchain) { private val apiKey = when (blockchain) { Blockchain.Ethereum -> INFURA_API_KEY Blockchain.RSK -> "" - else -> throw Exception("${blockchain.fullName} blockchain is not supported by EthereumNetworkManager") + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") } private val provider: EthereumProvider by lazy { EthereumProvider(api, apiKey) } diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinMainNetParams.java b/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinMainNetParams.java new file mode 100644 index 0000000000..1329dfe69c --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinMainNetParams.java @@ -0,0 +1,69 @@ +package com.tangem.blockchain.blockchains.litecoin; + +import org.bitcoinj.core.Utils; +import org.bitcoinj.params.AbstractBitcoinNetParams; +import org.bitcoinj.params.MainNetParams; +import org.spongycastle.util.encoders.Hex; + +import static com.google.common.base.Preconditions.checkState; + + +public class LitecoinMainNetParams extends AbstractBitcoinNetParams { + public static final int MAINNET_MAJORITY_WINDOW = MainNetParams.MAINNET_MAJORITY_WINDOW; + public static final int MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED = MainNetParams.MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED; + public static final int MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = MainNetParams.MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE; + + public LitecoinMainNetParams() { + super(); + id = "org.bitcoinj.litecoin_mainnet"; + // Genesis hash is 12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2 + packetMagic = 0xfbc0b6db; + + maxTarget = Utils.decodeCompactBits(0x1e0fffffL); + port = 9333; + addressHeader = 48; + p2shHeader = 50; + segwitAddressHrp = "ltc"; + dumpedPrivateKeyHeader = 176; + + spendableCoinbaseDepth = 100; + subsidyDecreaseBlockCount = 840000; + + genesisBlock.setTime(1317972665L); + genesisBlock.setDifficultyTarget(0x1e0ffff0L); + genesisBlock.setNonce(2084524493); + + String genesisHash = genesisBlock.getHashAsString(); + checkState(genesisHash.equals("5155a7ed2219a75c0735c58b5d459c6d07d97917570e27b9d1d4546fb8431381")); + alertSigningKey = Hex.decode("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9"); + + majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE; + majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED; + majorityWindow = MAINNET_MAJORITY_WINDOW; + + dnsSeeds = new String[]{ + "dnsseed.litecointools.com", + "dnsseed.litecoinpool.org", + "dnsseed.ltc.xurious.com", + "dnsseed.koin-project.com", + "dnsseed.weminemnc.com" + }; + bip32HeaderP2PKHpub = 0x0488B21E; + bip32HeaderP2PKHpriv = 0x0488ADE4; + } + + @Override + public String getPaymentProtocolId() { + return PAYMENT_PROTOCOL_ID_MAINNET; + } + + + private static LitecoinMainNetParams instance; + + public static synchronized LitecoinMainNetParams get() { + if (instance == null) { + instance = new LitecoinMainNetParams(); + } + return instance; + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinNetworkManager.kt new file mode 100644 index 0000000000..95e5e592ce --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinNetworkManager.kt @@ -0,0 +1,85 @@ +package com.tangem.blockchain.blockchains.litecoin + +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinFee +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinProvider +import com.tangem.blockchain.network.blockcypher.BlockcypherProvider +import com.tangem.blockchain.network.blockcypher.BlockcypherApi +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.network.API_BLOCKCHAIR +import com.tangem.blockchain.network.API_BLOCKCYPHER +import com.tangem.blockchain.network.blockchair.BlockchairApi +import com.tangem.blockchain.network.blockchair.BlockchairProvider +import com.tangem.blockchain.network.createRetrofitInstance +import retrofit2.HttpException +import java.io.IOException + + +class LitecoinNetworkManager : BitcoinProvider { + private val blockchain = Blockchain.Litecoin + + private val blockchairProvider by lazy { + val api = createRetrofitInstance(API_BLOCKCHAIR) + .create(BlockchairApi::class.java) + BlockchairProvider(api, blockchain) + } + + private val blockcypherProvider by lazy { + val api = createRetrofitInstance(API_BLOCKCYPHER) + .create(BlockcypherApi::class.java) + BlockcypherProvider(api, blockchain) + } + + private var provider: BitcoinProvider = blockchairProvider + + private fun changeProvider() { + provider = if (provider == blockchairProvider) blockcypherProvider else blockchairProvider + } + + override suspend fun getInfo(address: String): Result { + val result = provider.getInfo(address) + when (result) { + is Result.Success -> return result + is Result.Failure -> { + if (result.error is IOException || result.error is HttpException) { + changeProvider() + return provider.getInfo(address) + } else { + return result + } + } + } + } + + override suspend fun getFee(): Result { + val result = provider.getFee() + when (result) { + is Result.Success -> return result + is Result.Failure -> { + if (result.error is IOException || result.error is HttpException) { + changeProvider() + return provider.getFee() + } else { + return result + } + } + } + } + + override suspend fun sendTransaction(transaction: String): SimpleResult { + val result = provider.sendTransaction(transaction) + when (result) { + is SimpleResult.Success -> return result + is SimpleResult.Failure -> { + if (result.error is IOException || result.error is HttpException) { + changeProvider() + return provider.sendTransaction(transaction) + } else { + return result + } + } + } + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinWalletManager.kt new file mode 100644 index 0000000000..8c12297f26 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/litecoin/LitecoinWalletManager.kt @@ -0,0 +1,29 @@ +package com.tangem.blockchain.blockchains.litecoin + +import com.tangem.blockchain.blockchains.bitcoin.BitcoinTransactionBuilder +import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.TransactionSender +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.extensions.Result +import java.math.BigDecimal + +class LitecoinWalletManager( + cardId: String, + wallet: Wallet, + transactionBuilder: BitcoinTransactionBuilder, + networkManager: LitecoinNetworkManager +) : BitcoinWalletManager(cardId, wallet, transactionBuilder, networkManager), TransactionSender { + override suspend fun getFee(amount: Amount, destination: String): Result> { + val minimalFee = BigDecimal("0.00001") + when (val result = super.getFee(amount, destination)) { + is Result.Success -> { + for (fee in result.data) { + if (fee.value!! < minimalFee) fee.value = minimalFee + } + return result + } + is Result.Failure -> return result + } + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt index c579c2d3db..9284ec26b8 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt @@ -17,6 +17,7 @@ enum class Blockchain( Bitcoin("BTC", "BTC", "Bitcoin"), BitcoinTestnet("BTC/test", "BTCt", "Bitcoin Testnet"), BitcoinCash("BCH", "BCH", "Bitcoin Cash"), + Litecoin("LTC", "LTC", "Litecoin"), Ethereum("ETH", "ETH", "Ethereum"), RSK("RSK", "RBTC", "RSK"), Cardano("CARDANO", "ADA", "Cardano"), @@ -26,7 +27,7 @@ enum class Blockchain( Stellar("XLM", "XLM", "Stellar"); fun decimals(): Int = when (this) { - Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet -> 8 + Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet, Litecoin -> 8 Cardano, XRP -> 6 Ethereum, RSK -> 18 Stellar -> 7 @@ -43,8 +44,7 @@ enum class Blockchain( private fun getAddressService(): AddressService = when (this) { Unknown -> throw Exception("unsupported blockchain") - Bitcoin -> BitcoinAddressService() - BitcoinTestnet -> BitcoinAddressService(true) + Bitcoin, BitcoinTestnet, Litecoin -> BitcoinAddressService(this) BitcoinCash -> BitcoinCashAddressService() Ethereum, RSK -> EthereumAddressService() Cardano -> CardanoAddressService() @@ -66,6 +66,7 @@ enum class Blockchain( Bitcoin -> "https://blockchain.info/address/$address" BitcoinTestnet -> "https://live.blockcypher.com/btc-testnet/address/$address" BitcoinCash -> "https://blockchair.com/bitcoin-cash/address/$address" + Litecoin -> "https://live.blockcypher.com/ltc/address/$address" Cardano -> "https://cardanoexplorer.com/address/$address" Ethereum -> if (token == null) { "https://etherscan.io/address/" diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt index 621ed2f129..2f3d29bc18 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt @@ -15,6 +15,8 @@ import com.tangem.blockchain.blockchains.cardano.network.CardanoNetworkManager import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionBuilder import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.blockchains.ethereum.network.EthereumNetworkManager +import com.tangem.blockchain.blockchains.litecoin.LitecoinNetworkManager +import com.tangem.blockchain.blockchains.litecoin.LitecoinWalletManager import com.tangem.blockchain.blockchains.stellar.StellarNetworkManager import com.tangem.blockchain.blockchains.stellar.StellarTransactionBuilder @@ -42,24 +44,31 @@ object WalletManagerFactory { Blockchain.Bitcoin -> { return BitcoinWalletManager( cardId, wallet, - BitcoinTransactionBuilder(walletPublicKey), - BitcoinNetworkManager() + BitcoinTransactionBuilder(walletPublicKey, blockchain), + BitcoinNetworkManager(blockchain) ) } Blockchain.BitcoinTestnet -> { return BitcoinWalletManager( cardId, wallet, - BitcoinTransactionBuilder(walletPublicKey, true), - BitcoinNetworkManager(true) + BitcoinTransactionBuilder(walletPublicKey, blockchain), + BitcoinNetworkManager(blockchain) ) } Blockchain.BitcoinCash -> { return BitcoinCashWalletManager( cardId, wallet, - BitcoinCashTransactionBuilder(walletPublicKey.toCompressedPublicKey()), + BitcoinCashTransactionBuilder(walletPublicKey.toCompressedPublicKey(), blockchain), BitcoinCashNetworkManager() ) } + Blockchain.Litecoin -> { + return LitecoinWalletManager( + cardId, wallet, + BitcoinTransactionBuilder(walletPublicKey, blockchain), + LitecoinNetworkManager() + ) + } Blockchain.Ethereum, Blockchain.RSK -> { return EthereumWalletManager( cardId, wallet, diff --git a/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt index a14c6b8561..69e8111e66 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockchair/BlockchairProvider.kt @@ -15,7 +15,8 @@ import java.math.RoundingMode class BlockchairProvider(private val api: BlockchairApi, blockchain: Blockchain) : BitcoinProvider { private val blockchainPath = when (blockchain) { Blockchain.BitcoinCash -> "bitcoin-cash" - else -> throw Exception("${blockchain.fullName} blockchain is not supported by BlockchairProvider") + Blockchain.Litecoin -> "litecoin" + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") } private val decimals = blockchain.decimals() diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/BlockcypherApi.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherApi.kt similarity index 80% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/BlockcypherApi.kt rename to blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherApi.kt index 14b78b0e53..77ea5eaf45 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/api/BlockcypherApi.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherApi.kt @@ -1,9 +1,6 @@ -package com.tangem.blockchain.blockchains.bitcoin.network.api +package com.tangem.blockchain.network.blockcypher import com.squareup.moshi.JsonClass -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherFee -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherResponse -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherTx import retrofit2.http.* interface BlockcypherApi { diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherProvider.kt similarity index 66% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt rename to blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherProvider.kt index 58b6bec6a4..857e06f449 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/BlockcypherProvider.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherProvider.kt @@ -1,30 +1,35 @@ -package com.tangem.blockchain.blockchains.bitcoin.network +package com.tangem.blockchain.network.blockcypher import com.tangem.blockchain.blockchains.bitcoin.BitcoinUnspentOutput -import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherApi -import com.tangem.blockchain.blockchains.bitcoin.network.api.BlockcypherBody -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherFee -import com.tangem.blockchain.blockchains.bitcoin.network.response.BlockcypherResponse +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinAddressResponse +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinFee +import com.tangem.blockchain.blockchains.bitcoin.network.BitcoinProvider +import com.tangem.blockchain.network.blockcypher.BlockcypherApi +import com.tangem.blockchain.network.blockcypher.BlockcypherBody import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.extensions.retryIO import com.tangem.common.extensions.hexToBytes -class BlockcypherProvider(private val api: BlockcypherApi, isTestNet: Boolean) : BitcoinProvider { +class BlockcypherProvider(private val api: BlockcypherApi, blockchain: Blockchain) : BitcoinProvider { - private val blockchain = "btc" - private val decimals = Blockchain.Bitcoin.decimals() - - private val network = if (isTestNet) { - BlockcypherNetwork.Test.network - } else { - BlockcypherNetwork.Main.network + private val blockchainPath = when (blockchain) { + Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> "btc" + Blockchain.Litecoin -> "ltc" + else -> throw Exception("${blockchain.fullName} blockchain is not supported by ${this::class.simpleName}") } + private val network = when (blockchain) { + Blockchain.BitcoinTestnet -> "test3" + else -> "main" + } + + private val decimals = blockchain.decimals() + override suspend fun getInfo(address: String): Result { try { - val addressData: BlockcypherResponse = retryIO { api.getAddressData(blockchain, network, address) } + val addressData: BlockcypherResponse = retryIO { api.getAddressData(blockchainPath, network, address) } val unspents = addressData.txrefs?.map { BitcoinUnspentOutput( it.amount!!.toBigDecimal().movePointLeft(decimals), @@ -45,7 +50,7 @@ class BlockcypherProvider(private val api: BlockcypherApi, isTestNet: Boolean) : override suspend fun getFee(): Result { return try { - val receivedFee: BlockcypherFee = retryIO { api.getFee(blockchain, network) } + val receivedFee: BlockcypherFee = retryIO { api.getFee(blockchainPath, network) } Result.Success( BitcoinFee(receivedFee.minFeePerKb!!.toBigDecimal().movePointLeft(decimals), receivedFee.normalFeePerKb!!.toBigDecimal().movePointLeft(decimals), @@ -60,7 +65,7 @@ class BlockcypherProvider(private val api: BlockcypherApi, isTestNet: Boolean) : return try { retryIO { api.sendTransaction( - blockchain, network, BlockcypherBody(transaction), BlockcypherToken.getToken()) + blockchainPath, network, BlockcypherBody(transaction), BlockcypherToken.getToken()) } SimpleResult.Success } catch (error: Exception) { @@ -76,9 +81,4 @@ private object BlockcypherToken { "66a8a37c5e9d4d2c9bb191acfe7f93aa") fun getToken(): String = tokens.random() -} - -private enum class BlockcypherNetwork(val network: String) { - Main("main"), - Test("test3") } \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/response/BlockcypherResponse.kt b/blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherResponse.kt similarity index 95% rename from blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/response/BlockcypherResponse.kt rename to blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherResponse.kt index a0e79c45b2..71dec770fe 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/bitcoin/network/response/BlockcypherResponse.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/network/blockcypher/BlockcypherResponse.kt @@ -1,4 +1,4 @@ -package com.tangem.blockchain.blockchains.bitcoin.network.response +package com.tangem.blockchain.network.blockcypher import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressTest.kt b/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressTest.kt index c8b7cca7ef..9fabb12561 100644 --- a/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressTest.kt +++ b/blockchain/src/test/java/com/tangem/blockchain/blockchains/bitcoin/BitcoinAddressTest.kt @@ -1,12 +1,13 @@ package com.tangem.blockchain.blockchains.bitcoin import com.google.common.truth.Truth +import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.hexToBytes import org.junit.Test class BitcoinAddressTest { - private val addressService = BitcoinAddressService() + private val addressService = BitcoinAddressService(Blockchain.Bitcoin) @Test fun makeAddressFromCorrectPublicKey() { diff --git a/blockchain/src/test/java/com/tangem/blockchain/blockchains/litecoin/LitecoinAddressTest.kt b/blockchain/src/test/java/com/tangem/blockchain/blockchains/litecoin/LitecoinAddressTest.kt new file mode 100644 index 0000000000..a8753d94a4 --- /dev/null +++ b/blockchain/src/test/java/com/tangem/blockchain/blockchains/litecoin/LitecoinAddressTest.kt @@ -0,0 +1,28 @@ +package com.tangem.blockchain.blockchains.litecoin + +import com.google.common.truth.Truth +import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressService +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.extensions.hexToBytes +import org.junit.Test + +class LitecoinAddressTest { + + private val addressService = BitcoinAddressService(Blockchain.Litecoin) + + @Test + fun makeAddressFromCorrectPublicKey() { + val walletPublicKey = "044A76C9A70422160F515F956D0F50C71BBBA4F9862A22913817D63F0B1EF7C2FAF512E1C91B1BE827560EFE24FB1652B47337E296C778DFB1014D080CDD35EF65".hexToBytes() + val expected = "LeweDi2SMmishGyCqQN2972qNByaSRdcfT" + + Truth.assertThat(addressService.makeAddress(walletPublicKey)) + .isEqualTo(expected) + } + + @Test + fun validateCorrectAddress() { + val address = "LeweDi2SMmishGyCqQN2972qNByaSRdcfT" + Truth.assertThat(addressService.validate(address)) + .isTrue() + } +} \ No newline at end of file diff --git a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt index ff9edbcdd1..fc6ced7192 100644 --- a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt +++ b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt @@ -7,6 +7,7 @@ import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashWalletManager import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager +import com.tangem.blockchain.blockchains.litecoin.LitecoinWalletManager import com.tangem.blockchain.blockchains.stellar.StellarWalletManager import com.tangem.blockchain.blockchains.xrp.XrpWalletManager import com.tangem.commands.ReadCommand @@ -92,4 +93,15 @@ internal class WalletManagerFactoryTest { Truth.assertThat(walletManager) .isInstanceOf(BitcoinCashWalletManager::class.java) } + + @Test + fun createLitecoinWalletManager() { + val data = "0108BB00000000000023200754414E47454D00020102800A322E3432642053444B000341043539F86A40ADD04CE165764A761FD3E4D251028615D2A573B1C3AE652E60AFDBFAF02E3239E89EF2C43FA448A327557ADC5AF36376A0574570F6DBD20113514A0A04041E76310C618102FFFF8A0101820407E40414830B54414E47454D2053444B0084034C5443864004BDEAD0117544886346CB47F7CA84ABA8C34239502F23D28595A4B16CAD72F7DE506BA818B86A649C2BB945986D4574993B3B755B47CBEE31C4FB931F6748183041045F16BD1D2EAFE463E62A335A09E6B2BBCBD04452526885CB679FC4D27AF1BD22F553C7DEEFB54FD3D4F361D14E6DC3F11B7D4EA183250A60720EBDF9E110CD26050A736563703235366B31000804000186A00701006041044A76C9A70422160F515F956D0F50C71BBBA4F9862A22913817D63F0B1EF7C2FAF512E1C91B1BE827560EFE24FB1652B47337E296C778DFB1014D080CDD35EF6562040001869D6304000000030F01009000" + val responseApdu = ResponseApdu(data.hexToBytes()) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) + val walletManager = WalletManagerFactory.makeWalletManager(card!!) + + Truth.assertThat(walletManager) + .isInstanceOf(LitecoinWalletManager::class.java) + } } \ No newline at end of file diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java index 4e704fe1f0..69cf204c87 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java @@ -284,7 +284,7 @@ public class TLV { return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), ProductMask.getDescription(iValue)); } catch (Exception e) { e.printStackTrace(); - return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value)); + return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value)); } } else { return String.format("%s[]: [[NULL]]", tag.name()); diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index 6021923099..c7fa4559e7 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -19,6 +19,8 @@ import com.tangem.crypto.pbkdf2Hash */ interface CardSessionRunnable { + val performPreflightRead: Boolean + /** * The starting point for custom business logic. * Implement this interface and use [TangemSdk.startSessionWithRunnable] to run. @@ -55,6 +57,8 @@ class CardSession( */ private var isBusy = false + private var performPreflightRead = true + /** * This metod starts a card session, performs preflight [ReadCommand], * invokes [CardSessionRunnable.run] and closes the session. @@ -64,6 +68,8 @@ class CardSession( fun , R : CommandResponse> startWithRunnable( runnable: T, callback: (result: CompletionResult) -> Unit) { + performPreflightRead = runnable.performPreflightRead + start { session, error -> if (error != null) { callback(CompletionResult.Failure(error)) @@ -104,6 +110,11 @@ class CardSession( callback(this, error) } + if (!performPreflightRead) { + callback(this, null) + return + } + preflightRead() { result -> when (result) { is CompletionResult.Failure -> { @@ -174,7 +185,7 @@ class CardSession( * Stops the current session on error. * @param error An error that will be shown. */ - private fun stopWithError(error: Exception) { + private fun stopWithError(error: TangemSdkError) { if (!isBusy) return reader.closeSession() @@ -187,7 +198,7 @@ class CardSession( } if (error !is TangemSdkError.UserCancelled) { Log.e(tag, "Finishing with error: $errorMessage") - viewDelegate.onError(errorMessage) + viewDelegate.onError(error) } else { Log.i(tag, "User cancelled NFC session") } diff --git a/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt b/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt index c372b1730c..969b147b8f 100644 --- a/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt +++ b/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt @@ -40,7 +40,7 @@ interface SessionViewDelegate { /** * It is called when some error occur during NFC session. */ - fun onError(errorMessage: String) + fun onError(error: TangemSdkError) /** * It is called when a user is expected to enter pin code. diff --git a/tangem-core/src/main/java/com/tangem/TangemSdk.kt b/tangem-core/src/main/java/com/tangem/TangemSdk.kt index ce0c0e8a47..b03d3c87e3 100644 --- a/tangem-core/src/main/java/com/tangem/TangemSdk.kt +++ b/tangem-core/src/main/java/com/tangem/TangemSdk.kt @@ -307,7 +307,7 @@ class TangemSdk( * card response in the form of [DepersonalizeResponse] if the task was performed successfully * or [TangemSdkError] in case of an error. * */ - fun depersonalize(cardId: String, initialMessage: Message? = null, + fun depersonalize(cardId: String? = null, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { startSessionWithRunnable(DepersonalizeCommand(), cardId, initialMessage, callback) } diff --git a/tangem-core/src/main/java/com/tangem/TangemSdkError.kt b/tangem-core/src/main/java/com/tangem/TangemSdkError.kt index bcf51d0617..4aabb3da6b 100644 --- a/tangem-core/src/main/java/com/tangem/TangemSdkError.kt +++ b/tangem-core/src/main/java/com/tangem/TangemSdkError.kt @@ -101,7 +101,10 @@ sealed class TangemSdkError(val code: Int) : Exception(code.toString()) { * Tangem cards can sign currently up to 10 hashes during one [com.tangem.commands.SignCommand]. * This error is returned when a [com.tangem.commands.SignCommand] receives more than 10 hashes to sign. */ - class TooManyhHashesInOneTransaction : TangemSdkError(40906) + class TooManyHashesInOneTransaction : TangemSdkError(40906) + + //Write Extra Issuer Data Errors + class ExendedDataSizeTooLarge : TangemSdkError(41101) //General Errors class NotPersonalized() : TangemSdkError(40001) @@ -114,6 +117,7 @@ sealed class TangemSdkError(val code: Int) : Exception(code.toString()) { */ class VerificationFailed : TangemSdkError(40005) class DataSizeTooLarge : TangemSdkError(40006) + /** * This error is returned when [ReadIssuerDataTask] or [ReadIssuerExtraDataTask] expects a counter * (when the card's requires it), but the counter is missing. diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt index e7e90c5123..0b17642735 100644 --- a/tangem-core/src/main/java/com/tangem/commands/Command.kt +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -19,6 +19,8 @@ interface CommandResponse */ abstract class Command : CardSessionRunnable { + override val performPreflightRead: Boolean = true + /** * Serializes data into an array of [com.tangem.common.tlv.Tlv], * then creates [CommandApdu] with this data. diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index 607ad41cd7..fc926be116 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -112,7 +112,7 @@ class SignCommand(private val hashes: Array) private fun checkForErrors() { if (hashes.isEmpty()) throw TangemSdkError.EmptyHashes() - if (hashes.size > 10) throw TangemSdkError.TooManyhHashesInOneTransaction() + if (hashes.size > 10) throw TangemSdkError.TooManyHashesInOneTransaction() if (hashes.any { it.size != hashSizes }) throw TangemSdkError.HashSizeMustBeEqual() } diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt index 8cb0dfc949..84e0b50851 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt @@ -94,7 +94,7 @@ class WriteIssuerExtraDataCommand( return true } if (issuerData.size > MAX_SIZE) { - callback(CompletionResult.Failure(TangemSdkError.DataSizeTooLarge())) + callback(CompletionResult.Failure(TangemSdkError.ExendedDataSizeTooLarge())) return true } if (!isCounterValid(issuerDataCounter, card)) { diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt index c3bb2d4de8..00dccae6a1 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt @@ -1,12 +1,8 @@ package com.tangem.commands.personalization -import com.tangem.CardSession import com.tangem.SessionEnvironment -import com.tangem.TangemSdkError -import com.tangem.commands.CardStatus import com.tangem.commands.Command import com.tangem.commands.CommandResponse -import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -21,17 +17,19 @@ data class DepersonalizeResponse(val success: Boolean) : CommandResponse */ class DepersonalizeCommand : Command() { - override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { - if (session.environment.card?.status == CardStatus.NotPersonalized) { - callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) - return true - } - if (session.environment.card?.firmwareVersion?.contains("SDK") == false) { - callback(CompletionResult.Failure(TangemSdkError.CannotBeDepersonalized())) - return true - } - return false - } + override val performPreflightRead = false + +// override fun performPreCheck(session: CardSession, callback: (result: CompletionResult) -> Unit): Boolean { +// if (session.environment.card?.status == CardStatus.NotPersonalized) { +// callback(CompletionResult.Failure(TangemSdkError.NotPersonalized())) +// return true +// } +// if (session.environment.card?.firmwareVersion?.contains("SDK") == false) { +// callback(CompletionResult.Failure(TangemSdkError.CannotBeDepersonalized())) +// return true +// } +// return false +// } override fun serialize(environment: SessionEnvironment): CommandApdu { return CommandApdu( diff --git a/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt b/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt index c9b87b4da2..317134af60 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt @@ -11,6 +11,8 @@ import com.tangem.common.CompletionResult class CreateWalletTask : CardSessionRunnable { + override val performPreflightRead = true + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val curve = session.environment.card?.curve if (curve == null) { diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index db78568806..c05384d5a1 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -13,6 +13,8 @@ import com.tangem.common.CompletionResult */ internal class ScanTask : CardSessionRunnable { + override val performPreflightRead = true + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val card = session.environment.card diff --git a/tangem-devkit/build.gradle b/tangem-devkit/build.gradle index 266e0bf816..fd35b8f7ed 100644 --- a/tangem-devkit/build.gradle +++ b/tangem-devkit/build.gradle @@ -9,8 +9,8 @@ android { applicationId "com.tangem.devkit" minSdkVersion 21 targetSdkVersion 29 - versionCode 3 - versionName "1.1" + versionCode 4 + versionName "1.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/DepersonalizeAction.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/DepersonalizeAction.kt index d938eb85ad..9cfec29492 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/DepersonalizeAction.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/actions/DepersonalizeAction.kt @@ -1,26 +1,14 @@ package com.tangem.devkit.ucase.domain.actions -import com.tangem.devkit._arch.structure.Id import com.tangem.devkit._arch.structure.PayloadHolder -import com.tangem.devkit._arch.structure.abstraction.findItem import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback -import com.tangem.devkit.ucase.variants.TlvId +import com.tangem.devkit.ucase.domain.paramsManager.triggers.afterAction.AfterScanModifier /** [REDACTED_AUTHOR] */ class DepersonalizeAction : BaseAction() { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { - val item = attrs.itemList.findItem(TlvId.CardId) ?: return - val cardId = item.viewModel.data as? String ?: return - - attrs.tangemSdk.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) } - } - - override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { - return when (id) { - TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) } - else -> null - } + attrs.tangemSdk.depersonalize { handleResult(payload, it, AfterScanModifier(), attrs, callback) } } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt index bb6b16540e..350a72b9f1 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/domain/paramsManager/managers/SimpleManagers.kt @@ -10,13 +10,7 @@ import com.tangem.devkit.ucase.variants.TlvId */ class ScanItemsManager : BaseItemsManager(ScanAction()) -class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) { - - init { - setItemChangeConsequences(CardIdConsequence()) - setItems(listOf(EditTextItem(TlvId.CardId, null))) - } -} +class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) class SignItemsManager : BaseItemsManager(SignAction()) { diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt index 802ba1e807..9d1409d58d 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt @@ -1,8 +1,18 @@ package com.tangem.devkit.ucase.variants.depersonalize.ui +import android.os.Bundle +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import android.widget.FrameLayout +import android.widget.TextView +import com.tangem.commands.Card +import com.tangem.devkit.R import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager import com.tangem.devkit.ucase.domain.paramsManager.managers.DepersonalizeItemsManager import com.tangem.devkit.ucase.ui.BaseCardActionFragment +import com.tangem.devkit.ucase.variants.responses.ui.ResponseFragment +import ru.dev.gbixahue.eu4d.lib.android._android.views.dpToPx /** [REDACTED_AUTHOR] @@ -10,4 +20,37 @@ import com.tangem.devkit.ucase.ui.BaseCardActionFragment class DepersonalizeActionFragment : BaseCardActionFragment() { override val itemsManager: ItemsManager by lazy { DepersonalizeItemsManager() } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val howToUseView = createHowToUse() + contentContainer.layoutParams = FrameLayout.LayoutParams(-1, -1, Gravity.CENTER) + contentContainer.addView(howToUseView) + } + + private fun createHowToUse(): View { + val fl = FrameLayout(requireContext()) + fl.layoutParams = FrameLayout.LayoutParams(-1, -1, Gravity.CENTER) + val tv = TextView(requireContext()).apply { + val padding = dpToPx(16f).toInt() + setPadding(padding, 0, padding, 0) + gravity = Gravity.CENTER + setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f) + setText(R.string.htu_depersonalize_action) + } + fl.addView(tv) + return fl + } + + override fun initViews() { + swrLayout.isEnabled = false + actionFab.setOnClickListener { actionVM.invokeMainAction() } + } + + override fun handleResponseCardData(card: Card) { + super.handleResponseCardData(card) + val bundle = ResponseFragment.setTittle(R.string.fg_name_response_depersonalization) + navigateTo(R.id.action_nav_card_action_to_response_screen, bundle, null) + } } \ No newline at end of file diff --git a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt index f8c304044c..052430a792 100644 --- a/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt +++ b/tangem-devkit/src/main/java/com/tangem/devkit/ucase/variants/personalize/converter/ItemTypes.kt @@ -38,10 +38,11 @@ class ItemTypes { ) val hiddenList = mutableListOf( - CardNumberId.Series, CardNumberId.BatchId, PinsId.Pin3, SigningMethodId.SignExternal, + CardNumberId.Series, CardNumberId.BatchId, SigningMethodId.SignExternal, SignHashExPropId.CryptoExKey, SignHashExPropId.CheckPin3, SettingsMaskId.OneApdu, SettingsMaskId.UseBlock, SettingsMaskId.ProtectIssuerDataAgainstReplay, - SignHashExPropId.RequireTerminalCertSig, SignHashExPropId.RequireTerminalTxSig + SignHashExPropId.RequireTerminalCertSig, SignHashExPropId.RequireTerminalTxSig, + BlockId.Pins, PinsId.Pin, PinsId.Pin2, PinsId.Pin3, PinsId.Cvc ) val oftenUsedList = listOf( diff --git a/tangem-devkit/src/main/res/values/strings_action.xml b/tangem-devkit/src/main/res/values/strings_action.xml index 68d55b7bd9..f1a1c5f9ca 100644 --- a/tangem-devkit/src/main/res/values/strings_action.xml +++ b/tangem-devkit/src/main/res/values/strings_action.xml @@ -30,5 +30,6 @@ This command writes to the card User Data and User Counter fields. This command writes to the card User Protected Data and User Protected Counter fields. + To depersonalize the card, press the button at the right bottom corner of the screen \ No newline at end of file diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt index a15f16521e..d8ca2ad645 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt @@ -6,12 +6,10 @@ import android.view.HapticFeedbackConstants import android.view.View import android.view.animation.DecelerateInterpolator import com.google.android.material.bottomsheet.BottomSheetDialog -import com.tangem.Log -import com.tangem.LoggerInterface -import com.tangem.Message -import com.tangem.SessionViewDelegate +import com.tangem.* import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.hide +import com.tangem.tangem_sdk_new.extensions.localizedDescription import com.tangem.tangem_sdk_new.extensions.show import com.tangem.tangem_sdk_new.nfc.NfcReader import com.tangem.tangem_sdk_new.ui.TouchCardAnimation @@ -142,14 +140,17 @@ class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDel postUI(300) { readingDialog?.dismiss() } } - override fun onError(errorMessage: String) { + override fun onError(error: TangemSdkError) { postUI { readingDialog?.lTouchCard?.hide() readingDialog?.flSecurityDelay?.hide() readingDialog?.flCompletion?.hide() readingDialog?.flError?.show() readingDialog?.tvTaskTitle?.text = activity.getText(R.string.dialog_error) - readingDialog?.tvTaskText?.text = errorMessage + readingDialog?.tvTaskText?.text = activity.getString( + R.string.error_message, + error.code.toString(), activity.getString(error.localizedDescription()) + ) performHapticFeedback() } } diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdkError.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdkError.kt new file mode 100644 index 0000000000..09c84716e4 --- /dev/null +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdkError.kt @@ -0,0 +1,56 @@ +package com.tangem.tangem_sdk_new.extensions + +import com.tangem.TangemSdkError +import com.tangem.tangem_sdk_new.R + +fun TangemSdkError.localizedDescription(): Int { + return when (this) { + is TangemSdkError.TagLost -> R.string.error_tag_lost + is TangemSdkError.ExtendedLengthNotSupported -> R.string.error_operation + is TangemSdkError.SerializeCommandError -> R.string.error_operation + is TangemSdkError.DeserializeApduFailed -> R.string.error_operation + is TangemSdkError.EncodingFailedTypeMismatch -> R.string.error_operation + is TangemSdkError.EncodingFailed -> R.string.error_operation + is TangemSdkError.DecodingFailedMissingTag -> R.string.error_operation + is TangemSdkError.DecodingFailedTypeMismatch -> R.string.error_operation + is TangemSdkError.DecodingFailed -> R.string.error_operation + is TangemSdkError.UnknownStatus -> R.string.error_operation + is TangemSdkError.ErrorProcessingCommand -> R.string.error_operation + is TangemSdkError.InvalidState -> R.string.error_operation + is TangemSdkError.InsNotSupported -> R.string.error_operation + is TangemSdkError.InvalidParams -> R.string.error_operation + is TangemSdkError.NeedEncryption -> R.string.error_operation + is TangemSdkError.AlreadyPersonalized -> R.string.error_already_personalized + is TangemSdkError.CannotBeDepersonalized -> R.string.error_cannot_be_depersonalized + is TangemSdkError.Pin1Required -> R.string.error_operation + is TangemSdkError.AlreadyCreated -> R.string.error_already_created + is TangemSdkError.PurgeWalletProhibited -> R.string.error_purge_prohibited + is TangemSdkError.Pin1CannotBeChanged -> R.string.error_pin1_cannot_be_changed + is TangemSdkError.Pin2CannotBeChanged -> R.string.error_pin2_cannot_be_changed + is TangemSdkError.Pin1CannotBeDefault -> R.string.error_pin1_cannot_be_default + is TangemSdkError.NoRemainingSignatures -> R.string.error_no_remaining_signatures + is TangemSdkError.EmptyHashes -> R.string.error_empty_hashes + is TangemSdkError.HashSizeMustBeEqual -> R.string.error_cannot_be_signed + is TangemSdkError.CardIsEmpty -> R.string.error_card_is_empty + is TangemSdkError.SignHashesNotAvailable -> R.string.error_cannot_be_signed + is TangemSdkError.TooManyHashesInOneTransaction -> R.string.error_cannot_be_signed + is TangemSdkError.NotPersonalized -> R.string.error_not_personalized + is TangemSdkError.NotActivated -> R.string.error_not_activated + is TangemSdkError.CardIsPurged -> R.string.error_purged + is TangemSdkError.Pin2OrCvcRequired -> R.string.error_operation + is TangemSdkError.VerificationFailed -> R.string.error_verification_failed + is TangemSdkError.DataSizeTooLarge -> R.string.error_data_size_too_large + is TangemSdkError.ExendedDataSizeTooLarge -> R.string.error_data_size_too_large_extended + is TangemSdkError.MissingCounter -> R.string.error_missing_counter + is TangemSdkError.OverwritingDataIsProhibited -> R.string.error_data_cannot_be_written + is TangemSdkError.DataCannotBeWritten -> R.string.error_data_cannot_be_written + is TangemSdkError.MissingIssuerPubicKey -> R.string.error_missing_issuer_public_key + is TangemSdkError.UnknownError -> R.string.error_operation + is TangemSdkError.UserCancelled -> R.string.error_user_cancelled + is TangemSdkError.Busy -> R.string.error_busy + is TangemSdkError.MissingPreflightRead -> R.string.error_operation + is TangemSdkError.WrongCardNumber -> R.string.error_wrong_card_number + is TangemSdkError.WrongCardType -> R.string.error_wrong_card_type + is TangemSdkError.CardError -> R.string.error_card_error + } +} \ No newline at end of file diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt index aa5ce9972e..3643d3b945 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt @@ -10,6 +10,7 @@ import com.tangem.TangemSdkError import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.extensions.toHexString /** * Provides NFC communication between an Android application and Tangem card. @@ -76,7 +77,9 @@ class NfcReader : CardReader { val rawResponse: ByteArray? try { Log.i(this::class.simpleName!!, "Sending data to the card, size is ${data?.size}") + Log.v(this::class.simpleName!!, "Raw data that is to be send to the card: ${data?.toHexString()}") rawResponse = isoDep?.transceive(data) + Log.v(this::class.simpleName!!, "Raw data that was received from the card: ${rawResponse?.toHexString()}") } catch (exception: TagLostException) { callback?.invoke(CompletionResult.Failure(TangemSdkError.TagLost())) isoDep = null diff --git a/tangem-sdk/src/main/res/values/strings_errors.xml b/tangem-sdk/src/main/res/values/strings_errors.xml new file mode 100644 index 0000000000..b41a0e5d1f --- /dev/null +++ b/tangem-sdk/src/main/res/values/strings_errors.xml @@ -0,0 +1,32 @@ + + + %s: %s + + An error occurred during the operation + Tag lost. + This card has already been personalized + This card cannot be depersonalized + A wallet has already been created + This card’s configuration does not allow you to erase the wallet + PIN1 Access Code cannot be changed + PIN2 Passcode cannot be changed + PIN1 Access Code cannot be changed to the default value. Please create a new PIN1 Access Code. + You cannot sign data with this wallet because the card has reached its signature limit + There is nothing to sign here + This data cannot be signed + Please create a wallet to sign data + This card has not been personalized + This card has not been activated + You cannot sign data with this wallet because the card has been purged + Data verification failed + Data size exceeds the max limit of 512 bytes + Data size exceeds the max limit of 32 Kbytes + The data cannot be written to the card because there is no counter + The data cannot be written to the card + The issuer public key is missing + The user cancelled the operation + The CardSession is running a command. Please wait and try again + You tapped a different card. Please match the in-app Card ID to the your physical Card ID to continue this process. + This card is configured for a different app. Please see the information on your card and download the related app. + Your card is missing essential data + \ No newline at end of file