Updated on 2026-08-14
This commit is contained in:
commit
ebd33779df
71 changed files with 2041 additions and 500 deletions
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ public enum Blockchain {
|
|||
MaticTestNet("MATIC/test", "MTX", 1.0, R.drawable.tangem2, "Matic Testnet"),
|
||||
Stellar("XLM", "XLM", 1000000.0, R.drawable.ic_logo_stellar, "Stellar"),
|
||||
StellarTestNet("XLM/test", "XLM", 1000000.0, R.drawable.ic_logo_stellar, "Stellar Testnet"),
|
||||
StellarAsset("Asset", "XLM", 10000000.0, R.drawable.ic_logo_stellar, "Stellar"),
|
||||
Eos("EOS", "EOS", 10000.0, R.drawable.tangem2, "EOS");
|
||||
|
||||
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ import io.reactivex.schedulers.Schedulers;
|
|||
* {@link Listener}.onSuccess(...) callback
|
||||
*/
|
||||
public class ServerApiStellar {
|
||||
|
||||
public ServerApiStellar(Blockchain blockchain) {
|
||||
if (blockchain == Blockchain.Stellar || blockchain == Blockchain.StellarAsset) {
|
||||
currentURL = ServerURL.API_STELLAR;
|
||||
} else {
|
||||
currentURL = ServerURL.API_STELLAR_TESTNET;
|
||||
}
|
||||
}
|
||||
|
||||
private static String TAG = ServerApiStellar.class.getSimpleName();
|
||||
|
||||
/**
|
||||
|
|
@ -41,6 +50,12 @@ public class ServerApiStellar {
|
|||
|
||||
private int requestsCount = 0;
|
||||
|
||||
private String currentURL;
|
||||
|
||||
public String getCurrentURL() {
|
||||
return currentURL;
|
||||
}
|
||||
|
||||
public boolean isRequestsSequenceCompleted() {
|
||||
LOG.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
|
||||
return requestsCount <= 0;
|
||||
|
|
@ -82,9 +97,15 @@ public class ServerApiStellar {
|
|||
* @param ctx
|
||||
* @param stellarRequest
|
||||
*/
|
||||
|
||||
public void requestData(TangemContext ctx, StellarRequest.Base stellarRequest) {
|
||||
requestData(ctx, stellarRequest, false);
|
||||
}
|
||||
|
||||
public void requestData(TangemContext ctx, StellarRequest.Base stellarRequest, boolean isRetry) {
|
||||
requestsCount++;
|
||||
LOG.i(TAG, String.format("New request[%d]: %s", requestsCount, stellarRequest.getClass().getSimpleName()));
|
||||
|
||||
Observable<StellarRequest.Base> stellarObserver = Observable.just(stellarRequest)
|
||||
.doOnEach(stellarRequest1 -> doStellarRequest(ctx, stellarRequest))
|
||||
|
||||
|
|
@ -96,9 +117,9 @@ public class ServerApiStellar {
|
|||
return Observable.just(stellarRequest1);
|
||||
}
|
||||
)
|
||||
.retryWhen(errors -> errors
|
||||
.filter(throwable -> (throwable instanceof IOException) || (throwable instanceof ErrorResponse))
|
||||
.zipWith(Observable.range(1, 4), (n, i) -> i))
|
||||
// .retryWhen(errors -> errors
|
||||
// .filter(throwable -> (throwable instanceof IOException) || (throwable instanceof ErrorResponse))
|
||||
// .zipWith(Observable.range(1, 4), (n, i) -> i))
|
||||
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
|
|
@ -114,9 +135,14 @@ public class ServerApiStellar {
|
|||
requestsCount--;
|
||||
LOG.e(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onError " + e.getMessage());
|
||||
LOG.e(TAG, String.format("%d requests left in processing", requestsCount));
|
||||
stellarRequest.setError(ctx.getString(R.string.loaded_wallet_error_obtaining_blockchain_data));
|
||||
//setErrorOccurred(e.getMessage());//;
|
||||
listener.onFail(stellarRequest);
|
||||
|
||||
if (isRetry || stellarRequest.errorResponse.getCode() == 404) {
|
||||
stellarRequest.setError(e.getMessage());
|
||||
//setErrorOccurred(e.getMessage());//;
|
||||
listener.onFail(stellarRequest);
|
||||
} else {
|
||||
retryRequest(ctx, stellarRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -128,7 +154,12 @@ public class ServerApiStellar {
|
|||
LOG.e(TAG, String.format("%d requests left in processing", requestsCount));
|
||||
if (stellarRequest.getError() != null) {
|
||||
LOG.i(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onComplete, error!=null");
|
||||
listener.onFail(stellarRequest);
|
||||
|
||||
if (isRetry || stellarRequest.errorResponse.getCode() == 404) {
|
||||
listener.onFail(stellarRequest);
|
||||
} else {
|
||||
retryRequest(ctx, stellarRequest);
|
||||
}
|
||||
} else {
|
||||
LOG.e(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onComplete, error==null");
|
||||
listener.onSuccess(stellarRequest);
|
||||
|
|
@ -142,12 +173,12 @@ public class ServerApiStellar {
|
|||
stellarRequest.setError(null);
|
||||
try {
|
||||
Server server;
|
||||
if (ctx.getBlockchain() == Blockchain.Stellar) {
|
||||
if (ctx.getBlockchain() == Blockchain.Stellar || ctx.getBlockchain() == Blockchain.StellarAsset) {
|
||||
Network.usePublicNetwork();
|
||||
server = new Server(ServerURL.API_STELLAR);
|
||||
server = new Server(currentURL);
|
||||
} else if (ctx.getBlockchain() == Blockchain.StellarTestNet) {
|
||||
Network.useTestNetwork();
|
||||
server = new Server(ServerURL.API_STELLAR_TESTNET);
|
||||
server = new Server(currentURL);
|
||||
} else {
|
||||
throw new IOException("Wrong blockchain for ServerApiStellar");
|
||||
}
|
||||
|
|
@ -166,4 +197,9 @@ public class ServerApiStellar {
|
|||
}
|
||||
}
|
||||
|
||||
public void retryRequest (TangemContext ctx, StellarRequest.Base stellarRequest) {
|
||||
currentURL = ServerURL.API_STELLAR_RESERVE;
|
||||
requestData(ctx, stellarRequest, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -11,8 +11,9 @@ class ServerURL {
|
|||
static final String API_BLOCKCYPHER = "https://api.blockcypher.com/";
|
||||
static final String API_BINANCE = "https://dex.binance.org/";
|
||||
static final String API_BINANCE_TESTNET = "https://testnet-dex.binance.org/";
|
||||
static final String API_MATIC_TESTNET = "https://testnet2.matic.network";
|
||||
static final String API_MATIC_TESTNET = "https://testnet2.matic.network/";
|
||||
static final String API_STELLAR = "https://horizon.stellar.org/";
|
||||
static final String API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org";
|
||||
static final String API_STELLAR_RESERVE = "https://horizon.sui.li/";
|
||||
static final String API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/";
|
||||
static final String API_BLOCKCHAIN_INFO = "https://blockchain.info/";
|
||||
}
|
||||
|
|
@ -1,26 +1,21 @@
|
|||
@file:Suppress("ObsoleteExperimentalCoroutines")
|
||||
|
||||
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 androidx.navigation.findNavController
|
||||
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
|
||||
import com.tangem.ui.dialog.RootFoundDialog
|
||||
import com.tangem.util.navigateSafely
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import javax.inject.Inject
|
||||
|
|
@ -44,6 +39,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
|||
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
|
||||
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
|
||||
if (tag != null) {
|
||||
findNavController(R.id.nav_host_fragment).navigateSafely(R.id.main)
|
||||
onTagDiscovered(tag)
|
||||
}
|
||||
}
|
||||
|
|
@ -59,8 +55,6 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
|||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
|
||||
verifyPermissions()
|
||||
|
||||
nfcManager = NfcManager(this, this)
|
||||
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
|
||||
|
||||
|
|
@ -79,13 +73,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
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.navigation.fragment.NavHostFragment.findNavController
|
|||
import com.tangem.ui.activity.MainActivity
|
||||
import com.tangem.ui.navigation.NavigationResult
|
||||
import com.tangem.ui.navigation.NavigationResultListener
|
||||
import com.tangem.util.navigateSafely
|
||||
|
||||
abstract class BaseFragment : Fragment() {
|
||||
|
||||
|
|
@ -74,13 +75,7 @@ abstract class BaseFragment : Fragment() {
|
|||
}
|
||||
|
||||
protected fun navigateToDestination(@IdRes destination: Int, data: Bundle? = null) {
|
||||
try {
|
||||
findNavController(this).navigate(destination, data)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.w(this::class.java.simpleName, e.message)
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.w(this::class.java.simpleName, e.message)
|
||||
}
|
||||
findNavController(this).navigateSafely(destination, data)
|
||||
}
|
||||
|
||||
protected fun navigateBackWithResult(resultCode: Int, data: Bundle? = null,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -10,6 +11,7 @@ import android.widget.PopupMenu
|
|||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.tangem.App
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.Logger
|
||||
|
|
@ -17,6 +19,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 +59,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)
|
||||
|
|
@ -82,7 +86,7 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
|
|||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
// viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
|
||||
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
|
||||
//
|
||||
// // show snackbar about new version app
|
||||
// viewModel.getVersionName().observe(this, Observer { text ->
|
||||
|
|
@ -112,6 +116,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 +131,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 +172,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 +189,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 +237,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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -698,7 +698,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte
|
|||
// Bitcoin, Litecoin, BitcoinCash, Stellar
|
||||
if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet ||
|
||||
ctx.blockchain == Blockchain.Litecoin || ctx.blockchain == Blockchain.BitcoinCash ||
|
||||
ctx.blockchain == Blockchain.Stellar || ctx.blockchain == Blockchain.StellarTestNet) {
|
||||
ctx.blockchain == Blockchain.Stellar || ctx.blockchain == Blockchain.StellarTestNet || ctx.blockchain == Blockchain.StellarAsset) {
|
||||
ctx.coinData.setIsBalanceEqual(true)
|
||||
}
|
||||
|
||||
|
|
|
|||
16
app/src/main/java/com/tangem/util/NavigationExtensions.kt
Normal file
16
app/src/main/java/com/tangem/util/NavigationExtensions.kt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.util
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.navigation.NavController
|
||||
|
||||
fun NavController.navigateSafely(@IdRes destination: Int, data: Bundle? = null) {
|
||||
try {
|
||||
this.navigate(destination, data)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.w(this::class.java.simpleName, e.message)
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.w(this::class.java.simpleName, e.message)
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.wallet.matic.MaticTokenEngine
|
|||
import com.tangem.wallet.nftToken.NftTokenEngine
|
||||
import com.tangem.wallet.rsk.RskEngine
|
||||
import com.tangem.wallet.rsk.RskTokenEngine
|
||||
import com.tangem.wallet.xlm.XlmAssetEngine
|
||||
import com.tangem.wallet.xlm.XlmEngine
|
||||
import com.tangem.wallet.xrp.XrpEngine
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ object CoinEngineFactory {
|
|||
Blockchain.Binance, Blockchain.BinanceTestNet -> BinanceEngine()
|
||||
Blockchain.Matic, Blockchain.MaticTestNet -> MaticTokenEngine()
|
||||
Blockchain.StellarTestNet, Blockchain.Stellar -> XlmEngine()
|
||||
Blockchain.StellarAsset -> XlmAssetEngine()
|
||||
Blockchain.Eos -> EosEngine()
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -80,6 +82,8 @@ object CoinEngineFactory {
|
|||
MaticTokenEngine(context)
|
||||
else if (Blockchain.Stellar == context.blockchain || Blockchain.StellarTestNet == context.blockchain)
|
||||
XlmEngine(context)
|
||||
else if (Blockchain.StellarAsset == context.blockchain)
|
||||
XlmAssetEngine(context)
|
||||
else if (Blockchain.Eos == context.blockchain)
|
||||
EosEngine(context)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ public class TangemContext {
|
|||
if ((blockchain == Blockchain.Rootstock) && card.isToken()) {
|
||||
return Blockchain.RootstockToken;
|
||||
}
|
||||
if (blockchain == Blockchain.Stellar && card.isToken()) {
|
||||
return Blockchain.StellarAsset;
|
||||
}
|
||||
return blockchain;
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +61,9 @@ public class TangemContext {
|
|||
if (blockchain == Blockchain.NftToken) {
|
||||
return card.getTokenSymbol().substring(4) + " <br><small><small> " + getBlockchain().getOfficialName() + " NFT token</small></small>";
|
||||
}
|
||||
if (blockchain == Blockchain.StellarAsset) {
|
||||
return card.getTokenSymbol() + " <br><small><small> " + getBlockchain().getOfficialName() + " asset</small></small>";
|
||||
}
|
||||
return blockchain.getOfficialName();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.wallet.bch
|
|||
|
||||
enum class BitcoinCashNode(val host: String, val port: Int, val proto: String) {
|
||||
N_001("electrumx.hillsideinternet.com", 50002, "ssl"),
|
||||
N_002("electron.coinucopia.io", 50002, "ssl"),
|
||||
N_003("blackie.c3-soft.com", 50002, "ssl"),
|
||||
N_004("electrum.imaginary.cash", 50002, "ssl"),
|
||||
N_002("blackie.c3-soft.com", 50002, "ssl"),
|
||||
N_003("electrum.imaginary.cash", 50002, "ssl"),
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
177
app/src/main/java/com/tangem/wallet/xlm/XlmAssetData.java
Normal file
177
app/src/main/java/com/tangem/wallet/xlm/XlmAssetData.java
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package com.tangem.wallet.xlm;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.wallet.CoinData;
|
||||
import com.tangem.wallet.CoinEngine;
|
||||
|
||||
import org.stellar.sdk.KeyPair;
|
||||
import org.stellar.sdk.responses.AccountResponse;
|
||||
import org.stellar.sdk.responses.LedgerResponse;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/*
|
||||
* Created by dvol on 7.01.2019.
|
||||
*/
|
||||
|
||||
public class XlmAssetData extends CoinData {
|
||||
|
||||
|
||||
public static class AccountResponseEx extends AccountResponse {
|
||||
AccountResponseEx(String accountId, Long sequenceNumber) {
|
||||
super(KeyPair.fromAccountId(accountId), sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private CoinEngine.Amount xlmBalance, assetBalance = null;
|
||||
|
||||
private Long sequenceNumber = 0L;
|
||||
private CoinEngine.Amount baseReserve = new CoinEngine.Amount("0.5", "XLM");
|
||||
private CoinEngine.Amount baseFee = new CoinEngine.Amount("0.00001", "XLM");
|
||||
private boolean error404 = false;
|
||||
|
||||
@Override
|
||||
public void clearInfo() {
|
||||
super.clearInfo();
|
||||
xlmBalance = null;
|
||||
assetBalance = null;
|
||||
error404 = false;
|
||||
}
|
||||
|
||||
CoinEngine.Amount getXlmBalance() {
|
||||
if (xlmBalance != null) {
|
||||
return new CoinEngine.Amount(xlmBalance.subtract(getReserve()), "XLM");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
CoinEngine.Amount getAssetBalance() {
|
||||
return assetBalance;
|
||||
}
|
||||
|
||||
CoinEngine.Amount getReserve() {
|
||||
return new CoinEngine.Amount(baseReserve.multiply(BigDecimal.valueOf(3)), "XLM");
|
||||
}
|
||||
|
||||
CoinEngine.Amount getBaseFee() {
|
||||
return baseFee;
|
||||
}
|
||||
|
||||
AccountResponse getAccountResponse() {
|
||||
return new AccountResponseEx(getWallet(), sequenceNumber);
|
||||
}
|
||||
|
||||
void setAccountResponse(AccountResponse accountResponse) {
|
||||
for (AccountResponse.Balance responseBalance : accountResponse.getBalances()) {
|
||||
if (responseBalance.getAssetType().equals("native")) {
|
||||
xlmBalance = new CoinEngine.Amount(responseBalance.getBalance(), "XLM");
|
||||
} else {
|
||||
assetBalance = new CoinEngine.Amount(responseBalance.getBalance(), responseBalance.getAssetCode());
|
||||
}
|
||||
}
|
||||
sequenceNumber = accountResponse.getSequenceNumber();
|
||||
setBalanceReceived(true);
|
||||
}
|
||||
|
||||
void setLedgerResponse(LedgerResponse ledgerResponse) {
|
||||
XlmEngine xlmEngine = new XlmEngine();
|
||||
baseReserve = xlmEngine.convertToAmount(new CoinEngine.InternalAmount(ledgerResponse.getBaseReserveInStroops(), "stroops"));
|
||||
baseFee = xlmEngine.convertToAmount(new CoinEngine.InternalAmount(ledgerResponse.getBaseFeeInStroops(), "stroops"));
|
||||
}
|
||||
|
||||
public void incSequenceNumber() {
|
||||
sequenceNumber++;
|
||||
}
|
||||
|
||||
public boolean isError404() {
|
||||
return error404;
|
||||
}
|
||||
|
||||
public void setError404(boolean error404) {
|
||||
this.error404 = error404;
|
||||
}
|
||||
|
||||
public boolean isAssetBalanceZero() {
|
||||
if (assetBalance != null && assetBalance.notZero())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromBundle(Bundle B) {
|
||||
super.loadFromBundle(B);
|
||||
|
||||
if (B.containsKey("BalanceCurrency") && B.containsKey("BalanceDecimal")) {
|
||||
xlmBalance = new CoinEngine.Amount(B.getString("BalanceDecimal"), B.getString("BalanceCurrency"));
|
||||
} else {
|
||||
xlmBalance = null;
|
||||
}
|
||||
|
||||
if (B.containsKey("AssetBalanceCurrency") && B.containsKey("AssetBalanceDecimal")) {
|
||||
assetBalance = new CoinEngine.Amount(B.getString("AssetBalanceDecimal"), B.getString("AssetBalanceCurrency"));
|
||||
} else {
|
||||
assetBalance = null;
|
||||
}
|
||||
|
||||
if (B.containsKey("sequenceNumber")) {
|
||||
sequenceNumber = B.getLong("sequenceNumber");
|
||||
} else {
|
||||
sequenceNumber = 0L;
|
||||
}
|
||||
|
||||
if (B.containsKey("BaseReserveCurrency") && B.containsKey("BaseReserveDecimal")) {
|
||||
baseReserve = new CoinEngine.Amount(B.getString("BaseReserveDecimal"), B.getString("BaseReserveCurrency"));
|
||||
} else {
|
||||
baseReserve = new CoinEngine.Amount("0.5", "XLM");
|
||||
}
|
||||
|
||||
if (B.containsKey("BaseFeeCurrency") && B.containsKey("BaseFeeDecimal")) {
|
||||
baseFee = new CoinEngine.Amount(B.getString("BaseFeeDecimal"), B.getString("BaseFeeCurrency"));
|
||||
} else {
|
||||
baseFee = new CoinEngine.Amount("0.00001", "XLM");
|
||||
}
|
||||
|
||||
if (B.containsKey("Error404")) error404 = B.getBoolean("Error404");
|
||||
else error404 = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToBundle(Bundle B) {
|
||||
super.saveToBundle(B);
|
||||
try {
|
||||
if (xlmBalance != null) {
|
||||
B.putString("BalanceCurrency", xlmBalance.getCurrency());
|
||||
B.putString("BalanceDecimal", xlmBalance.toValueString());
|
||||
}
|
||||
|
||||
if (assetBalance != null) {
|
||||
B.putString("AssetBalanceCurrency", assetBalance.getCurrency());
|
||||
B.putString("AssetBalanceDecimal", assetBalance.toValueString());
|
||||
}
|
||||
|
||||
if (sequenceNumber != null) {
|
||||
B.putLong("sequenceNumber", sequenceNumber);
|
||||
}
|
||||
|
||||
if (baseReserve != null) {
|
||||
B.putString("BaseReserveCurrency", baseReserve.getCurrency());
|
||||
B.putString("BaseReserveDecimal", baseReserve.toValueString());
|
||||
}
|
||||
|
||||
if (baseFee != null) {
|
||||
B.putString("BaseFeeCurrency", baseFee.getCurrency());
|
||||
B.putString("BaseFeeDecimal", baseFee.toValueString());
|
||||
}
|
||||
|
||||
if (error404) B.putBoolean("Error404", true);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("Can't save to bundle ", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
610
app/src/main/java/com/tangem/wallet/xlm/XlmAssetEngine.java
Normal file
610
app/src/main/java/com/tangem/wallet/xlm/XlmAssetEngine.java
Normal file
|
|
@ -0,0 +1,610 @@
|
|||
package com.tangem.wallet.xlm;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.StrictMode;
|
||||
import android.text.InputFilter;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.data.Blockchain;
|
||||
import com.tangem.data.network.ServerApiStellar;
|
||||
import com.tangem.data.network.StellarRequest;
|
||||
import com.tangem.tangem_card.data.TangemCard;
|
||||
import com.tangem.tangem_card.tasks.SignTask;
|
||||
import com.tangem.tangem_card.util.Util;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.wallet.BalanceValidator;
|
||||
import com.tangem.wallet.CoinData;
|
||||
import com.tangem.wallet.CoinEngine;
|
||||
import com.tangem.wallet.R;
|
||||
import com.tangem.wallet.TangemContext;
|
||||
|
||||
import org.stellar.sdk.Asset;
|
||||
import org.stellar.sdk.AssetTypeNative;
|
||||
import org.stellar.sdk.ChangeTrustOperation;
|
||||
import org.stellar.sdk.CreateAccountOperation;
|
||||
import org.stellar.sdk.KeyPair;
|
||||
import org.stellar.sdk.Operation;
|
||||
import org.stellar.sdk.PaymentOperation;
|
||||
import org.stellar.sdk.Transaction;
|
||||
import org.stellar.sdk.TransactionEx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by dvol on 7.01.2019.
|
||||
* <p>
|
||||
* PS. To create and fill testnet account just open https://friendbot.stellar.org/?addr=XXX in browser
|
||||
**/
|
||||
|
||||
public class XlmAssetEngine extends CoinEngine {
|
||||
|
||||
private static final String TAG = XlmAssetEngine.class.getSimpleName();
|
||||
|
||||
public XlmAssetData coinData = null;
|
||||
|
||||
public XlmAssetEngine(TangemContext context) throws Exception {
|
||||
super(context);
|
||||
if (context.getCoinData() == null) {
|
||||
coinData = new XlmAssetData();
|
||||
context.setCoinData(coinData);
|
||||
} else if (context.getCoinData() instanceof XlmAssetData) {
|
||||
coinData = (XlmAssetData) context.getCoinData();
|
||||
} else {
|
||||
throw new Exception("Invalid type of Blockchain data for XlmAssetEngine");
|
||||
}
|
||||
}
|
||||
|
||||
public XlmAssetEngine() {
|
||||
super();
|
||||
}
|
||||
|
||||
private static int getDecimals() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
|
||||
private void checkBlockchainDataExists() throws Exception {
|
||||
if (coinData == null) throw new Exception("No blockchain data");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitingConfirmation() {
|
||||
if (coinData == null) return false;
|
||||
//TODO
|
||||
return false;//coinData.getBalanceUnconfirmed() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Amount getBalance() {
|
||||
if (!hasBalanceInfo()) return null;
|
||||
if (!coinData.isAssetBalanceZero())
|
||||
return coinData.getAssetBalance();
|
||||
else
|
||||
return coinData.getXlmBalance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBalanceHTML() {
|
||||
Amount balance = coinData.getXlmBalance();
|
||||
Amount assetBalance = coinData.getAssetBalance();
|
||||
|
||||
if (balance != null) {
|
||||
if (!coinData.isAssetBalanceZero()) {
|
||||
return " " + assetBalance.toDescriptionString(getDecimals()) + "<br><small><small>"+ balance.toDescriptionString(getDecimals()) + " for fee + " + coinData.getReserve().toDescriptionString(getDecimals()) + " reserve</small></small>";
|
||||
}
|
||||
return " " + balance.toDescriptionString(getDecimals()) + "<br><small><small>+ " + coinData.getReserve().toDescriptionString(getDecimals()) + " reserve</small></small>";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBalanceCurrency() {
|
||||
if (!coinData.isAssetBalanceZero()) {
|
||||
return coinData.getAssetBalance().getCurrency();
|
||||
} else {
|
||||
return "XLM";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBalanceNotZero() {
|
||||
if (coinData == null) return false;
|
||||
if (coinData.getXlmBalance() == null) return false;
|
||||
return coinData.getXlmBalance().notZero();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBalanceInfo() {
|
||||
if (coinData == null) return false;
|
||||
return coinData.getXlmBalance() != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isExtractPossible() {
|
||||
if (!hasBalanceInfo()) {
|
||||
ctx.setMessage(R.string.loaded_wallet_error_obtaining_blockchain_data);
|
||||
} else if (!isBalanceNotZero()) {
|
||||
ctx.setMessage(R.string.general_wallet_empty);
|
||||
} else if (awaitingConfirmation()) {
|
||||
ctx.setMessage(R.string.loaded_wallet_message_wait);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeeCurrency() {
|
||||
return "XLM";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateAddress(String address) {
|
||||
try {
|
||||
KeyPair kp = KeyPair.fromAccountId(address);
|
||||
// TODO is it possible to check address testNet or not
|
||||
// if (ctx.getBlockchain() == Blockchain.StellarTestNet) {
|
||||
// return false;
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isNeedCheckNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getWalletExplorerUri() {
|
||||
return Uri.parse("http://stellarchain.io/address/" + ctx.getCoinData().getWallet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getShareWalletUri() {
|
||||
//TODO - how to construct payment query intent for stellar?
|
||||
// if (ctx.getCard().getDenomination() != null) {
|
||||
// return Uri.parse(ctx.getCoinData().getWallet() + "?amount=" + convertToAmount(convertToInternalAmount(ctx.getCard().getDenomination())).toValueString());
|
||||
// } else {
|
||||
return Uri.parse(ctx.getCoinData().getWallet());
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputFilter[] getAmountInputFilters() {
|
||||
return new InputFilter[]{new DecimalDigitsInputFilter(getDecimals())};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkNewTransactionAmount(Amount amount) {
|
||||
if (coinData == null) return false;
|
||||
|
||||
Amount balance;
|
||||
if (!coinData.isAssetBalanceZero()) {
|
||||
balance = coinData.getAssetBalance();
|
||||
} else {
|
||||
balance = coinData.getXlmBalance();
|
||||
}
|
||||
|
||||
if (amount.compareTo(balance) > 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkNewTransactionAmountAndFee(Amount amountValue, Amount feeValue, Boolean isIncludeFee) {
|
||||
try {
|
||||
checkBlockchainDataExists();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (feeValue == null || amountValue == null)
|
||||
return false;
|
||||
|
||||
if (feeValue.isZero() || amountValue.isZero())
|
||||
return false;
|
||||
|
||||
if (!coinData.isAssetBalanceZero()) {
|
||||
if (amountValue.compareTo(coinData.getAssetBalance()) > 0 || feeValue.compareTo(coinData.getXlmBalance()) > 0)
|
||||
return false;
|
||||
} else {
|
||||
if (isIncludeFee && (amountValue.compareTo(coinData.getXlmBalance()) > 0 || amountValue.compareTo(feeValue) < 0))
|
||||
return false;
|
||||
if (!isIncludeFee && amountValue.add(feeValue).compareTo(coinData.getXlmBalance()) > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateBalance(BalanceValidator balanceValidator) {
|
||||
try {
|
||||
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(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(R.string.balance_validator_first_line_unknown_balance);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround before new back-end
|
||||
// if (card.getRemainingSignatures() == card.getMaxSignatures()) {
|
||||
// firstLine = "Verified balance";
|
||||
// secondLine = "Balance confirmed in blockchain. ";
|
||||
// secondLine += "Verified note identity. ";
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (coinData.getBalanceUnconfirmed() != 0) {
|
||||
// balanceValidator.setScore(0);
|
||||
// balanceValidator.setFirstLine("Transaction in progress");
|
||||
// balanceValidator.setSecondLine("Wait for confirmation in blockchain");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
|
||||
balanceValidator.setScore(100);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
|
||||
if (coinData.getXlmBalance().isZero()) {
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
|
||||
balanceValidator.setSecondLine(R.string.empty_string);
|
||||
}
|
||||
}
|
||||
|
||||
// rule 4 TODO: need to check SignedHashed against number of outputs in blockchain
|
||||
// if((card.getRemainingSignatures() != card.getMaxSignatures()) && card.getBalance() != 0)
|
||||
// {
|
||||
// score = 80;
|
||||
// firstLine = "Unguaranteed balance";
|
||||
// secondLine = "Potential unsent transaction. Redeem immediately if accept. ";
|
||||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getXlmBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
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) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
// secondLine += "Not all nodes have returned balance. Swipe down or tap again. ";
|
||||
// if(score <= 0)
|
||||
// return;
|
||||
// }
|
||||
|
||||
//
|
||||
// if(card.isBalanceReceived() && !card.isBalanceEqual()) {
|
||||
// score = 0;
|
||||
// firstLine = "Disputed balance";
|
||||
// secondLine += " Cannot obtain trusted balance at the moment. Try to tap and check this banknote later.";
|
||||
// return;
|
||||
// }
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String evaluateFeeEquivalent(String fee) {
|
||||
if (!coinData.getAmountEquivalentDescriptionAvailable()) return "";
|
||||
try {
|
||||
Amount feeAmount = new Amount(fee, getFeeCurrency());
|
||||
return feeAmount.toEquivalentString(coinData.getRate());
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBalanceEquivalent() {
|
||||
if (coinData == null || !coinData.getAmountEquivalentDescriptionAvailable()) return "";
|
||||
Amount balance = getBalance();
|
||||
if (balance == null) return "";
|
||||
return balance.toEquivalentString(coinData.getRate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String calculateAddress(byte[] pkUncompressed) {
|
||||
KeyPair kp = KeyPair.fromPublicKey(pkUncompressed);
|
||||
return kp.getAccountId();
|
||||
}
|
||||
|
||||
private static BigDecimal multiplier = new BigDecimal("10000000");
|
||||
|
||||
@Override
|
||||
public Amount convertToAmount(InternalAmount internalAmount) {
|
||||
BigDecimal d = internalAmount.divide(multiplier);
|
||||
return new Amount(d, getBalanceCurrency());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Amount convertToAmount(String strAmount, String currency) {
|
||||
return new Amount(strAmount, currency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalAmount convertToInternalAmount(Amount amount) {
|
||||
BigDecimal d = amount.multiply(multiplier);
|
||||
return new InternalAmount(d, "stroops");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalAmount convertToInternalAmount(byte[] bytes) {
|
||||
if (bytes == null) return null;
|
||||
byte[] reversed = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1];
|
||||
return new InternalAmount(Util.byteArrayToLong(reversed), "stroops");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] convertToByteArray(InternalAmount internalAmount) {
|
||||
byte[] bytes = Util.longToByteArray(internalAmount.longValueExact());
|
||||
byte[] reversed = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1];
|
||||
return reversed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoinData createCoinData() {
|
||||
return new XlmAssetData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUnspentInputsDescription() {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception {
|
||||
checkBlockchainDataExists();
|
||||
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
|
||||
if (coinData.isAssetBalanceZero() && IncFee) {
|
||||
amountValue = new Amount(amountValue.subtract(feeValue), amountValue.getCurrency());
|
||||
}
|
||||
|
||||
Operation operation;
|
||||
if (coinData.getAssetBalance() == null) {
|
||||
operation = new ChangeTrustOperation.Builder(Asset.createNonNativeAsset(ctx.getCard().getTokenSymbol(), KeyPair.fromAccountId(ctx.getCard().getContractAddress())), "900000000000.0000000").build();
|
||||
} else {
|
||||
if (!coinData.isAssetBalanceZero()) {
|
||||
operation = new PaymentOperation.Builder(KeyPair.fromAccountId(targetAddress), Asset.createNonNativeAsset(ctx.getCard().getTokenSymbol(), KeyPair.fromAccountId(ctx.getCard().getContractAddress())), amountValue.toValueString()).build();
|
||||
} else {
|
||||
if (isAccountCreated(targetAddress))
|
||||
operation = new PaymentOperation.Builder(KeyPair.fromAccountId(targetAddress), new AssetTypeNative(), amountValue.toValueString()).build();
|
||||
else
|
||||
operation = new CreateAccountOperation.Builder(KeyPair.fromAccountId(targetAddress), amountValue.toValueString()).build();
|
||||
}
|
||||
}
|
||||
TransactionEx transaction = TransactionEx.buildEx(60, coinData.getAccountResponse(), operation);
|
||||
|
||||
|
||||
if (transaction.getFee() != convertToInternalAmount(feeValue).intValueExact()) {
|
||||
throw new Exception("Invalid fee!");
|
||||
}
|
||||
|
||||
return new SignTask.TransactionToSign() {
|
||||
|
||||
@Override
|
||||
public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
|
||||
return signingMethod == TangemCard.SigningMethod.Sign_Hash || signingMethod == TangemCard.SigningMethod.Sign_Raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getHashesToSign() throws Exception {
|
||||
byte[][] dataForSign = new byte[1][];
|
||||
dataForSign[0] = transaction.hash();
|
||||
return dataForSign;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRawDataToSign() throws Exception {
|
||||
return transaction.signatureBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHashAlgToSign() {
|
||||
return "sha-256";
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
|
||||
throw new Exception("Issuer validation not supported!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] onSignCompleted(byte[] signFromCard) throws Exception {
|
||||
// Sign the transaction to prove you are actually the person sending it.
|
||||
transaction.setSign(signFromCard);
|
||||
|
||||
byte[] txForSend = transaction.toEnvelopeXdrBase64().getBytes();
|
||||
notifyOnNeedSendTransaction(txForSend);
|
||||
return txForSend;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// network call inside, don't use on main thread
|
||||
private boolean isAccountCreated(String address) {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar(ctx.getBlockchain());
|
||||
|
||||
StellarRequest.Balance request = new StellarRequest.Balance(address);
|
||||
|
||||
try {
|
||||
serverApi.doStellarRequest(ctx, request);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
return true; // suppose account is created if anything goes wrong TODO:check
|
||||
}
|
||||
|
||||
if (request.errorResponse != null && request.errorResponse.getCode() == 404)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar(ctx.getBlockchain());
|
||||
|
||||
ServerApiStellar.Listener listener = new ServerApiStellar.Listener() {
|
||||
@Override
|
||||
public void onSuccess(StellarRequest.Base request) {
|
||||
Log.i(TAG, "onSuccess: " + request.getClass().getSimpleName());
|
||||
|
||||
if (request instanceof StellarRequest.Balance) {
|
||||
StellarRequest.Balance balanceRequest = (StellarRequest.Balance) request;
|
||||
|
||||
coinData.setAccountResponse(balanceRequest.accountResponse);
|
||||
coinData.setValidationNodeDescription(serverApi.getCurrentURL());
|
||||
|
||||
if (serverApi.isRequestsSequenceCompleted()) {
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress();
|
||||
}
|
||||
} else if (request instanceof StellarRequest.Ledgers) {
|
||||
StellarRequest.Ledgers ledgersRequest = (StellarRequest.Ledgers) request;
|
||||
|
||||
coinData.setLedgerResponse(ledgersRequest.ledgerResponse);
|
||||
|
||||
if (serverApi.isRequestsSequenceCompleted()) {
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress();
|
||||
}
|
||||
} else {
|
||||
ctx.setError("Invalid request logic");
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFail(StellarRequest.Base request) {
|
||||
Log.i(TAG, "onFail: " + request.getClass().getSimpleName() + " " + request.getError());
|
||||
|
||||
if (request.errorResponse.getCode() == 404) {
|
||||
coinData.setError404(true);
|
||||
} else {
|
||||
ctx.setError(request.getError());
|
||||
}
|
||||
|
||||
if (serverApi.isRequestsSequenceCompleted()) {
|
||||
if (ctx.hasError()) {
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
}
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
serverApi.setListener(listener);
|
||||
|
||||
serverApi.requestData(ctx, new StellarRequest.Balance(coinData.getWallet()));
|
||||
serverApi.requestData(ctx, new StellarRequest.Ledgers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception {
|
||||
// TODO: get fee stats?
|
||||
coinData.minFee = coinData.normalFee = coinData.maxFee = coinData.getBaseFee();
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws IOException {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar(ctx.getBlockchain());
|
||||
|
||||
ServerApiStellar.Listener listener = new ServerApiStellar.Listener() {
|
||||
@Override
|
||||
public void onSuccess(StellarRequest.Base request) {
|
||||
try {
|
||||
if (!StellarRequest.SubmitTransaction.class.isInstance(request))
|
||||
throw new Exception("Invalid request logic");
|
||||
StellarRequest.SubmitTransaction submitTransactionRequest = (StellarRequest.SubmitTransaction) request;
|
||||
if (submitTransactionRequest.response.isSuccess()) {
|
||||
ctx.setError(null);
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
} else {
|
||||
if (submitTransactionRequest.response.getExtras() != null && submitTransactionRequest.response.getExtras().getResultCodes() != null) {
|
||||
String trResult = submitTransactionRequest.response.getExtras().getResultCodes().getTransactionResultCode();
|
||||
if (submitTransactionRequest.response.getExtras().getResultCodes().getOperationsResultCodes() != null && submitTransactionRequest.response.getExtras().getResultCodes().getOperationsResultCodes().size() > 0) {
|
||||
trResult += "/" + submitTransactionRequest.response.getExtras().getResultCodes().getOperationsResultCodes().get(0);
|
||||
}
|
||||
ctx.setError(trResult);
|
||||
} else {
|
||||
ctx.setError("transaction failed");
|
||||
}
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage() != null) {
|
||||
ctx.setError(e.getMessage());
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
} else {
|
||||
ctx.setError(e.getClass().getName());
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(StellarRequest.Base request) {
|
||||
ctx.setError(request.getError());
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
};
|
||||
serverApi.setListener(listener);
|
||||
|
||||
Transaction transaction = TransactionEx.fromEnvelopeXdr(new String(txForSend));
|
||||
coinData.incSequenceNumber();
|
||||
serverApi.requestData(ctx, new StellarRequest.SubmitTransaction(transaction));
|
||||
|
||||
}
|
||||
|
||||
public boolean needMultipleLinesForBalance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean allowSelectFeeLevel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean allowSelectFeeInclusion() {
|
||||
return coinData.isAssetBalanceZero();
|
||||
}
|
||||
|
||||
public int pendingTransactionTimeoutInSeconds() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,13 +30,14 @@ public class XlmData extends CoinData {
|
|||
private Long sequenceNumber = 0L;
|
||||
private CoinEngine.Amount baseReserve = new CoinEngine.Amount("0.5", "XLM");
|
||||
private CoinEngine.Amount baseFee = new CoinEngine.Amount("0.00001", "XLM");
|
||||
private boolean error404 = false;
|
||||
private boolean error404, targetAccountCreated = false;
|
||||
|
||||
@Override
|
||||
public void clearInfo() {
|
||||
super.clearInfo();
|
||||
balance = null;
|
||||
error404 = false;
|
||||
targetAccountCreated = false;
|
||||
}
|
||||
|
||||
CoinEngine.Amount getBalance() {
|
||||
|
|
@ -86,6 +87,14 @@ public class XlmData extends CoinData {
|
|||
this.error404 = error404;
|
||||
}
|
||||
|
||||
public boolean isTargetAccountCreated() {
|
||||
return targetAccountCreated;
|
||||
}
|
||||
|
||||
public void setTargetAccountCreated(boolean targetAccountCreated) {
|
||||
this.targetAccountCreated = targetAccountCreated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromBundle(Bundle B) {
|
||||
super.loadFromBundle(B);
|
||||
|
|
@ -116,6 +125,9 @@ public class XlmData extends CoinData {
|
|||
|
||||
if (B.containsKey("Error404")) error404 = B.getBoolean("Error404");
|
||||
else error404 = false;
|
||||
|
||||
if (B.containsKey("TargetAccountCreated")) targetAccountCreated = B.getBoolean("TargetAccountCreated");
|
||||
else targetAccountCreated = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -143,6 +155,8 @@ public class XlmData extends CoinData {
|
|||
|
||||
if (error404) B.putBoolean("Error404", true);
|
||||
|
||||
if (targetAccountCreated) B.putBoolean("TargetAccountCreated", true);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("Can't save to bundle ", e.getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -361,7 +361,7 @@ public class XlmEngine extends CoinEngine {
|
|||
}
|
||||
|
||||
Operation operation;
|
||||
if (isAccountCreated(targetAddress))
|
||||
if (coinData.isTargetAccountCreated())
|
||||
operation = new PaymentOperation.Builder(KeyPair.fromAccountId(targetAddress), new AssetTypeNative(), amountValue.toValueString()).build();
|
||||
else
|
||||
operation = new CreateAccountOperation.Builder(KeyPair.fromAccountId(targetAddress), amountValue.toValueString()).build();
|
||||
|
|
@ -414,29 +414,46 @@ public class XlmEngine extends CoinEngine {
|
|||
};
|
||||
}
|
||||
|
||||
private void checkTargetAccountCreated(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar(ctx.getBlockchain());
|
||||
|
||||
// network call inside, don't use on main thread
|
||||
private boolean isAccountCreated(String address) {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar();
|
||||
ServerApiStellar.Listener listener = new ServerApiStellar.Listener() {
|
||||
@Override
|
||||
public void onSuccess(StellarRequest.Base request) {
|
||||
coinData.setTargetAccountCreated(true);
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
}
|
||||
|
||||
StellarRequest.Balance request = new StellarRequest.Balance(address);
|
||||
|
||||
try {
|
||||
serverApi.doStellarRequest(ctx, request);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
return true; // suppose account is created if anything goes wrong TODO:check
|
||||
}
|
||||
@Override
|
||||
public void onFail(StellarRequest.Base request) {
|
||||
Log.i(TAG, "onFail: " + request.getClass().getSimpleName() + " " + request.getError());
|
||||
|
||||
if (request.errorResponse.getCode() == 404) {
|
||||
coinData.setTargetAccountCreated(false);
|
||||
|
||||
if (amount.compareTo(coinData.getReserve()) >= 0) { //TODO: take fee inclusion in account, now 1 XLM with fee included will fail after transaction is sent
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
} else {
|
||||
ctx.setError(R.string.confirm_transaction_error_not_enough_xlm_for_create);
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
} else { // suppose account is created if anything goes wrong
|
||||
coinData.setTargetAccountCreated(true);
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
serverApi.setListener(listener);
|
||||
|
||||
serverApi.requestData(ctx, new StellarRequest.Balance(targetAddress));
|
||||
|
||||
if (request.errorResponse != null && request.errorResponse.getCode() == 404)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar();
|
||||
final ServerApiStellar serverApi = new ServerApiStellar(ctx.getBlockchain());
|
||||
|
||||
ServerApiStellar.Listener listener = new ServerApiStellar.Listener() {
|
||||
@Override
|
||||
|
|
@ -447,6 +464,7 @@ public class XlmEngine extends CoinEngine {
|
|||
StellarRequest.Balance balanceRequest = (StellarRequest.Balance) request;
|
||||
|
||||
coinData.setAccountResponse(balanceRequest.accountResponse);
|
||||
coinData.setValidationNodeDescription(serverApi.getCurrentURL());
|
||||
|
||||
if (serverApi.isRequestsSequenceCompleted()) {
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
|
||||
|
|
@ -500,14 +518,13 @@ public class XlmEngine extends CoinEngine {
|
|||
|
||||
@Override
|
||||
public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception {
|
||||
// TODO: get fee stats?
|
||||
coinData.minFee = coinData.normalFee = coinData.maxFee = coinData.getBaseFee();
|
||||
blockchainRequestsCallbacks.onComplete(true);
|
||||
checkTargetAccountCreated(blockchainRequestsCallbacks, targetAddress, amount); //TODO: move?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws IOException {
|
||||
final ServerApiStellar serverApi = new ServerApiStellar();
|
||||
final ServerApiStellar serverApi = new ServerApiStellar(ctx.getBlockchain());
|
||||
|
||||
ServerApiStellar.Listener listener = new ServerApiStellar.Listener() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
299
app/src/main/res/values-fr/strings.xml
Normal file
299
app/src/main/res/values-fr/strings.xml
Normal 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 d’une 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 l’utilisateur</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 l’utilisateur</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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -171,10 +205,12 @@
|
|||
<string name="confirm_transaction_error_incoming_transaction_unconfirmed">Please wait for confirmation of incoming transaction</string>
|
||||
<string name="confirm_transaction_error_not_enough_eth_for_fee">Not enough ETH funds for fee</string>
|
||||
<string name="confirm_transaction_error_not_enough_rbtc_for_fee">Not enough RBTC funds for fee</string>
|
||||
<string name="confirm_transaction_error_not_enough_xlm_for_create">Target account is not created! Send 1+ XLM to create it</string>
|
||||
<string name="confirm_transaction_error_pin_2_is_required">PIN2 is required to sign the payment</string>
|
||||
<string name="confirm_transaction_error_service_unavailable">Service unavailable</string>
|
||||
<string name="confirm_transaction_warning_risk_delaying">You have a risk of delaying transaction</string>
|
||||
|
||||
|
||||
<!-- EmptyWallet -->
|
||||
<string name="empty_wallet_not_created">Wallet hasn\'t been yet created</string>
|
||||
<string name="empty_wallet_btn_create">Create Wallet</string>
|
||||
|
|
@ -212,6 +248,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>
|
||||
|
|
|
|||
|
|
@ -1,31 +1,32 @@
|
|||
<resources>
|
||||
|
||||
<string name="in_msg_prefix"><-- </string>
|
||||
<string name="out_msg_prefix">--> </string>
|
||||
<string name="okay_msg_prefix">** </string>
|
||||
<string name="err_msg_prefix">!! </string>
|
||||
<string name="action_msg_prefix">-- </string>
|
||||
<string name="no_data_string">-- -- --</string>
|
||||
<string name="no_data_string_1">---- ---- ---- ----</string>
|
||||
<string name="pin_back"><</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"><-- </string>
|
||||
<string name="out_msg_prefix" translatable="false">--> </string>
|
||||
<string name="okay_msg_prefix" translatable="false">** </string>
|
||||
<string name="err_msg_prefix" translatable="false">!! </string>
|
||||
<string name="action_msg_prefix" translatable="false">-- </string>
|
||||
<string name="no_data_string" translatable="false">-- -- --</string>
|
||||
<string name="no_data_string_1" translatable="false">---- ---- ---- ----</string>
|
||||
<string name="pin_back" translatable="false"><</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>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.ui
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.os.Build
|
||||
|
|
@ -12,9 +13,9 @@ import android.view.inputmethod.EditorInfo
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
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
|
||||
|
|
@ -22,6 +23,7 @@ import com.tangem.wallet.R
|
|||
import com.tangem.wallet.TangemContext
|
||||
import kotlinx.android.synthetic.tangemAccess.fragment_prepare_transaction.*
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, NfcAdapter.ReaderCallback {
|
||||
companion object {
|
||||
|
|
@ -31,6 +33,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,45 +122,43 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
|
||||
if (requestCode == Constant.REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.containsKey("QRCode")) {
|
||||
var code = data.getString("QRCode")
|
||||
when (ctx.blockchain) {
|
||||
Blockchain.Bitcoin -> {
|
||||
if (code.contains("bitcoin:")) {
|
||||
val tmp = code.split("bitcoin:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
code = tmp[1]
|
||||
}
|
||||
}
|
||||
Blockchain.Ethereum, Blockchain.Token -> {
|
||||
if (code.contains("ethereum:")) {
|
||||
val tmp = code.split("ethereum:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
code = tmp[1]
|
||||
} else if (code.contains("blockchain:")) { //TODO: is this needed?
|
||||
val tmp = code.split("blockchain:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
code = tmp[1]
|
||||
}
|
||||
}
|
||||
Blockchain.Litecoin -> {
|
||||
if (code.contains("litecoin:")) {
|
||||
val tmp = code.split("litecoin:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
code = tmp[1]
|
||||
}
|
||||
}
|
||||
Blockchain.Ripple -> {
|
||||
if (code.contains("ripple:")) {
|
||||
val tmp = code.split("ripple:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
code = tmp[1]
|
||||
val code = data.getString("QRCode")
|
||||
val schemeSplit = code!!.split(":")
|
||||
when (schemeSplit.size) {
|
||||
2 -> {
|
||||
if (ctx.blockchain.officialName.toLowerCase(Locale.ROOT).replace("\\s","") == schemeSplit[0]) {
|
||||
val uri = Uri.parse(schemeSplit[1])
|
||||
etWallet?.setText(uri.path)
|
||||
// val amount = uri.getQueryParameter("amount") //TODO: enable after redesign
|
||||
// if (amount != null) {
|
||||
// etAmount?.setText(amount)
|
||||
// rgIncFee.check(R.id.rbFeeOut)
|
||||
// }
|
||||
} else {
|
||||
etWallet?.setText(code)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
etWallet?.setText(code)
|
||||
}
|
||||
}
|
||||
etWallet?.setText(code)
|
||||
} else if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION__) {
|
||||
navigateBackWithResult(resultCode, data)
|
||||
}
|
||||
|
|
@ -170,5 +171,4 @@ class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, Nfc
|
|||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.3.41'
|
||||
ext.kotlin_version = '1.3.50'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
|
||||
classpath 'io.fabric.tools:gradle:1.27.1'
|
||||
classpath 'io.fabric.tools:gradle:1.31.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ dependencies {
|
|||
implementation project(':tangem-card')
|
||||
// implementation 'com.github.TangemCash.card_android-common:card_android-android:0.1.0'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
repositories {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,7 @@ public interface PINsProvider {
|
|||
*/
|
||||
void setLastUsedPIN(String pin);
|
||||
|
||||
byte[] getTerminalPublicKey();
|
||||
byte[] getTerminalPrivateKey();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -159,8 +159,13 @@ public class CommandApdu {
|
|||
public static String toString(byte[] cmdApdu, int Lc) {
|
||||
String cmd = Util.bytesToHex(cmdApdu);
|
||||
if (Lc == 0) return cmd;
|
||||
return cmd.substring(0, 8) + " " + cmd.substring(8, 10) + " " +
|
||||
cmd.substring(10, 10 + Lc * 2) + " " + cmd.substring(10 + Lc * 2, cmd.length());
|
||||
if (cmd.substring(8, 10).equals("00")) {
|
||||
return cmd.substring(0, 8) + " " + cmd.substring(8, 14) + " " +
|
||||
cmd.substring(14, 14 + Lc * 2) + " " + cmd.substring(14 + Lc * 2, cmd.length());
|
||||
} else {
|
||||
return cmd.substring(0, 8) + " " + cmd.substring(8, 10) + " " +
|
||||
cmd.substring(10, 10 + Lc * 2) + " " + cmd.substring(10 + Lc * 2, cmd.length());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -196,6 +201,10 @@ public class CommandApdu {
|
|||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return toBytes(true);
|
||||
}
|
||||
|
||||
public byte[] toBytes(boolean forceExtendedLength) {
|
||||
int length = 4; // CLA, INS, P1, P2
|
||||
|
||||
if (tlvList.size() != 0) {
|
||||
|
|
@ -205,14 +214,14 @@ public class CommandApdu {
|
|||
|
||||
if (mData.length != 0) {
|
||||
length += 1; // LC
|
||||
if (mLc >= 256)
|
||||
length += 2;
|
||||
if (forceExtendedLength || mLc >= 256)
|
||||
length += 2;
|
||||
length += mData.length; // DATA
|
||||
}
|
||||
if (mLeUsed) {
|
||||
length += 1; // LE
|
||||
if (mLc >= 256)
|
||||
length += 2;
|
||||
if (forceExtendedLength || mLc >= 256)
|
||||
length += 2;
|
||||
}
|
||||
|
||||
byte[] apdu = new byte[length];
|
||||
|
|
@ -227,31 +236,31 @@ public class CommandApdu {
|
|||
apdu[index] = (byte) mP2;
|
||||
index++;
|
||||
if (mLc != 0) {
|
||||
if (mLc < 256) {
|
||||
apdu[index] = (byte) mLc;
|
||||
index++;
|
||||
} else {
|
||||
if (forceExtendedLength || mLc >= 256) {
|
||||
apdu[index] = 0;
|
||||
index++;
|
||||
apdu[index] = (byte) (mLc >> 8);
|
||||
index++;
|
||||
apdu[index] = (byte) (mLc & 0xFF);
|
||||
index++;
|
||||
} else {
|
||||
apdu[index] = (byte) mLc;
|
||||
index++;
|
||||
}
|
||||
|
||||
System.arraycopy(mData, 0, apdu, index, mData.length);
|
||||
index += mData.length;
|
||||
}
|
||||
if (mLeUsed) {
|
||||
if (mLc < 256) {
|
||||
apdu[index] += (byte) mLe; // LE
|
||||
} else {
|
||||
if (forceExtendedLength || mLc >= 256) {
|
||||
apdu[index] = 0;
|
||||
index++;
|
||||
apdu[index] = (byte) (mLe >> 8);
|
||||
index++;
|
||||
apdu[index] = (byte) (mLe & 0xFF);
|
||||
index++;
|
||||
} else {
|
||||
apdu[index] += (byte) mLe; // LE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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("]");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ dependencies {
|
|||
implementation project(':tangem-card')
|
||||
// implementation 'com.github.TangemCash.card_android-common:card_android-android:0.1.0'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
|
||||
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.appcompat:appcompat:1.1.0'
|
||||
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 'com.google.code.gson:gson:2.8.5'
|
||||
implementation 'com.scottyab:rootbeer-lib:0.0.7'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
8
tangem-sdk/src/main/res/values-fr/strings.xml
Normal file
8
tangem-sdk/src/main/res/values-fr/strings.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue