Updated on 2026-08-14

This commit is contained in:
Tangem 2019-09-20 10:33:48 +00:00
commit f08f12033f
56 changed files with 1062 additions and 398 deletions

View file

@ -46,7 +46,7 @@ android {
applicationIdSuffix ".debug_beta"
buildConfigField 'boolean', 'CRASHLYTICS', 'Boolean.parseBoolean("true")'
manifestPlaceholders = [fabric_api_key: properties.hasProperty('fabric_api_key')
? project.property('fabric_api_key') : 000]
? project.property('fabric_api_key') : '000']
}
}
flavorDimensions ""
@ -91,27 +91,26 @@ dependencies {
implementation project(':tangem-card')
implementation project(':tangem-sdk')
implementation project(':server-android')
// implementation 'com.github.TangemCash:card-common:0.1.2'
// implementation 'com.github.TangemCash:card-android:0.1.3'
// implementation 'com.github.TangemCash:server-android:0.1.4'
implementation 'android.arch.navigation:navigation-fragment:1.0.0'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0'
implementation 'androidx.navigation:navigation-fragment:2.1.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation "androidx.lifecycle:lifecycle-runtime:2.0.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.0.0"
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation 'androidx.fragment:fragment:1.1.0-rc04'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation "androidx.lifecycle:lifecycle-runtime:2.1.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.1.0"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.fragment:fragment:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.preference:preference:1.1.0-rc01"
implementation 'androidx.biometric:biometric:1.0.0-alpha04'
implementation 'com.google.android.material:material:1.1.0-alpha09'
implementation 'androidx.core:core-ktx:1.0.2'
implementation "androidx.preference:preference:1.1.0"
implementation 'androidx.biometric:biometric:1.0.0-beta01'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'com.google.android.material:material:1.1.0-alpha10'
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.google.zxing:core:3.4.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.madgag.spongycastle:core:1.56.0.0'
implementation 'com.madgag.spongycastle:prov:1.56.0.0'

View file

@ -97,4 +97,6 @@ object Constant {
const val REQUEST_CODE_SCAN_QR_SECRET = "REQUEST_CODE_SCAN_QR_SECRET"
const val REQUEST_CODE_SCAN_QR_USER_ID = "REQUEST_CODE_SCAN_QR_USER_ID"
const val TERMINAL_PRIVATE_KEY = "terminalPrivateKey"
const val TERMINAL_PUBLIC_KEY = "terminalPublicKey"
}

View file

@ -3,9 +3,9 @@ package com.tangem.data.dp
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import com.orhanobut.hawk.Hawk
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardCrypto
class PrefsManager {
companion object {
@ -47,4 +47,18 @@ class PrefsManager {
}
fun getAllCids(): String = Hawk.get("CID_Key", "")
val terminalKeys: Map<String, ByteArray>
get() {
val privateKey = Hawk.get<ByteArray>(Constant.TERMINAL_PRIVATE_KEY) ?: byteArrayOf()
val publicKey = Hawk.get<ByteArray>(Constant.TERMINAL_PUBLIC_KEY) ?: byteArrayOf()
return if (privateKey.isNotEmpty() && publicKey.isNotEmpty()) {
mapOf(Constant.TERMINAL_PRIVATE_KEY to privateKey, Constant.TERMINAL_PUBLIC_KEY to publicKey)
} else {
val keys = CardCrypto.generateTerminalKeys()
Hawk.put(Constant.TERMINAL_PRIVATE_KEY, keys[Constant.TERMINAL_PRIVATE_KEY])
Hawk.put(Constant.TERMINAL_PUBLIC_KEY, keys[Constant.TERMINAL_PUBLIC_KEY])
keys
}
}
}

View file

@ -2,13 +2,14 @@ package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.data.network.model.AdaliteBody;
import com.tangem.data.network.model.AdaliteResponse;
import com.tangem.data.network.model.AdaliteResponseUtxo;
import java.util.List;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@ -26,7 +27,14 @@ public class ServerApiAdalite {
private int requestsCount = 0;
public static String lastNode;
private final String adaliteURL1 = "https://explorer2.adalite.io"; //TODO: make random selection, add more?, move
private final String adaliteURL2 = "https://nodes.southeastasia.cloudapp.azure.com";
private String currentURL = adaliteURL1;
public String getCurrentURL() {
return currentURL;
}
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
@ -50,12 +58,14 @@ public class ServerApiAdalite {
}
public void requestData(String method, String wallet, String tx) {
requestData(method, wallet, tx, false);
}
public void requestData(String method, String wallet, String tx, boolean isRetry) {
requestsCount++;
String adaliteURL = "https://explorer2.adalite.io"; //TODO: make random selection
this.lastNode = adaliteURL; //TODO: show node instead of URL
Retrofit retrofitAdalite = new Retrofit.Builder()
.baseUrl(adaliteURL)
.baseUrl(currentURL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
@ -69,20 +79,32 @@ public class ServerApiAdalite {
addressCall.enqueue(new Callback<AdaliteResponse>() {
@Override
public void onResponse(@NonNull Call<AdaliteResponse> call, @NonNull Response<AdaliteResponse> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(response.code()));
}
}
}
@Override
public void onFailure(@NonNull Call<AdaliteResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
requestsCount--;
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(t.getMessage()));
}
}
});
break;
@ -92,20 +114,32 @@ public class ServerApiAdalite {
outputsCall.enqueue(new Callback<AdaliteResponseUtxo>() {
@Override
public void onResponse(@NonNull Call<AdaliteResponseUtxo> call, @NonNull Response<AdaliteResponseUtxo> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(response.code()));
}
}
}
@Override
public void onFailure(@NonNull Call<AdaliteResponseUtxo> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
requestsCount--;
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(t.getMessage()));
}
}
});
break;
@ -115,30 +149,48 @@ public class ServerApiAdalite {
sendCall.enqueue(new Callback<List>() {
@Override
public void onResponse(@NonNull Call<List> call, @NonNull Response<List> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(response.code()));
}
}
}
@Override
public void onFailure(@NonNull Call<List> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
requestsCount--;
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(t.getMessage()));
}
}
});
break;
default:
requestsCount--;
responseListener.onFail(method, "undeclared method");
Log.e(TAG, "requestData " + method + " onFailure - undeclared method");
break;
}
}
private void retryRequest(String method, String wallet, String tx) {
currentURL = adaliteURL2;
requestData(method, wallet, tx, true);
}
}

View file

@ -1,8 +1,9 @@
package com.tangem.data.network;
import androidx.annotation.NonNull;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
@ -84,8 +85,9 @@ public class ServerApiInfura {
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
@ -96,6 +98,7 @@ public class ServerApiInfura {
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}

View file

@ -2,12 +2,13 @@ package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
import java.util.List;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@ -64,8 +65,9 @@ public class ServerApiInsight {
call.enqueue(new Callback<List<InsightResponse>>() {
@Override
public void onResponse(@NonNull Call<List<InsightResponse>> call, @NonNull Response<List<InsightResponse>> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
@ -76,6 +78,7 @@ public class ServerApiInsight {
@Override
public void onFailure(@NonNull Call<List<InsightResponse>> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
@ -108,8 +111,9 @@ public class ServerApiInsight {
call.enqueue(new Callback<InsightResponse>() {
@Override
public void onResponse(@NonNull Call<InsightResponse> call, @NonNull Response<InsightResponse> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
@ -120,6 +124,7 @@ public class ServerApiInsight {
@Override
public void onFailure(@NonNull Call<InsightResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}

View file

@ -1,8 +1,9 @@
package com.tangem.data.network;
import androidx.annotation.NonNull;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
@ -84,8 +85,9 @@ public class ServerApiMatic {
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
@ -96,6 +98,7 @@ public class ServerApiMatic {
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}

View file

@ -135,8 +135,9 @@ public class ServerApiRipple {
call.enqueue(new Callback<RippleResponse>() {
@Override
public void onResponse(@NonNull Call<RippleResponse> call, @NonNull Response<RippleResponse> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
@ -147,6 +148,7 @@ public class ServerApiRipple {
@Override
public void onFailure(@NonNull Call<RippleResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}

View file

@ -1,8 +1,9 @@
package com.tangem.data.network;
import androidx.annotation.NonNull;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
@ -84,8 +85,9 @@ public class ServerApiRootstock {
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
requestsCount--;
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
@ -96,6 +98,7 @@ public class ServerApiRootstock {
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}

View file

@ -2,11 +2,12 @@ package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.SoChain;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@ -164,9 +165,9 @@ public class ServerApiSoChain {
call.enqueue(new Callback<SoChain.Response.GetTx>() {
@Override
public void onResponse(@NonNull Call<SoChain.Response.GetTx> call, @NonNull Response<SoChain.Response.GetTx> response) {
requestsCount--;
Log.i(TAG, "requestAddressBalance onResponse " + response.code());
if (response.code() == 200) {
requestsCount--;
txInfoListener.onSuccess(response.body());
} else {
txInfoListener.onFail(String.valueOf(response.code()));
@ -175,6 +176,7 @@ public class ServerApiSoChain {
@Override
public void onFailure(@NonNull Call<SoChain.Response.GetTx> call, @NonNull Throwable t) {
requestsCount--;
Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage());
txInfoListener.onFail(String.valueOf(t.getMessage()));
}

View file

@ -2,21 +2,16 @@
package com.tangem.ui.activity
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProviders
import com.scottyab.rootbeer.RootBeer
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.ToastHelper
import com.tangem.tangem_sdk.android.nfc.NfcLifecycleObserver
import com.tangem.tangem_sdk.android.reader.NfcManager
@ -59,8 +54,6 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
verifyPermissions()
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
@ -79,13 +72,6 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
RootFoundDialog().show(supportFragmentManager, RootFoundDialog.TAG)
}
private fun verifyPermissions() {
NfcManager.verifyPermissions(this)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), Constant.REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS)
}
}
override fun onTagDiscovered(tag: Tag) {
val activeFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
?.childFragmentManager?.primaryNavigationFragment

View file

@ -1,6 +1,7 @@
package com.tangem.ui.fragment
import android.app.Activity
import android.app.AlertDialog
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
@ -17,6 +18,7 @@ import com.tangem.tangem_card.data.TangemCard
import com.tangem.tangem_card.reader.CardProtocol
import com.tangem.tangem_card.tasks.CustomReadCardTask
import com.tangem.tangem_card.tasks.ReadCardInfoTask
import com.tangem.tangem_sdk.android.data.PINStorage
import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation
import com.tangem.tangem_sdk.android.reader.NfcReader
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
@ -56,6 +58,7 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
private var task: CustomReadCardTask? = null
private var lastTag: Tag? = null
private var zipFile: File? = null
private var unknownBlockchain = false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
setHasOptionsMenu(true)
@ -112,6 +115,11 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
}
override fun onTagDiscovered(tag: Tag) {
if (unknownBlockchain) {
(activity as MainActivity).nfcManager.ignoreTag(tag)
return
}
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
@ -122,6 +130,10 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
lastTag = tag
val terminalKeys = viewModel.getTerminalKeys()
PINStorage.setTerminalPrivateKey(terminalKeys[Constant.TERMINAL_PRIVATE_KEY])
PINStorage.setTerminalPublicKey(terminalKeys[Constant.TERMINAL_PUBLIC_KEY])
task = ReadCardInfoTask(NfcReader((activity as MainActivity).nfcManager, isoDep),
App.localStorage, App.pinStorage, this)
task?.start()
@ -159,6 +171,10 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
val card = TangemCard(uid)
cardInfo.getBundle(EXTRA_TANGEM_CARD)?.let { card.loadFromBundle(it) }
val terminalKeys = viewModel.getTerminalKeys()
card.terminalPrivateKey = terminalKeys[Constant.TERMINAL_PRIVATE_KEY]
card.terminalPublicKey = terminalKeys[Constant.TERMINAL_PUBLIC_KEY]
val ctx = TangemContext(card)
when {
@ -172,7 +188,8 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
ctx.saveToBundle(bundle)
navigateForResult(Constant.REQUEST_CODE_SHOW_CARD_ACTIVITY,
R.id.action_main_to_loadedWalletFragment, bundle)
} else {
showUnkownBlockchainWarning()
}
}
card.status == TangemCard.Status.Empty -> {
@ -219,6 +236,17 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
rlProgressBar?.postDelayed({ rlProgressBar?.visibility = View.GONE }, 500)
}
private fun showUnkownBlockchainWarning() {
unknownBlockchain = true
AlertDialog.Builder(context)
.setTitle(R.string.dialog_warning)
.setMessage(R.string.alert_unknown_blockchain)
.setPositiveButton(R.string.general_ok) { _, _ -> }
.setOnDismissListener { unknownBlockchain = false }
.create()
.show()
}
override fun onReadCancel() {
task = null
ReadCardInfoTask.resetLastReadInfo()

View file

@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.tangem.Constant
import com.tangem.data.dp.PrefsManager
import com.tangem.data.network.ServerApiCommon
import com.tangem.wallet.BuildConfig
@ -35,4 +36,9 @@ class MainViewModel : ViewModel() {
}
serverApiCommon.requestLastVersion()
}
fun getTerminalKeys(): Map<String, ByteArray> {
return PrefsManager.getInstance().terminalKeys
}
}

View file

@ -1,70 +0,0 @@
package com.tangem.ui.fragment
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.app.ActivityCompat
import com.google.zxing.Result
import com.tangem.Constant
import me.dm7.barcodescanner.zxing.ZXingScannerView
class QrScanFragment : BaseFragment(), ZXingScannerView.ResultHandler {
companion object {
fun callingIntent(context: Context): Intent {
return Intent(context, QrScanFragment::class.java)
}
}
override val layoutId = 0
private var scannerView: ZXingScannerView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (ActivityCompat.checkSelfPermission(context!!, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(activity!!, arrayOf(Manifest.permission.CAMERA), 1)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
scannerView = ZXingScannerView(activity)
return scannerView
}
override fun onPause() {
super.onPause()
scannerView?.stopCamera()
}
override fun onResume() {
super.onResume()
scannerView?.setResultHandler(this)
scannerView?.startCamera()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
1 -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)
else {
navigateBackWithResult(Activity.RESULT_CANCELED)
}
}
}
}
override fun handleResult(result: Result) {
val data = Bundle()
data.putString(Constant.EXTRA_QR_CODE, result.text)
navigateBackWithResult(Activity.RESULT_OK, data)
}
}

View file

@ -333,36 +333,48 @@ class VerifyCardFragment : BaseFragment(), NavigationResultListener, NfcAdapter.
var features = ""
features += if (ctx.card!!.allowSwapPIN()!! && ctx.card!!.allowSwapPIN2()!!) {
"Allows change PIN1 and PIN2\n"
getString(R.string.details_both_pins_can_be_changed)
} else if (ctx.card!!.allowSwapPIN()!!) {
"Allows change PIN1\n"
getString(R.string.details_pin1_can_be_changed)
} else if (ctx.card!!.allowSwapPIN2()!!) {
"Allows change PIN2\n"
getString(R.string.details_pin2_can_be_changed)
} else {
"Fixed PIN1 and PIN2\n"
getString(R.string.details_both_pins_fixed)
}
if (ctx.card!!.needCVC()!!)
features += "Requires CVC\n"
features += getString(R.string.details_required_cvc)
if (ctx.card!!.supportDynamicNDEF()!!) {
features += "Dynamic NDEF for iOS\n"
features += getString(R.string.details_dynamic_ndef)
} else if (ctx.card!!.supportNDEF()!!)
features += "NDEF\n"
features += getString(R.string.details_ndef)
if (ctx.card!!.supportBlock()!!)
features += "Blockable\n"
features += getString(R.string.details_blockable)
if (ctx.card!!.supportLinkingTerminal()) {
features += getString(R.string.details_linking_card_supported)
llLinkedCard.visibility = View.VISIBLE
}
if (ctx.card!!.supportOnlyOneCommandAtTime()!!)
features += "Atomic command mode"
if (ctx.card!!.supportOnlyOneCommandAtTime())
features += getString(R.string.details_atomic_commmands)
if (features.endsWith("\n"))
features = features.substring(0, features.length - 1)
tvFeatures.text = features
val textIsLinked = if (ctx.card.terminalIsLinked) {
getString(R.string.details_linked_card_to_phone)
} else {
getString(R.string.general_no)
}
tvIsLinked.text = textIsLinked
if (ctx.card!!.useDefaultPIN1()) {
imgPIN.setImageResource(R.drawable.unlock_pin1)
imgPIN.setOnClickListener { Toast.makeText(context, R.string.details_protected_by_default_pin_1, Toast.LENGTH_LONG).show() }

View file

@ -11,6 +11,7 @@ import com.tangem.data.Blockchain
import com.tangem.data.network.CryptonitOtherApi
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.fragment.BaseFragment
import com.tangem.ui.fragment.qr.CameraPermissionManager
import com.tangem.ui.navigation.NavigationResultListener
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.R
@ -27,6 +28,7 @@ class PrepareCryptonitOtherApiWithdrawalFragment : BaseFragment(), NavigationRes
override val layoutId = R.layout.fragment_prepare_cryptonit_other_api_withdrawal
private val cameraPermissionManager: CameraPermissionManager by lazy { CameraPermissionManager(this) }
private val ctx: TangemContext by lazy { TangemContext.loadFromBundle(context, arguments) }
private val cryptonit: CryptonitOtherApi by lazy { CryptonitOtherApi(context) }
@ -69,23 +71,11 @@ class PrepareCryptonitOtherApiWithdrawalFragment : BaseFragment(), NavigationRes
// }
}
ivCameraKey.setOnClickListener {
navigateForResult(
Constant.REQUEST_CODE_SCAN_QR_KEY,
R.id.action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment)
}
ivCameraKey.setOnClickListener { checkPermissionsAndRunCamera() }
ivCameraSecret.setOnClickListener {
navigateForResult(
Constant.REQUEST_CODE_SCAN_QR_SECRET,
R.id.action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment)
}
ivCameraSecret.setOnClickListener { checkPermissionsAndRunCamera() }
ivCameraUserId.setOnClickListener {
navigateForResult(
Constant.REQUEST_CODE_SCAN_QR_USER_ID,
R.id.action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment)
}
ivCameraUserId.setOnClickListener { checkPermissionsAndRunCamera() }
ivRefreshBalance.setOnClickListener { doRequestBalance() }
@ -125,6 +115,14 @@ class PrepareCryptonitOtherApiWithdrawalFragment : BaseFragment(), NavigationRes
doRequestBalance()
}
private fun checkPermissionsAndRunCamera() {
if (cameraPermissionManager.isPermissionGranted()) {
navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment)
} else {
cameraPermissionManager.requirePermission()
}
}
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
if (resultCode == Activity.RESULT_OK && data != null && data.containsKey("QRCode")) {
when (requestCode) {
@ -166,4 +164,11 @@ class PrepareCryptonitOtherApiWithdrawalFragment : BaseFragment(), NavigationRes
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
cameraPermissionManager.handleRequestPermissionResult(requestCode, grantResults) {
navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment)
}
}
}

View file

@ -0,0 +1,34 @@
package com.tangem.ui.fragment.qr
import android.Manifest
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker
import com.tangem.ui.fragment.BaseFragment
import com.tangem.wallet.R
class CameraPermissionManager(val fragment: BaseFragment) {
fun isPermissionGranted(): Boolean {
return ContextCompat.checkSelfPermission(fragment.requireContext(), Manifest.permission.CAMERA) ==
PermissionChecker.PERMISSION_GRANTED
}
fun handleRequestPermissionResult(requestCode: Int, grantResults: IntArray, action: () -> Unit) {
when (requestCode) {
1 -> {
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(fragment.requireContext(), R.string.general_toast_no_permission, Toast.LENGTH_LONG).show()
} else {
action.invoke()
}
}
}
}
fun requirePermission() {
fragment.requestPermissions(arrayOf(Manifest.permission.CAMERA), 1)
}
}

View file

@ -0,0 +1,40 @@
package com.tangem.ui.fragment.qr
import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.zxing.Result
import com.tangem.Constant
import com.tangem.ui.fragment.BaseFragment
import me.dm7.barcodescanner.zxing.ZXingScannerView
class QrScanFragment : BaseFragment(), ZXingScannerView.ResultHandler {
override val layoutId = 0
private var scannerView: ZXingScannerView? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
scannerView = ZXingScannerView(activity)
return scannerView
}
override fun onPause() {
super.onPause()
scannerView?.stopCamera()
}
override fun onResume() {
super.onResume()
scannerView?.setResultHandler(this)
scannerView?.startCamera()
}
override fun handleResult(result: Result) {
val data = Bundle()
data.putString(Constant.EXTRA_QR_CODE, result.text)
navigateBackWithResult(Activity.RESULT_OK, data)
}
}

View file

@ -595,23 +595,23 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte
}
if (ctx.message == null || ctx.message.isEmpty()) {
tvMessage.text = ""
tvMessage.visibility = View.GONE
tvMessage?.text = ""
tvMessage?.visibility = View.GONE
} else {
tvMessage.text = ctx.message
tvMessage.visibility = View.VISIBLE
tvMessage?.text = ctx.message
tvMessage?.visibility = View.VISIBLE
}
if (tvError.visibility == View.VISIBLE || tvMessage.visibility == View.VISIBLE) {
if (tvError?.visibility == View.VISIBLE || tvMessage?.visibility == View.VISIBLE) {
timerHideErrorAndMessage = Timer()
timerHideErrorAndMessage!!.schedule(
timerHideErrorAndMessage?.schedule(
timerTask {
activity?.runOnUiThread {
tvMessage?.visibility = View.GONE
tvError?.visibility = View.GONE
// clear only already viewed messages
if (tvMessage.text == ctx.message) ctx.message = null
if (tvError.text == ctx.error) ctx.error = null
if (tvMessage?.text == ctx.message) ctx.message = null
if (tvError?.text == ctx.error) ctx.error = null
}
},
5000)
@ -627,9 +627,9 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte
val validator = BalanceValidator()
// TODO why attest=false?
validator.check(ctx, false)
context?.let { ContextCompat.getColor(it, validator.color) }?.let { tvBalanceLine1.setTextColor(it) }
tvBalanceLine1.text = validator.firstLine
tvBalanceLine2.text = validator.getSecondLine(false)
context?.let { ContextCompat.getColor(it, validator.color) }?.let { tvBalanceLine1?.setTextColor(it) }
tvBalanceLine1?.text = getString(validator.firstLine)
tvBalanceLine2?.text = getString(validator.getSecondLine(false))
}
val engine = CoinEngineFactory.create(ctx)

View file

@ -1,36 +1,40 @@
package com.tangem.wallet;
import androidx.annotation.StringRes;
import com.tangem.App;
import com.tangem.tangem_card.data.TangemCard;
public class BalanceValidator {
private String firstLine;
private String secondLine;
private @StringRes int firstLine;
private @StringRes int secondLine;
private int score;
private boolean hasPending;
public String getFirstLine() {
public @StringRes int getFirstLine() {
return firstLine;
}
public void setFirstLine(String value) {
public void setFirstLine(@StringRes int value) {
firstLine = value;
}
public String getSecondLine(Boolean recommend) {
if (!recommend) return secondLine;
if (score > 89) {
return "Safe to accept. " + secondLine;
} else if (score > 74) {
return "Not fully safe to accept. " + secondLine;
} else if (score > 30) {
return "Not safe to accept. " + secondLine;
} else {
return "Do not accept! " + secondLine;
}
public @StringRes int getSecondLine(Boolean recommend) {
// if (!recommend)
return secondLine;
// if (score > 89) {
// return "Safe to accept. " + secondLine;
// } else if (score > 74) {
// return "Not fully safe to accept. " + secondLine;
// } else if (score > 30) {
// return "Not safe to accept. " + secondLine;
// } else {
// return "Do not accept! " + secondLine;
// }
}
public void setSecondLine(String value) {
public void setSecondLine(@StringRes int value) {
secondLine = value;
}
@ -55,8 +59,8 @@ public class BalanceValidator {
}
public void check(TangemContext ctx, Boolean attest) {
firstLine = "Verification failed";
secondLine = "";
firstLine = R.string.balance_validator_first_line_verification_failed;
secondLine = R.string.empty_string;
TangemCard card = ctx.getCard();
CoinEngine engine = CoinEngineFactory.INSTANCE.create(ctx);
@ -66,8 +70,8 @@ public class BalanceValidator {
if( hasPending )
{
firstLine = "Pending transaction...";
secondLine = "Swipe down to refresh";
firstLine = R.string.balance_validator_first_line_pending_transaction;
secondLine = R.string.balance_validator_second_line_swipe_to_refresh;
return;
}
@ -76,38 +80,38 @@ public class BalanceValidator {
if (!card.isWalletPublicKeyValid()) {
score = 0;
firstLine = "Verification failed";
secondLine = "Wallet verification failed. Tap again.";
firstLine = R.string.balance_validator_first_line_verification_failed;
secondLine = R.string.balance_validator_second_line_verification_failed;
return;
}
if (card.isOnlineVerified() != null && !card.isOnlineVerified()) {
score = 0;
firstLine = "Not genuine banknote";
secondLine = "Tangem Attestation service says the banknote is not genuine.";
firstLine = R.string.balance_validator_first_line_not_genuine;
secondLine = R.string.balance_validator_second_line_failed_attestation;
return;
}
if (card.isCodeConfirmed() != null && !card.isCodeConfirmed()) {
score = 0;
firstLine = "Not genuine banknote";
secondLine = "Firmware binary code verification failed";
firstLine = R.string.balance_validator_first_line_not_genuine;
secondLine = R.string.balance_validator_second_line_failed_binary_code_verification;
return;
}
if (card.PIN2 == TangemCard.PIN2_Mode.CustomPIN2) {
score = 0;
firstLine = "Locked with PIN2";
secondLine = "Ask the holder to disable PIN2 before accepting";
firstLine = R.string.balance_validator_first_line_locked_pin2;
secondLine = R.string.balance_validator_second_line_disable_pin_2;
return;
}
// rule 2.b
if (card.isOnlineVerified()) {
secondLine += "Verified note identity. ";
secondLine += R.string.balance_validator_first_line_verify_identity;
} else {
score = 80;
secondLine += "Card identity was not verified. Cannot reach Tangem attestation service. ";
secondLine += R.string.balance_validator_second_line_identity_not_verified;
}
}
}

View file

@ -234,8 +234,8 @@ public class BtcCashEngine extends CoinEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
@ -249,18 +249,18 @@ public class BtcCashEngine extends CoinEngine {
if (coinData.getBalanceUnconfirmed() != 0) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
@ -275,8 +275,8 @@ public class BtcCashEngine extends CoinEngine {
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
// if(card.getFailedBalanceRequestCounter()!=0) {

View file

@ -226,30 +226,30 @@ public class BinanceEngine extends CoinEngine {
if(coinData.isError404()) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("No account or network error");
balanceValidator.setSecondLine("To create account send funds to this address");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_no_account);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_create_account);
} else {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
}
if (coinData.isBalanceReceived()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalance().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalance().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
return true;

View file

@ -257,8 +257,8 @@ public class BtcEngine extends CoinEngine {
try {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
@ -272,18 +272,18 @@ public class BtcEngine extends CoinEngine {
if (coinData.getBalanceUnconfirmed() != 0 || coinData.isHasUnconfirmed()) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
@ -298,8 +298,8 @@ public class BtcEngine extends CoinEngine {
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
// if(card.getFailedBalanceRequestCounter()!=0) {
@ -641,11 +641,7 @@ public class BtcEngine extends CoinEngine {
Log.e(TAG, "FAIL BLOCKCYPHER_ADDRESS Exception");
}
if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
checkPending(blockchainRequestsCallbacks);
} else {
blockchainRequestsCallbacks.onProgress();
}
checkPending(blockchainRequestsCallbacks);
}
public void onSuccess(String method, BlockcypherFee blockcypherFee) {

View file

@ -192,7 +192,7 @@ public class CardanoEngine extends CoinEngine {
@Override
public boolean checkNewTransactionAmount(Amount amount) {
if( BuildConfig.FLAVOR==Constant.FLAVOR_TANGEM_CARDANO ) {
if (BuildConfig.FLAVOR == Constant.FLAVOR_TANGEM_CARDANO) {
return true;
}
if (coinData == null) return false;
@ -236,25 +236,25 @@ public class CardanoEngine extends CoinEngine {
try {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
if (coinData.isBalanceReceived()) {// && coinData.isBalanceEqual()) { TODO:check
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
return true;
@ -586,7 +586,8 @@ public class CardanoEngine extends CoinEngine {
Amount feeDummy = new Amount(new BigDecimal(0.2).setScale(getDecimals(), RoundingMode.DOWN), getFeeCurrency());
try {
OnNeedSendTransaction onNeedSendTransactionBackup = onNeedSendTransaction;
onNeedSendTransaction =(tx)->{}; // empty function to bypass exception
onNeedSendTransaction = (tx) -> {
}; // empty function to bypass exception
SignTask.TransactionToSign ttsDummy = constructTransaction(amount, feeDummy, true, targetAddress);
byte[] txForSendDummy = ttsDummy.onSignCompleted(new byte[64]);
@ -617,7 +618,7 @@ public class CardanoEngine extends CoinEngine {
}
coinData.setBalanceReceived(true);
coinData.setBalance(adaliteResponse.getRight().getCaBalance().getGetCoin());
coinData.setValidationNodeDescription(ServerApiAdalite.lastNode);
coinData.setValidationNodeDescription(serverApiAdalite.getCurrentURL());
//check pending
if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) {
@ -632,8 +633,8 @@ public class CardanoEngine extends CoinEngine {
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "FAIL ADALITE_ADDRESS Exception");
e.printStackTrace();
}
if (serverApiAdalite.isRequestsSequenceCompleted()) {
@ -658,6 +659,7 @@ public class CardanoEngine extends CoinEngine {
}
} catch (Exception e) {
Log.e(TAG, "FAIL ADALITE_UNSPENT_OUTPUTS Exception");
e.printStackTrace();
}
@ -665,7 +667,6 @@ public class CardanoEngine extends CoinEngine {
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
} else {
blockchainRequestsCallbacks.onProgress();
Log.e(TAG, "FAIL ADALITE_UNSPENT_OUTPUTS Exception");
}
}
@ -678,9 +679,13 @@ public class CardanoEngine extends CoinEngine {
@Override
public void onFail(String method, String message) {
Log.i(TAG, "onFail: " + method + " " + message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
if (serverApiAdalite.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);
} else {
blockchainRequestsCallbacks.onProgress();
}
}
};
@ -752,10 +757,10 @@ public class CardanoEngine extends CoinEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiAdalite.isRequestsSequenceCompleted()) {
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
}
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
}
};
serverApiAdalite.setResponseListener(responseListener);
@ -769,7 +774,9 @@ public class CardanoEngine extends CoinEngine {
}
@Override
public int pendingTransactionTimeoutInSeconds() { return 60; }
public int pendingTransactionTimeoutInSeconds() {
return 60;
}
private String CalculateTxHash(String tx) throws CborException {
Array txArray = (Array) CborDecoder.decode(BTCUtils.fromHex(tx)).get(0);

View file

@ -226,8 +226,8 @@ public class DucatusEngine extends BtcEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
@ -241,18 +241,18 @@ public class DucatusEngine extends BtcEngine {
if (coinData.getBalanceUnconfirmed() != 0) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived()) {// && coinData.isBalanceEqual()) { TODO:check
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
@ -267,8 +267,8 @@ public class DucatusEngine extends BtcEngine {
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
// if(card.getFailedBalanceRequestCounter()!=0) {
@ -562,7 +562,7 @@ public class DucatusEngine extends BtcEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiInsight.isRequestsSequenceCompleted()) {
if (!serverApiInsight.isRequestsSequenceCompleted()) { //TODO: rework request sequence
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
}

View file

@ -277,25 +277,25 @@ public class EosEngine extends CoinEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (getBalance() == null) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
if (coinData.isBalanceReceived()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (getBalance().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && getBalance().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Restore internet connection to obtain trusted balance from blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
}
return true;

View file

@ -311,32 +311,32 @@ public class EthEngine extends CoinEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (getBalance() == null) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
if (!coinData.getUnconfirmedTXCount().equals(coinData.getConfirmedTXCount())) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (getBalance().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && getBalance().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Restore internet connection to obtain trusted balance from blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
}
return true;
@ -509,9 +509,12 @@ public class EthEngine extends CoinEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiInfura.isRequestsSequenceCompleted()) {
ctx.setError(message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
if (serverApiInfura.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);
} else {
blockchainRequestsCallbacks.onProgress();
}
}
};

View file

@ -235,8 +235,8 @@ public class LtcEngine extends BtcEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
@ -250,18 +250,18 @@ public class LtcEngine extends BtcEngine {
if (coinData.getBalanceUnconfirmed() != 0) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
@ -276,8 +276,8 @@ public class LtcEngine extends BtcEngine {
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
}
// if(card.getFailedBalanceRequestCounter()!=0) {
@ -418,8 +418,8 @@ public class LtcEngine extends BtcEngine {
change = change - fees;
}
final long amountFinal=amount;
final long changeFinal=change;
final long amountFinal = amount;
final long changeFinal = change;
if (amount + fees > fullAmount) {
throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
@ -427,7 +427,7 @@ public class LtcEngine extends BtcEngine {
final byte[][] txForSign = new byte[unspentOutputs.size()][];
final byte[][] bodyDoubleHash = new byte[unspentOutputs.size()][];
final byte[][] bodyHash= new byte[unspentOutputs.size()][];
final byte[][] bodyHash = new byte[unspentOutputs.size()][];
for (int i = 0; i < unspentOutputs.size(); ++i) {
txForSign[i] = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change);
@ -439,13 +439,14 @@ public class LtcEngine extends BtcEngine {
@Override
public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw;
return signingMethod == TangemCard.SigningMethod.Sign_Hash || signingMethod == TangemCard.SigningMethod.Sign_Raw;
}
@Override
public byte[][] getHashesToSign() throws Exception {
byte[][] dataForSign=new byte[unspentOutputs.size()][];
if (txForSign.length > 10) throw new Exception("To much hashes in one transaction!");
byte[][] dataForSign = new byte[unspentOutputs.size()][];
if (txForSign.length > 10)
throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < unspentOutputs.size(); ++i) {
dataForSign[i] = bodyDoubleHash[i];
}
@ -484,7 +485,7 @@ public class LtcEngine extends BtcEngine {
unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey);
}
byte[] txForSend=BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal);
byte[] txForSend = BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal);
notifyOnNeedSendTransaction(txForSend);
return txForSend;
}
@ -641,15 +642,13 @@ public class LtcEngine extends BtcEngine {
}
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "FAIL BLOCKCYPHER_ADDRESS Exception");
e.printStackTrace();
ctx.setError(e.getMessage());
blockchainRequestsCallbacks.onComplete(false);
}
if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
checkPending(blockchainRequestsCallbacks);
} else {
blockchainRequestsCallbacks.onProgress();
}
checkPending(blockchainRequestsCallbacks);
}
public void onSuccess(String method, BlockcypherFee blockcypherFee) {

View file

@ -1,6 +1,7 @@
package com.tangem.wallet.matic;
import android.net.Uri;
import android.util.Log;
import com.tangem.data.Blockchain;
import com.tangem.data.network.ServerApiMatic;
@ -168,9 +169,12 @@ public class MaticTokenEngine extends TokenEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiMatic.isRequestsSequenceCompleted()) {
ctx.setError(message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
if (serverApiMatic.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);
} else {
blockchainRequestsCallbacks.onProgress();
}
}
};

View file

@ -15,6 +15,7 @@ import com.tangem.wallet.BalanceValidator;
import com.tangem.wallet.CoinData;
import com.tangem.wallet.CoinEngine;
import com.tangem.wallet.Keccak256;
import com.tangem.wallet.R;
import com.tangem.wallet.TangemContext;
import com.tangem.wallet.eth.EthData;
import com.tangem.wallet.token.TokenData;
@ -210,20 +211,20 @@ public class NftTokenEngine extends CoinEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (coinData.getBalanceInInternalUnits() == null) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("No connection");
balanceValidator.setSecondLine("Authenticity cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_no_connection);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_authenticity_not_verified);
return false;
}
if (coinData.isBalanceReceived()) {
if (isBalanceNotZero()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified in blockchain");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_in_blockchain);
balanceValidator.setSecondLine(R.string.empty_string);
} else {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Authenticity was not verified");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_authenticity);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
@ -277,10 +278,9 @@ public class NftTokenEngine extends CoinEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiInfura.isRequestsSequenceCompleted()) {
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
}
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
}
};
serverApiInfura.setResponseListener(responseListener);

View file

@ -111,9 +111,12 @@ public class RskEngine extends EthEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiRootstock.isRequestsSequenceCompleted()) {
ctx.setError(message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
if (serverApiRootstock.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);
} else {
blockchainRequestsCallbacks.onProgress();
}
}
};

View file

@ -140,9 +140,12 @@ public class RskTokenEngine extends TokenEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiRootstock.isRequestsSequenceCompleted()) {
ctx.setError(message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
if (serverApiRootstock.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);
} else {
blockchainRequestsCallbacks.onProgress();
}
}
};

View file

@ -382,32 +382,32 @@ public class TokenEngine extends CoinEngine {
public boolean validateBalance(BalanceValidator balanceValidator) {
if (getBalance() == null) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
if (!coinData.getUnconfirmedTXCount().equals(coinData.getConfirmedTXCount())) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (getBalance().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && getBalance().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Restore internet connection to obtain trusted balance from blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
}
return true;
@ -708,9 +708,12 @@ public class TokenEngine extends CoinEngine {
@Override
public void onFail(String method, String message) {
if (!serverApiInfura.isRequestsSequenceCompleted()) {
ctx.setError(message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
if (serverApiInfura.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);
} else {
blockchainRequestsCallbacks.onProgress();
}
}
};

View file

@ -200,12 +200,12 @@ public class XlmEngine extends CoinEngine {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
if (coinData.isError404()) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("No account or network error");
balanceValidator.setSecondLine("To create account send 1+ XLM to this address");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_no_account);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_create_account_instruction);
} else {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
}
@ -227,11 +227,11 @@ public class XlmEngine extends CoinEngine {
if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalance().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
@ -246,8 +246,8 @@ public class XlmEngine extends CoinEngine {
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalance().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
// if(card.getFailedBalanceRequestCounter()!=0) {

View file

@ -198,39 +198,39 @@ public class XrpEngine extends CoinEngine {
try {
if (coinData.isAccountNotFound()) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Account not found");
balanceValidator.setSecondLine("Load 20+ XRP to create account");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_account_not_found);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_create_account_xrp);
return false;
}
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_unknown_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
return false;
}
if (coinData.hasUnconfirmed()) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_transaction_in_progress);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_wait_for_confirmation);
return false;
}
if (coinData.isBalanceReceived()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}
}
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
}
return true;
@ -497,7 +497,7 @@ public class XrpEngine extends CoinEngine {
@Override
public void onFail(String method, String message) {
Log.i(TAG, "onFail: " + method + " " + message);
Log.e(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
if (serverApiRipple.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);

View file

@ -189,6 +189,35 @@
</LinearLayout>
<LinearLayout
android:id="@+id/llLinkedCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="@dimen/verify_card_left_col_width"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:fontFamily="@font/maax"
android:text="The card is linked"
android:textSize="@dimen/text_size_1_small" />
<TextView
android:id="@+id/tvIsLinked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/maax"
android:textColor="@color/primary_dark"
android:textSize="@dimen/text_size_1_small"
android:textStyle="normal|bold"
tools:text="To this phone" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -91,7 +91,8 @@
app:destination="@id/confirmTransactionFragment" />
<action
android:id="@+id/action_prepareTransactionFragment_to_qrScanFragment"
app:destination="@id/qrScanFragment" />
app:destination="@id/qrScanFragment"
app:enterAnim="@anim/nav_default_enter_anim" />
</fragment>
<fragment
android:id="@+id/signTransactionFragment"
@ -113,7 +114,7 @@
</fragment>
<fragment
android:id="@+id/qrScanFragment"
android:name="com.tangem.ui.fragment.QrScanFragment"
android:name="com.tangem.ui.fragment.qr.QrScanFragment"
android:label="QrScanFragment" >
</fragment>
<fragment
@ -176,7 +177,8 @@
android:label="PrepareCryptonitOtherApiWithdrawalFragment" >
<action
android:id="@+id/action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment"
app:destination="@id/qrScanFragment" />
app:destination="@id/qrScanFragment"
app:enterAnim="@anim/nav_default_enter_anim"/>
</fragment>
<fragment
android:id="@+id/prepareCryptonitWithdrawalFragment"

View file

@ -0,0 +1,299 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="general_cancel">Annuler</string>
<string name="general_yes">Oui</string>
<string name="general_no">Non</string>
<string name="general_error">Erreur!</string>
<string name="general_error_no_connection">Pas de connexion avec les nœuds de la chaîne de blocage</string>
<string name="general_continue">Continuer</string>
<string name="error_empty_pin">Le code PIN est vide</string>
<string name="general_blockchain">Blockchain</string>
<string name="general_notification_scan_again">Réessayez de numériser</string>
<string name="general_error_cannot_erase_wallet_with_non_zero_balance">Impossible d\'effacer le porte-monnaie avec un solde différent de zéro</string>
<string name="general_send_transaction">Envoyer le paiement</string>
<string name="general_from_banknote">Depuis la carte</string>
<string name="general_on_banknote">sur la carte</string>
<string name="general_balance">avec solde</string>
<string name="general_send_to_wallet">Envoyer au porte-monnaie</string>
<string name="general_amount">Montant</string>
<string name="general_btc">BTC</string>
<string name="general_tangem" translatable="false">Tangem AG</string>
<string name="general_notification_scan_again_to_verify">Scanner à nouveau pour vérifier la carte</string>
<string name="general_status_reading">READING…</string>
<string name="general_wallet_empty">Le porte-monnaie est vide</string>
<string name="general_toast_no_permission">L\'utilisateur n\'a pas obtenu l\'autorisation d\'utiliser la caméra</string>
<!-- Paramètres -->
<string name="settings_title">Paramètres</string>
<string name="settings_category_сommon">Commun</string>
<string name="settings_option_manual_editing_fee">Frais de modification manuelle</string>
<string name="settings_option_encryption_modes">Modes de cryptage</string>
<string-array name="settings_encryption_modes_entries">
<item>Aucun</item>
<item>Rapide</item>
<item>Fort</item>
</string-array>
<string-array name="settings_encryption_mode_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string name="settings_option_select_nodes">Sélectionner des nœuds</string>
<!-- dialogues -->
<string name="dialog_device_is_rooted">Votre appareil Android est rooté. La sécurité en danger!</string>
<string name="dialog_btn_got_it">Je l\'ai</string>
<string name="dialog_warning">Avertissement</string>
<string name="dialog_security_delay">Délai de sécurité</string>
<string name="dialog_the_nfc_adapter_length_apdu">Oups .. Il semble que votre smartphone ne supporte pas de tels paquets NFC.</string>
<string name="dialog_the_nfc_adapter_length_apdu_advice">Essayez d\'envoyer une quantité inférieure ou utilisez un smartphone avec prise en charge complète de NFC.</string>
<string name="dialog_title_money_is_at_risk">Votre argent est en jeu!</string>
<string name="dialog_this_banknote_has_enforced_security_delay">Cette carte a imposé un délai de sécurité</string>
<string name="dialog_hold_banknote">Veuillez tenir la carte fermement \ n jusqu\'à ce que l\'opération soit terminéev…</string>
<string name="dialog_you_may_be_required_to_repeat">Vous devrez peut-être répéter cette opération plusieurs fois en fonction des performances NFC de votre smartphone. \ n Ceci est fait pour la sécurité de vos fonds.</string>
<string name="alert_unknown_blockchain">Cette carte n\'est pas supportée</string>
<!-- menu main -->
<string name="main_menu_manage_pin_1">Gérer le code PIN de l\'utilisateur…</string>
<string name="main_menu_manage_pin_2">Gérer le code PIN de l\'utilisateur…</string>
<string name="main_menu_debug_send_logs">Envoyer les journaux…</string>
<string name="main_menu_about">À propos de…</string>
<!-- porte-monnaie chargé par menu -->
<string name="menu_loaded_wallet_set_pin_1">Définir PIN1</string>
<string name="menu_loaded_wallet_set_pin_2">Définir le code PIN2</string>
<string name="menu_loaded_wallet_reset_pin_1">>Réinitialiser PIN1 par défaut</string>
<string name="menu_loaded_wallet_reset_pin_2">Réinitialiser le code PIN2 par défaut</string>
<string name="menu_loaded_wallet_reset_pins">Réinitialiser les codes confidentiels par défaut</string>
<string name="menu_loaded_wallet_erase_wallet">Effacer le porte-monnaie</string>
<!-- Écran de démarrage -->
<string name="splash_version_name_debug">ALPHA v.%1$s \n dev \n génération %2$s</string>
<string name="splash_version_name_release">v.%1$s</string>
<string name="splash_cardano">Cardano</string>
<!-- Main -->
<string name="main_screen_tap_card">Appuyez sur la carte Tangem avec votre smartphone</string>
<string name="main_screen_scan_banknote">Scanner une carte avec votre \n %1$s \n comme indiqué ci-dessus</string>
<string name="main_screen_phone">phone</string>
<string name="main_screen_erased_wallet">Le porte-monnaie a été effacé</string>
<string name="main_screen_not_personalized">Non personnalisé</string>
<string name="main_screen_new_version_toast">Il existe une nouvelle version de l\'application: %1$s</string>
<string name="main_screen_btn_update">Mettre à jour</string>
<!-- LoadedWallet -->
<string name="loaded_wallet_no_compatible_wallet">Aucun porte-monnaie compatible installé</string>
<string name="loaded_wallet_message_wait">Veuillez patienter pendant que la transaction précédente est confirmée dans la blockchain</string>
<string name="loaded_wallet_message_refresh">Impossible d\'obtenir toutes les entrées. Balayez vers le bas pour actualiser.</string>
<string name="loaded_wallet_warning_no_signature">La carte n\'a plus de signature!</string>
<string name="loaded_wallet_warning_dont_forget_pin">Si vous oubliez votre nouveau code PIN, vous perdrez votre argent pour toujours!</string>
<string name="loaded_wallet_warning_default_pin">Si vous utilisez un code PIN par défaut, une personne peut voler votre argent!</string>
<string name="loaded_wallet_error_obtaining_blockchain_data">>Impossible d\'obtenir des données de la blockchain</string>
<string name="loaded_wallet_error_blockchain_connection_refused">Impossible d\'obtenir des données de la blockchain (connexion refusée)</string>
<string name="loaded_wallet_error_blockchain_empty_answer">Impossible d\'obtenir les données de la blockchain (réponse vide reçue)</string>
<string name="loaded_wallet_error_blockchain_communication_error">Impossible d\'obtenir les données de la blockchain (erreur de communication)</string>
<string name="loaded_wallet_chooser_share">Partager l\'adresse du porte-monnaie avec:</string>
<string name="loaded_wallet_toast_copied">copié dans le presse-papiers</string>
<string name="loaded_wallet_btn_extract">Retirer</string>
<string name="loaded_wallet_btn_load">Charger</string>
<string name="loaded_wallet_btn_explore">Explorer</string>
<string name="loaded_wallet_btn_copy">Copier</string>
<string name="loaded_wallet_btn_details">Détails</string>
<string name="loaded_wallet_btn_new_scan">Scanner</string>
<string name="loaded_wallet_verifying_in_blockchain">Vérification dans la blockchain…</string>
<string name="loaded_wallet_warning_card_signed_transactions">Avertissement: cette carte a déjà été complétée et des transactions signées dans le passé.Envisagez le retrait immédiat de tous les fonds si vous avez reçu cette carte dune source non fiable.</string>
<string name="loaded_wallet_status_verifying">VERIFYING…</string>
<string name="loaded_wallet_load_via_cryptonit">via CRYPTONIT</string>
<string name="loaded_wallet_load_via_cryptonit2">via CRYPTONIT2</string>
<string name="loaded_wallet_load_via_kraken">via KRAKEN</string>
<string name="loaded_wallet_load_via_app">Une autre application de porte-monnaie</string>
<string name="loaded_wallet_load_via_share_address">Adresse de partage</string>
<string name="loaded_wallet_load_via_qr">Afficher le code QR</string>
<string name="loaded_wallet_dialog_show_qr">LOAD</string>
<!-- LoadedWallet Blockchain Engines -->
<string name="balance_validator_first_line_verified_balance">Solde vérifié</string>
<string name="balance_validator_first_line_unknown_balance">Solde inconnu</string>
<string name="balance_validator_first_line_transaction_in_progress">Transaction en cours</string>
<string name="balance_validator_first_line_verified_offline">Solde hors ligne vérifié</string>
<string name="balance_validator_first_line_empty_wallet">Portefeuille vide</string>
<string name="balance_validator_first_line_verification_failed">La vérification a échoué</string>
<string name="balance_validator_first_line_pending_transaction">Transaction en attente…</string>
<string name="balance_validator_first_line_not_genuine">Billet non authentique</string>
<string name="balance_validator_first_line_locked_pin2">Verrouillé avec code PIN2</string>
<string name="balance_validator_first_line_verify_identity">Identité de note vérifiée.</string>
<string name="balance_validator_first_line_no_account">Aucun compte ni erreur réseau</string>
<string name="balance_validator_first_line_account_not_found">Compte introuvable</string>
<string name="balance_validator_first_line_no_connection">Pas de connexion</string>
<string name="balance_validator_first_line_verified_in_blockchain">Vérifié dans la blockchain</string>
<string name="balance_validator_first_line_authenticity">L\'authenticité n\'a pas été vérifiée</string>
<string name="balance_validator_second_line_confirmed_in_blockchain">Solde confirmé dans la blockchain</string>
<string name="balance_validator_second_line_unverified_balance">Solde ne peut pas être vérifié. Balayez vers le bas pour actualiser..</string>
<string name="balance_validator_second_line_wait_for_confirmation">Attendez la confirmation dans la chaîne de blocs</string>
<string name="balance_validator_second_line_internet_to_verify_online">Restaurez la connexion Internet pour obtenir un solde sécurisé de la chaîne de blocs</string>
<string name="balance_validator_second_line_internet_to_get_balance">Impossible d\'obtenir le
solde de la blockchain. Restaurez la connexion Internet pour être plus confiant.</string>
<string name="balance_validator_second_line_swipe_to_refresh">Glissez vers le bas pour actualiser</string>
<string name="balance_validator_second_line_verification_failed">La vérification du portefeuille a échoué. Touchez à nouveau.</string>
<string name="balance_validator_second_line_failed_attestation">Le service d\'attestation Tangem indique que le billet n\'est pas authentique.</string>
<string name="balance_validator_second_line_failed_binary_code_verification">La vérification du code binaire du micrologiciel a échoué</string>
<string name="balance_validator_second_line_disable_pin_2">Demandez au titulaire de désactiver le code PIN2 avant d\'accepter</string>
<string name="balance_validator_second_line_identity_not_verified">\'identité de la carte n\'a pas été vérifiée. Impossible d\'atteindre le service d\'attestation Tangem.</string>
<string name="balance_validator_second_line_create_account">Pour créer un compte, envoyez des fonds à cette adresse</string>
<string name="balance_validator_second_line_create_account_instruction">Pour créer un compte, envoyez 1+ XLM à cette adresse</string>
<string name="balance_validator_second_line_create_account_xrp">Chargez plus de 20 XRP pour créer un compte</string>
<string name="balance_validator_second_line_authenticity_not_verified">L\'authenticité ne peut pas être vérifiée. Balayez vers le bas pour actualiser.</string>
    
<!-- CreateNewWallet, Purge, SignTransaction -->
<string name="now_touch_the_banknote_with_id"> Maintenant, touchez la carte avec l\'ID </string>
<string name="to_create_the_wallet">pour créer le porte-monnaie </string>
<string name="to_erase_the_wallet">pour effacer le porte-monnaie</string>
<string name="to_sign_the_transaction">pour signer le paiement</string>
<string name="to_change_pin_codes">pour changer les codes PIN / PIN2</string>
<string name="nfc_purge_warning">En tapotant sur la carte, vous retirerez définitivement le porte-monnaie de la blockchain. Assurez-vous qu\'il n\'y aura pas d\autres transactions entrantes</string>
<string name="nfc_error_cannot_erase_wallet">Impossible d\'effacer le porte-monnaie. Veillez à entrer le code PIN2 correct!</string>
<string name="nfc_error_cannot_create_wallet">Impossible de créer un porte-monnaie. Veillez à entrer le code PIN2 correct!</string>
<!-- PinRequest -->
<string name="pin_request_enter_new_pin_or_use_fingerprint_scanner">Entrez un nouveau code PIN ou utilisez un lecteur d\'empreintes digitales</string>
<string name="pin_request_enter_new_pin">Entrez le nouveau code PIN</string>
<string name="pin_request_enter_pin_or_use_fingerprint_scanner">Entrez un code PIN ou utilisez un lecteur d\'empreintes digitales</string>
<string name="pin_request_enter_pin_2_or_use_fingerprint_scanner">Entrez le code PIN2 ou utilisez un lecteur d\'empreintes digitales</string>
<string name="pin_request_confirm_new_pin">Confirmer le nouveau code confidentiel</string>
<string name="pin_request_confirm_new_pin_2">Confirmer le nouveau code PIN2</string>
<string name="pin_request_enter_pin">Entrez le code confidentiel</string>
<string name="pin_request_prompt_enter_pin_2">Entrez le code PIN2</string>
<string name="pin_request_prompt_new_pin_2_or_fingerprint">Entrez un nouveau code PIN2 ou utilisez un lecteur d\'empreintes digitales</string>
<string name="pin_request_new_pin_2">Entrez le nouveau code PIN2</string>
<string name="pin_request_error_pin_confirmation_failed">Veuillez saisir le code PIN pour confirmation!</string>
<!-- SendTransaction -->
<string name="send_transaction_notification_wait">Veuillez patienter pendant l\'envoi du paiement…</string>
<string name="send_transaction_success">La transaction a été signée et envoyée avec succès au noeud de la blockchain. La balance de porte-monnaie sera mise à jour dans un moment</string>
<string name="send_transaction_error_failed_to_send">Réessayez. Échec d\'envoi de la transaction (%s)</string>
<string name="send_transaction_error_cannot_sign">Impossible de signer la transaction. Veillez à entrer le code PIN2 correct!</string>
<string name="send_transaction_error_wrong_amount">Le montant et les frais sont supérieurs au solde total du porte-monnaie!</string>
<!-- PrepareTransaction -->
<string name="prepare_transaction_hint_enter_address">entrez l\'adresse du porte-monnaie</string>
<string name="prepare_transaction_hint_enter_amount">entrer le montant</string>
<string name="prepare_transaction_btn_verify">Vérifiez</string>
<string name="prepare_transaction_error_not_enough_funds">Pas assez de fonds</string>
<string name="prepare_transaction_error_unknown_amount_format">Format de montant inconnu</string>
<string name="prepare_transaction_error_incorrect_destination">Adresse du porte-monnaie de destination incorrecte</string>
<string name="prepare_transaction_error_same_address">L\'adresse du porte-monnaie de destination est identique à l\'adresse source</string>
<string name="prepare_transaction_error_amount_empty">Le montant est vide</string>
<!-- ConfirmTransaction -->
<string name="confirm_transaction_hint_target_address">adresse du porte-monnaie cible</string>
<string name="confirm_transaction_hint_fee_amount">montant des frais</string>
<string name="confirm_transaction_fee">Frais</string>
<string name="confirm_transaction_btn_send">Envoyer</string>
<string name="confirm_transaction_btn_fee_minimal">Minimal</string>
<string name="confirm_transaction_btn_fee_normal">Normal</string>
<string name="confirm_transaction_btn_fee_priority">Priorité</string>
<string name="confirm_transaction_btn_including_fee">Frais inclus</string>
<string name="confirm_transaction_btn_not_including_fee">Frais non compris</string>
<string name="confirm_transaction_including_fee">(frais inclus)</string>
<string name="confirm_transaction_not_including_fee">(sans frais)</string>
<string name="confirm_transaction_error_data_is_outdated">Les données obtenues sont obsolètes! Réessayez</string>
<string name="confirm_transaction_error_cannot_reach_node">Impossible d\'atteindre le nœud de la blockchain active en cours. Réessayez</string>
<string name="confirm_transaction_error_cannot_check_balance">Impossible de vérifier le solde! Pas de connexion avec les noeuds de la blockchain</string>
<string name="confirm_transaction_error_cannot_calculate_fee">Impossible de calculer les frais! Mauvaises données reçues du noeud</string>
<string name="confirm_transaction_error_incoming_transaction_unconfirmed">Veuillez attendre la confirmation de la transaction entrante</string>
<string name="confirm_transaction_error_not_enough_eth_for_fee">Pas assez de fonds ETH contre des taxes</string>
<string name="confirm_transaction_error_not_enough_rbtc_for_fee">Pas assez de fonds RBTC pour les frais</string>
<string name="confirm_transaction_error_pin_2_is_required">Un code PIN2 est requis pour signer le paiement</string>
<string name="confirm_transaction_error_service_unavailable">Service indisponible</string>
<string name="confirm_transaction_warning_risk_delaying">Vous courez un risque de retarder la transaction</string>
<!-- EmptyWallet -->
<string name="empty_wallet_not_created">Le porte-monnaie n\'a pas encore été créé</string>
<string name="empty_wallet_btn_create">Créer un porte-monnaie</string>
<!-- VerifyCard -->
<string name="details_title_card_id">ID de la carte</string>
<string name="details_signing_method">Méthode de signature</string>
<string name="details_is_card_reusable">Cette carte est</string>
<string name="details_signed_transactions">Transactions signées</string>
<string name="details_remaining_signatures">Signatures restantes</string>
<string name="details_time_of_last_signing">Heure de la dernière signature</string>
<string name="details_features">Fonctionnalités</string>
<string name="details_category_manufacturer">Fabricant</string>
<string name="details_category_wallet">Porte-monnaie</string>
<string name="details_card_identity">Identité de la carte</string>
<string name="details_attested">Attested</string>
<string name="details_not_confirmed">Non confirmé</string>
<string name="details_reusable">Réutilisable</string>
<string name="details_none">None</string>
<string name="details_last_one">Le dernier!</string>
<string name="details_unlimited">Unlimited</string>
<string name="details_one_off_banknote">Carte ponctuelle</string>
<string name="details_firmware">Micrologiciel</string>
<string name="details_registration_date">Date d\'enregistrement</string>
<string name="details_possession_proved">Possession prouvée</string>
<string name="details_possession_not_proved">Possession NON prouvée</string>
<string name="details_not_available">non disponible</string>
<string name="details_category_issuer">Émetteur</string>
<string name="details_private_key">Clé privée</string>
<string name="details_validation_node">Noeud de validation</string>
<string name="details_unspents">Unspents</string>
<string name="details_protected_by_default_pin_1">Cette carte est protégé par le code PIN1 par défaut</string>
<string name="details_protected_by_user_pin_1">Cette carte est protégé par le code PIN1 de lutilisateur</string>
<string name="details_protected_by_default_pin_2">Cette carte est protégé par le code PIN2 par défaut</string>
<string name="details_protected_by_user_pin_2">Cette carte est protégé par le code PIN2 de lutilisateur</string>
<string name="details_unlocked_banknote">Carte débloqué, uniquement à des fins de développement</string>
<string name="details_security_delay">Cette carte appliquera un délai de sécurité de %.0f secondes pour toutes lesopérations nécessitant un code PIN2</string>
<string name="details_both_pins_can_be_changed">Permet de changer les codes PIN1 et PIN2\n</string>
<string name="details_pin1_can_be_changed">Permet de changer PIN1\n</string>
<string name="details_pin2_can_be_changed">Permet de changer le code PIN2\n</string>
<string name="details_both_pins_fixed">PIN1 et PIN2 sont fixes\n</string>
<string name="details_required_cvc">Requiert CVC\n</string>
<string name="details_dynamic_ndef">NDEF dynamique pour iOS\n</string>
<string name="details_ndef">NDEF\n</string>
<string name="details_blockable">Bloquable\n</string>
<string name="details_atomic_commmands">Mode de commande atomique\n</string>
<string name="details_linking_card_supported">La liaison au terminal est prise en charge\n</string>
<string name="details_linked_card_title">La carte est liée</string>
<string name="details_linked_card_to_phone">à ce téléphone</string>
<!-- PinSave -->
<string name="pin_save_btn_save">Enregistrer</string>
<string name="pin_save_btn_delete">Supprimer</string>
<string name="pin_save_checkbox_protect_with_fingerprint">Protéger avec empreinte digitale</string>
<string name="pin_save_dialog_touch_fingerprint_scanner">Appuyez sur le lecteur d\'empreintes digitales pour confirmer</string>
<string name="pin_save_toast_lock_screen_not_enabled">L\'utilisateur n\'a pas activé l\'écran verrouillé</string>
<string name="pin_save_toast_no_permission_to_use_fingerprint">L\'utilisateur n\'a pas obtenu l\'autorisation d\'utiliser une empreinte digitale</string>
<string name="pin_save_toast_no_fingerprints_registered">L\'utilisateur n\'a enregistré aucune empreinte digitale</string>
<string name="pin_save_notification_failed">Échec de l\'enregistrement du code confidentiel</string>
<string name="pin_save_title_enter_pin_and_save">Entrez le code PIN et enregistrez-le</string>
<string name="pin_save_title_enter_pin2_and_save">Entrez le code PIN2 et enregistrez-le</string>
<!-- PrepareCryptonitWithdrawal -->
<string name="cryptonit_payment">Paiement CRYPTONIT</string>
<string name="cryptonit_from_account">À partir du compte CRYPTONIT:</string>
<string name="cryptonit_user_id">ID utilisateur:</string>
<string name="cryptonit_key">clé:</string>
<string name="cryptonit_secret">secret:</string>
<string name="cryptonit_nonce">nonce:</string>
<string name="cryptonit_username">nom d\'utilisateur:</string>
<string name="cryptonit_password">mot_de_passe:</string>
<string name="cryptonit_not_enough_account_data">Veuillez saisir les données du compte</string>
<string name="cryptonit_request_balance">Obtenir le solde</string>
<string name="cryptonit_request_withdrawal">Retrait</string>
<string name="withdrawal_fee">frais d\'opération</string>
<string name="withdrawal_successful">Retrait réussi!</string>
<!-- PrepareKrakenWithdrawal -->
<string name="kraken_payment">Paiement KRAKEN</string>
<string name="kraken_from_account">À partir du compte KRAKEN:</string>
<string name="kraken_key">clé:</string>
<string name="kraken_secret">secret:</string>
<string name="kraken_not_enough_account_data">"Veuillez saisir les données du compte</string>
<string name="kraken_request_balance">Obtenir le solde</string>
<string name="kraken_request_withdrawal">Retrait</string>
<string name="kraken_operation_canceled">Opération annulée!</string>
<string name="kraken_please_confirm_withdraw">Veuillez confirmer retirer</string>
</resources>

View file

@ -16,11 +16,13 @@
<string name="general_balance">with balance</string>
<string name="general_send_to_wallet">Send to wallet</string>
<string name="general_amount">Amount</string>
<string name="general_btc">BTC</string>
<string name="general_btc" translatable="false">BTC</string>
<string name="general_tangem" translatable="false">Tangem AG</string>
<string name="general_notification_scan_again_to_verify">Scan again to verify the card</string>
<string name="general_status_reading">READING…</string>
<string name="general_wallet_empty">The wallet is empty</string>
<string name="general_toast_no_permission">User hasn\'t granted permission to use camera</string>
<!-- Settings -->
<string name="settings_title">Settings</string>
@ -69,7 +71,7 @@
<string name="splash_version_name_debug">ALPHA v.%1$s \n dev \n build %2$s</string>
<string name="splash_version_name_release">v.%1$s</string>
<string name="splash_cardano">Cardano</string>
<string name="alert_unknown_blockchain">This card is not supported</string>
<!-- Main -->
<string name="main_screen_tap_card">Tap Tangem card with your smartphone</string>
@ -111,6 +113,39 @@
<string name="loaded_wallet_load_via_qr">Show QR-code</string>
<string name="loaded_wallet_dialog_show_qr">LOAD</string>
<!-- LoadedWallet Blockchain Engines -->
<string name="balance_validator_first_line_verified_balance">Verified balance</string>
<string name="balance_validator_first_line_unknown_balance">Unknown balance</string>
<string name="balance_validator_first_line_transaction_in_progress">Transaction in progress</string>
<string name="balance_validator_first_line_verified_offline">Verified offline balance</string>
<string name="balance_validator_first_line_empty_wallet">Empty wallet</string>
<string name="balance_validator_first_line_verification_failed">Verification failed</string>
<string name="balance_validator_first_line_pending_transaction">Pending transaction…</string>
<string name="balance_validator_first_line_not_genuine">Not genuine banknote</string>
<string name="balance_validator_first_line_locked_pin2">Locked with PIN2</string>
<string name="balance_validator_first_line_verify_identity">Verified note identity.</string>
<string name="balance_validator_first_line_no_account">No account or network error</string>
<string name="balance_validator_first_line_account_not_found">Account not found</string>
<string name="balance_validator_first_line_no_connection">No connection</string>
<string name="balance_validator_first_line_verified_in_blockchain">Verified in blockchain</string>
<string name="balance_validator_first_line_authenticity">Authenticity was not verified</string>
<string name="balance_validator_second_line_confirmed_in_blockchain">Balance confirmed in blockchain</string>
<string name="balance_validator_second_line_unverified_balance">Balance cannot be verified. Swipe down to refresh.</string>
<string name="balance_validator_second_line_wait_for_confirmation">Wait for confirmation in blockchain</string>
<string name="balance_validator_second_line_internet_to_verify_online">Restore internet connection to obtain trusted balance from blockchain</string>
<string name="balance_validator_second_line_internet_to_get_balance">Can\'t obtain balance from blockchain. Restore internet connection to be more confident.</string>
<string name="balance_validator_second_line_swipe_to_refresh">Swipe down to refresh</string>
<string name="balance_validator_second_line_verification_failed">Wallet verification failed. Tap again.</string>
<string name="balance_validator_second_line_failed_attestation">Tangem Attestation service says the banknote is not genuine.</string>
<string name="balance_validator_second_line_failed_binary_code_verification">Firmware binary code verification failed</string>
<string name="balance_validator_second_line_disable_pin_2">Ask the holder to disable PIN2 before accepting</string>
<string name="balance_validator_second_line_identity_not_verified">Card identity was not verified. Cannot reach Tangem attestation service.</string>
<string name="balance_validator_second_line_create_account">To create account send funds to this address</string>
<string name="balance_validator_second_line_create_account_instruction">To create account send 1+ XLM to this address</string>
<string name="balance_validator_second_line_create_account_xrp">Load 20+ XRP to create account</string>
<string name="balance_validator_second_line_authenticity_not_verified">Authenticity cannot be verified. Swipe down to refresh.</string>
<!-- CreateNewWallet, Purge, SignTransaction -->
<string name="now_touch_the_banknote_with_id">Now touch the banknote with ID</string>
<string name="to_create_the_wallet">to create the wallet</string>
@ -134,7 +169,6 @@
<string name="pin_request_new_pin_2">Enter new PIN2</string>
<string name="pin_request_error_pin_confirmation_failed">Please enter the PIN for confirmation!</string>
<!-- SendTransaction-->
<string name="send_transaction_notification_wait">Please wait while the payment is sent…</string>
<string name="send_transaction_success">Transaction has been successfully signed and sent to blockchain node. Wallet balance will be updated in a while</string>
@ -212,6 +246,18 @@
<string name="details_protected_by_user_pin_2">This banknote is protected by user\'s PIN2 code</string>
<string name="details_unlocked_banknote">Unlocked banknote, only for development use</string>
<string name="details_security_delay">This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code</string>
<string name="details_both_pins_can_be_changed">Allows to change PIN1 and PIN2\n</string>
<string name="details_pin1_can_be_changed">Allows to change PIN1\n</string>
<string name="details_pin2_can_be_changed">Allows to change PIN2\n</string>
<string name="details_both_pins_fixed">Fixed PIN1 and PIN2\n</string>
<string name="details_required_cvc">Requires CVC\n</string>
<string name="details_dynamic_ndef">Dynamic NDEF for iOS\n</string>
<string name="details_ndef">NDEF\n</string>
<string name="details_blockable">Blockable\n</string>
<string name="details_atomic_commmands">Atomic command mode\n</string>
<string name="details_linking_card_supported">Linking to the terminal is supported\n</string>
<string name="details_linked_card_title">The card is linked</string>
<string name="details_linked_card_to_phone">To this phone</string>
<!-- PinSave -->
<string name="pin_save_btn_save">Save</string>

View file

@ -1,31 +1,32 @@
<resources>
<string name="in_msg_prefix">&lt;&#45;&#45;&#160;</string>
<string name="out_msg_prefix">&#45;&#45;&gt;&#160;</string>
<string name="okay_msg_prefix">**&#160;</string>
<string name="err_msg_prefix">!!&#160;</string>
<string name="action_msg_prefix">--&#160;</string>
<string name="no_data_string">-- -- --</string>
<string name="no_data_string_1">---- ---- ---- ----</string>
<string name="pin_back">&lt;</string>
<string name="empty">--</string>
<string name="_1">1</string>
<string name="_2">2</string>
<string name="_3">3</string>
<string name="_4">4</string>
<string name="_5">5</string>
<string name="_6">6</string>
<string name="_7">7</string>
<string name="_8">8</string>
<string name="_9">9</string>
<string name="_0">0</string>
<string name="in_msg_prefix" translatable="false">&lt;&#45;&#45;&#160;</string>
<string name="out_msg_prefix" translatable="false">&#45;&#45;&gt;&#160;</string>
<string name="okay_msg_prefix" translatable="false">**&#160;</string>
<string name="err_msg_prefix" translatable="false">!!&#160;</string>
<string name="action_msg_prefix" translatable="false">--&#160;</string>
<string name="no_data_string" translatable="false">-- -- --</string>
<string name="no_data_string_1" translatable="false">---- ---- ---- ----</string>
<string name="pin_back" translatable="false">&lt;</string>
<string name="empty" translatable="false">--</string>
<string name="empty_string" translatable="false"></string>
<string name="_1" translatable="false">1</string>
<string name="_2" translatable="false">2</string>
<string name="_3" translatable="false">3</string>
<string name="_4" translatable="false">4</string>
<string name="_5" translatable="false">5</string>
<string name="_6" translatable="false">6</string>
<string name="_7" translatable="false">7</string>
<string name="_8" translatable="false">8</string>
<string name="_9" translatable="false">9</string>
<string name="_0" translatable="false">0</string>
<string name="log_file_provider_authorities">com.tangem.data.LogFileProvider</string>
<string name="ex_cant_create_coinEngine">Can\'t create CoinEngine!</string>
<string name="log_file_provider_authorities" translatable="false">com.tangem.data.LogFileProvider</string>
<string name="ex_cant_create_coinEngine" translatable="false">Can\'t create CoinEngine!</string>
<string name="pref_category_common">category_common</string>
<string name="pref_manual_editing_fee">manual_editing_fee</string>
<string name="pref_select_nodes">select_nodes</string>
<string name="pref_encryption_modes">encryption_modes</string>
<string name="pref_category_common" translatable="false">category_common</string>
<string name="pref_manual_editing_fee" translatable="false">manual_editing_fee</string>
<string name="pref_select_nodes" translatable="false">select_nodes</string>
<string name="pref_encryption_modes" translatable="false">encryption_modes</string>
</resources>

View file

@ -15,6 +15,7 @@ import com.tangem.Constant
import com.tangem.data.Blockchain
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.fragment.BaseFragment
import com.tangem.ui.fragment.qr.CameraPermissionManager
import com.tangem.ui.navigation.NavigationResultListener
import com.tangem.util.UtilHelper
import com.tangem.wallet.CoinEngineFactory
@ -31,6 +32,7 @@ class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, Nfc
override val layoutId = R.layout.fragment_prepare_transaction
private val ctx: TangemContext by lazy { TangemContext.loadFromBundle(context, arguments) }
private val cameraPermissionManager: CameraPermissionManager by lazy { CameraPermissionManager(this) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -119,6 +121,17 @@ class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, Nfc
}
ivCamera.setOnClickListener {
if (cameraPermissionManager.isPermissionGranted()) {
navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareTransactionFragment_to_qrScanFragment)
} else {
cameraPermissionManager.requirePermission()
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
cameraPermissionManager.handleRequestPermissionResult(requestCode, grantResults) {
navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareTransactionFragment_to_qrScanFragment)
}
}
@ -170,5 +183,4 @@ class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, Nfc
e.printStackTrace()
}
}
}

View file

@ -26,6 +26,9 @@ public class TangemCard {
private byte[] pbCardKey = null;
private byte[] pbWalletKeyRar = null;
private Date dtPersonalization = null;
private byte[] terminalPrivateKey;
private byte[] terminalPublicKey;
private boolean terminalIsLinked = false;
public String getBlockchainID() {
return blockchainID;
@ -156,6 +159,30 @@ public class TangemCard {
this.dtPersonalization = dtPersonalization;
}
public byte[] getTerminalPrivateKey() {
return terminalPrivateKey;
}
public void setTerminalPrivateKey(byte[] terminalPrivateKey) {
this.terminalPrivateKey = terminalPrivateKey;
}
public byte[] getTerminalPublicKey() {
return terminalPublicKey;
}
public void setTerminalPublicKey(byte[] terminalPublicKey) {
this.terminalPublicKey = terminalPublicKey;
}
public boolean getTerminalIsLinked() {
return terminalIsLinked;
}
public void setTerminalIsLinked(boolean terminalIsLinked) {
this.terminalIsLinked = terminalIsLinked;
}
private int health = 0;
public int getHealth() {
@ -350,6 +377,11 @@ public class TangemCard {
return (settingsMask & SettingsMask.UseBlock) != 0;
}
public Boolean supportLinkingTerminal() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0;
}
public int getMaxSignatures() {
return maxSignatures;
}

View file

@ -27,4 +27,7 @@ public interface PINsProvider {
*/
void setLastUsedPIN(String pin);
byte[] getTerminalPublicKey();
byte[] getTerminalPrivateKey();
}

View file

@ -34,6 +34,8 @@ import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@ -150,7 +152,8 @@ public class CardCrypto {
int rLength = enc[3];
if (enc[4 + rLength] != 0x02) throw new Exception("bad encoding 3");
if ((enc[5 + rLength] & 0x80) != 0) throw new Exception("unsupported length encoding 3");
if ((enc[5 + rLength] & 0x80) != 0)
throw new Exception("unsupported length encoding 3");
int sLength = enc[5 + rLength];
@ -286,4 +289,15 @@ public class CardCrypto {
return decryptedData;
}
}
public static Map<String, byte[]> generateTerminalKeys() throws Exception {
byte[] privateKey = Util.generateRandomBytes(32);
byte[] publicKey = GeneratePublicKey(privateKey);
Map<String, byte[]> keys = new HashMap<>();
keys.put("terminalPrivateKey", privateKey);
keys.put("terminalPublicKey", publicKey);
return keys;
}
}

View file

@ -120,6 +120,11 @@ public class CardProtocol {
return (DefaultPIN2.equals(pin2));
}
private byte[] terminalPublicKey;
public void setTerminalPublicKey(byte[] terminalPubKey) {
this.terminalPublicKey = terminalPubKey;
}
private TangemCard mCard;
private Exception mError;
@ -488,12 +493,22 @@ public class CardProtocol {
CommandApdu Apdu = new CommandApdu(ins);
byte[] baPIN = Util.calculateSHA256(mPIN);
Apdu.addTLV(TLV.Tag.TAG_PIN, baPIN);
if (ins != INS.Read) {
if (ins == INS.Read) {
addTerminalPublicKeyToApdu(Apdu);
} else {
Apdu.addTLV(TLV.Tag.TAG_CardID, mCard.getCID());
}
return Apdu;
}
private void addTerminalPublicKeyToApdu(CommandApdu apdu) {
if (terminalPublicKey != null) {
apdu.addTLV(TLV.Tag.TAG_Terminal_PublicKey, terminalPublicKey);
} else if (mCard.getTerminalPublicKey() != null) {
apdu.addTLV(TLV.Tag.TAG_Terminal_PublicKey, mCard.getTerminalPublicKey());
}
}
// /**
// * Run READ command and parse answer
// * {@see run_Read(boolean parseResult) }
@ -633,7 +648,8 @@ public class CardProtocol {
* @throws Exception - if something went wrong
*/
public void run_CreateWallet(String PIN2) throws Exception {
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
if (readResult == null)
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
CommandApdu rqApdu = StartPrepareCommand(INS.CreateWallet);
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
@ -835,7 +851,8 @@ public class CardProtocol {
CommandApdu rqApdu = StartPrepareCommand(INS.Sign);
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
rqApdu.addTLV_U8(TLV.Tag.TAG_TrOut_HashSize, hashes[0].length);
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, bs.toByteArray());
byte[] hashesConcatenated = bs.toByteArray();
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, hashesConcatenated);
if (issuerData != null) {
if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData))
throw new TangemException("Card don't support simultaneous sign with write issuer data!");
@ -853,6 +870,8 @@ public class CardProtocol {
throw new TangemException("Card require issuer validation before sign the transaction!");
}
prepareDataForLinkingTerminal(rqApdu, hashesConcatenated);
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
@ -915,6 +934,8 @@ public class CardProtocol {
throw new TangemException("Card require issuer validation before sign the transaction!");
}
prepareDataForLinkingTerminal(rqApdu, bTxOutData);
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
@ -935,6 +956,12 @@ public class CardProtocol {
}
}
private void prepareDataForLinkingTerminal(CommandApdu apdu, byte[] data) throws Exception {
byte[] transactionSignature = CardCrypto.Signature(mCard.getTerminalPrivateKey(), data);
apdu.addTLV(TLV.Tag.TAG_Terminal_TransactionSignature, transactionSignature);
addTerminalPublicKeyToApdu(apdu);
}
/**
* VERIFY_CODE command
* See [1] 8.8
@ -952,7 +979,8 @@ public class CardProtocol {
* @throws Exception - if something went wrong
*/
public byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
if (readResult == null)
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode);
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII"));
rqApdu.addTLV_U32(TLV.Tag.TAG_CodePageAddress, codePageAddress);
@ -988,7 +1016,8 @@ public class CardProtocol {
* @throws Exception - if something went wrong
*/
private void run_ValidateCard(String PIN2) throws Exception {
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
if (readResult == null)
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
CommandApdu rqApdu = StartPrepareCommand(INS.ValidateCard);
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
@ -1022,7 +1051,8 @@ public class CardProtocol {
* @throws Exception - if something went wrong
*/
public void run_WriteIssuerData(byte[] issuerData, byte[] issuerSignature) throws Exception {
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
if (readResult == null)
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData);
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);

View file

@ -29,6 +29,8 @@ public class SettingsMask {
public static final int DisablePrecomputedNDEF = 0x00010000;
public static final int SkipSecurityDelayIfValidatedByLinkedTerminal = 0x00080000;
public static String getDescription(int iValue) {
StringBuilder sb=new StringBuilder();
sb.append("[");
@ -58,6 +60,8 @@ public class SettingsMask {
if ((iValue & SettingsMask.ForbidPurgeWallet) != 0) sb.append("ForbidPurgeWallet, ");
if ((iValue & SettingsMask.AllowSelectBlockchain) != 0) sb.append("AllowSelectBlockchain, ");
if ((iValue & SettingsMask.DisablePrecomputedNDEF) != 0) sb.append("DisablePrecomputedNDEF, ");
if ((iValue & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0)
sb.append("SkipSecurityDelayIfValidatedByLinkedTerminal, ");
if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
sb.append("]");

View file

@ -85,8 +85,11 @@ public class TLV {
TAG_Denomination(0xC0),
TAG_ValidatedBalance(0xC1),
TAG_LastSign_Date(0xC2),
TAG_DenominationText(0xC3);
TAG_DenominationText(0xC3),
TAG_Terminal_IsLinked(0x58),
TAG_Terminal_PublicKey(0x5C),
TAG_Terminal_TransactionSignature(0x57);
Tag(int Code) {
this.Code = Code;

View file

@ -235,6 +235,9 @@ public class CustomReadCardTask extends Thread {
Log.e(TAG, "Can't get max signatures");
}
TLV terminalIsLinked = protocol.getReadResult().getTLV(TLV.Tag.TAG_Terminal_IsLinked);
mCard.setTerminalIsLinked(terminalIsLinked != null);
} catch (Exception e) {
e.printStackTrace();
throw new CardProtocol.TangemException("Can't parse card data");
@ -299,6 +302,9 @@ public class CustomReadCardTask extends Thread {
if (isCancelled) return;
protocol.setPIN(CardProtocol.DefaultPIN);
if (pinsProvider != null) {
protocol.setTerminalPublicKey(pinsProvider.getTerminalPublicKey());
}
protocol.clearReadResult();
if (lastRead_Encryption == null) {

View file

@ -47,7 +47,7 @@ public class SignTask extends CustomReadCardTask {
mNotifications.onReadProgress(protocol, 30);
if (isCancelled) return;
if (mCard.getPauseBeforePIN2() > 0) {
if (mCard.getPauseBeforePIN2() > 0 && !mCard.getTerminalIsLinked()) {
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
}

View file

@ -8,7 +8,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.

View file

@ -20,6 +20,7 @@ import javax.crypto.Cipher;
public class PINStorage implements PINsProvider {
private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2;
private static byte[] terminalPubicKey, terminalPrivateKey;
private static SharedPreferences sharedPreferences = null;
public static void init(Context context) {
@ -194,4 +195,19 @@ public class PINStorage implements PINsProvider {
return sharedPreferences == null;
}
public byte[] getTerminalPublicKey() {
return terminalPubicKey;
}
public static void setTerminalPublicKey(byte[] terminalPubicKey) {
PINStorage.terminalPubicKey = terminalPubicKey;
}
public byte[] getTerminalPrivateKey() {
return terminalPrivateKey;
}
public static void setTerminalPrivateKey(byte[] terminalPrivateKey) {
PINStorage.terminalPrivateKey = terminalPrivateKey;
}
}

View file

@ -81,6 +81,10 @@ fun TangemCard.loadFromBundle(B: Bundle) {
if (B.containsKey("onlineValidated"))
isOnlineValidated = B.getBoolean("onlineValidated")
if (B.containsKey("terminalPrivateKey")) terminalPrivateKey = B.getByteArray("terminalPrivateKey")
if (B.containsKey("terminalPublicKey")) terminalPublicKey = B.getByteArray("terminalPublicKey")
terminalIsLinked = B.getBoolean("terminalIsLinked")
}
val TangemCard.asBundle: Bundle
@ -152,6 +156,13 @@ fun TangemCard.saveToBundle(B: Bundle) {
if (isOnlineValidated != null)
B.putBoolean("onlineValidated", isOnlineValidated)
if (terminalPrivateKey != null)
B.putByteArray("terminalPrivateKey", terminalPrivateKey)
if (terminalPublicKey != null)
B.putByteArray("terminalPublicKey", terminalPublicKey)
B.putBoolean("terminalIsLinked", terminalIsLinked)
} catch (e: Exception) {
Log.e("Can't save to bundle ", e.message)
}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name"> tangemcard </string>
<string name="general_ok"> OK </string>
<string name="dialog_quit"> Quitter </string>
<string name="dialog_nfc_enable_title"> Activer NFC? </string>
<string name="dialog_nfc_enable_text"> Cette application est inutile lorsque NFC est désactivé. Le mode lecteur en dépend. Appuyez sur OK pour accéder aux paramètres, où vous pouvez activer NFC. </string>
</resources>

View file

@ -1,6 +1,6 @@
<resources>
<string name="app_name">tangemcard</string>
<string name="app_name" translatable="false">tangemcard</string>
<string name="general_ok">OK</string>
<string name="dialog_quit">Quit</string>
<string name="dialog_nfc_enable_title">Enable NFC?</string>