Updated on 2026-08-14
This commit is contained in:
parent
b690338759
commit
f7bd06e040
6 changed files with 297 additions and 23 deletions
|
|
@ -19,7 +19,6 @@ android {
|
|||
versionName "0.85.5." + generateVersionName()
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
debuggable false
|
||||
|
|
@ -34,11 +33,11 @@ android {
|
|||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildToolsVersion '27.0.3'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
@ -61,6 +60,7 @@ dependencies {
|
|||
implementation 'org.bitcoinj:bitcoinj-core:0.14.4'
|
||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
implementation 'info.hoang8f:android-segmented:1.0.6'
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
package com.tangem.data.network.request;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.util.Util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Date;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
public class VerificationServerProtocol {
|
||||
|
||||
public static class Request
|
||||
{
|
||||
public Request(CustomCommand command, Class answerClass)
|
||||
{
|
||||
this.command=command;
|
||||
this.answerClass=answerClass;
|
||||
}
|
||||
public String error;
|
||||
public CustomCommand command;
|
||||
public CustomAnswer answer;
|
||||
public Type answerClass;
|
||||
|
||||
public void doPost(String hostURL) throws IOException {
|
||||
URL url = new URL(hostURL+"/"+command.getURL());
|
||||
URLConnection con = url.openConnection();
|
||||
HttpURLConnection http = (HttpURLConnection) con;
|
||||
try {
|
||||
http.setConnectTimeout(30000);
|
||||
http.setReadTimeout(30000);
|
||||
http.setRequestMethod("POST"); // PUT is another valid option
|
||||
http.setDoOutput(true);
|
||||
|
||||
String sRequestBody = getGson().toJson(this.command);
|
||||
|
||||
byte[] out = sRequestBody.getBytes(StandardCharsets.UTF_8);
|
||||
int length = out.length;
|
||||
|
||||
http.setFixedLengthStreamingMode(length);
|
||||
http.setRequestProperty("Content-Type", "application/json");
|
||||
http.connect();
|
||||
try (OutputStream os = http.getOutputStream()) {
|
||||
os.write(out);
|
||||
}
|
||||
try (InputStream is = http.getInputStream()) {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||
answer = getGson().fromJson(br, answerClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
http.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class CustomCommand {
|
||||
public abstract String getURL();
|
||||
|
||||
public byte[] toBytes()
|
||||
{
|
||||
return getGson().toJson(this).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getGson().toJson(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomAnswer {
|
||||
public String error;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getGson().toJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Verify {
|
||||
|
||||
static class RequestItem {
|
||||
public String CID;
|
||||
public String publicKey;
|
||||
}
|
||||
|
||||
static class Command extends CustomCommand {
|
||||
public RequestItem[] requests;
|
||||
|
||||
@Override
|
||||
public String getURL() {
|
||||
return "verify";
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResultItem {
|
||||
public ResultItem(RequestItem request) {
|
||||
CID = request.CID;
|
||||
}
|
||||
|
||||
public String error;
|
||||
public String CID;
|
||||
public Boolean passed;
|
||||
}
|
||||
|
||||
public static class Answer extends CustomAnswer {
|
||||
public ResultItem[] results;
|
||||
}
|
||||
|
||||
public static Request prepare(TangemCard card)
|
||||
{
|
||||
Command c=new Command();
|
||||
c.requests=new RequestItem[1];
|
||||
c.requests[0]=new RequestItem();
|
||||
c.requests[0].CID=Util.bytesToHex(card.getCID());
|
||||
c.requests[0].publicKey=Util.bytesToHex(card.getCardPublicKey());
|
||||
|
||||
return new Request(c,Answer.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Validate {
|
||||
|
||||
static class RequestItem {
|
||||
public String CID;
|
||||
public int counter;
|
||||
public String signature;
|
||||
}
|
||||
|
||||
static class Command extends CustomCommand {
|
||||
public RequestItem[] requests;
|
||||
|
||||
@Override
|
||||
public String getURL() {
|
||||
return "validate";
|
||||
}
|
||||
}
|
||||
|
||||
static class ResultItem {
|
||||
public ResultItem(RequestItem request) {
|
||||
CID = request.CID;
|
||||
}
|
||||
public String error;
|
||||
public String CID;
|
||||
public Integer previousCounter;
|
||||
public Boolean passed;
|
||||
}
|
||||
|
||||
static class Answer extends CustomAnswer {
|
||||
public ResultItem[] results;
|
||||
}
|
||||
|
||||
public Request prepare(TangemCard card, int ValidationCounter, byte[] ValidationSignature)
|
||||
{
|
||||
Command c=new Command();
|
||||
c.requests=new RequestItem[1];
|
||||
c.requests[0].CID= Util.bytesToHex(card.getCID());
|
||||
c.requests[0].counter=ValidationCounter;
|
||||
c.requests[0].signature=Util.bytesToHex(ValidationSignature);
|
||||
return new Request(new Command(),Answer.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Date strToDate(String date) throws ParseException {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
return new Date(formatter.parse(date).getTime());
|
||||
}
|
||||
|
||||
public static String dateToStr(Date date) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
public static Gson getGson() {
|
||||
return new GsonBuilder().create();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.data.network.task;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.tangem.data.network.request.VerificationServerProtocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VerificationServerTask extends AsyncTask<VerificationServerProtocol.Request, Void, List<VerificationServerProtocol.Request>> {
|
||||
public static final String hostURL ="https://tangem-webapp.appspot.com";
|
||||
|
||||
public VerificationServerTask() {
|
||||
|
||||
}
|
||||
|
||||
protected List<VerificationServerProtocol.Request> doInBackground(VerificationServerProtocol.Request... requests) {
|
||||
List<VerificationServerProtocol.Request> result = new ArrayList<>();
|
||||
for (int i = 0; i < requests.length; i++) {
|
||||
result.add(requests[i]);
|
||||
}
|
||||
|
||||
for (VerificationServerProtocol.Request request : result) {
|
||||
try {
|
||||
request.doPost(hostURL);
|
||||
} catch (Exception e) {
|
||||
request.error = e.getMessage();
|
||||
if( request.error==null || request.error.isEmpty() )
|
||||
{
|
||||
request.error = e.getClass().getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getValidationNodeDescription() {
|
||||
return hostURL;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,16 +21,23 @@ public class ReadCardInfoTask extends Thread {
|
|||
private Context mContext;
|
||||
private NfcManager mNfcManager;
|
||||
|
||||
private ArrayList<String> lastRead_UnsuccessfullPINs = new ArrayList<>();
|
||||
private TangemCard.EncryptionMode lastRead_Encryption = null;
|
||||
private String lastRead_UID;
|
||||
// this fields are static to optimize process when need enter pin and scan card again
|
||||
private static ArrayList<String> lastRead_UnsuccessfullPINs = new ArrayList<>();
|
||||
private static TangemCard.EncryptionMode lastRead_Encryption = null;
|
||||
private static String lastRead_UID;
|
||||
|
||||
public ReadCardInfoTask(Context context, NfcManager nfcManager, String lastRead_UID, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
public static void resetLastReadInfo() {
|
||||
lastRead_UID="";
|
||||
lastRead_Encryption=null;
|
||||
lastRead_UnsuccessfullPINs.clear();
|
||||
}
|
||||
|
||||
public ReadCardInfoTask(Context context, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
mContext = context;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
mNfcManager = nfcManager;
|
||||
this.lastRead_UID = lastRead_UID;
|
||||
// ReadCardInfoTask.lastRead_UID = lastRead_UID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -56,9 +63,7 @@ public class ReadCardInfoTask extends Thread {
|
|||
byte[] UID = mIsoDep.getTag().getId();
|
||||
String sUID = Util.byteArrayToHexString(UID);
|
||||
if (!lastRead_UID.equals(sUID)) {
|
||||
lastRead_UID = sUID;
|
||||
lastRead_UnsuccessfullPINs.clear();
|
||||
lastRead_Encryption = null;
|
||||
resetLastReadInfo();
|
||||
}
|
||||
|
||||
Log.i(TAG, "[-- Start read card info --]");
|
||||
|
|
@ -146,6 +151,7 @@ public class ReadCardInfoTask extends Thread {
|
|||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mNfcManager.notifyReadResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tangem.presentation.dialog.NFCEnableDialog;
|
||||
|
||||
|
|
@ -76,6 +77,27 @@ public class NfcManager {
|
|||
// }
|
||||
}
|
||||
|
||||
int errorCount=0;
|
||||
public void notifyReadResult(boolean success)
|
||||
{
|
||||
if(success)
|
||||
{
|
||||
errorCount=0;
|
||||
}else{
|
||||
errorCount++;
|
||||
}
|
||||
if( errorCount>=3 )
|
||||
{
|
||||
disableReaderMode();
|
||||
mNfcAdapter=null;
|
||||
Toast.makeText(mActivity,"NFC restarted!",Toast.LENGTH_SHORT).show();
|
||||
mActivity.runOnUiThread(()->{
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity);
|
||||
enableReaderMode();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showNFCEnableDialog() {
|
||||
mEnableNfcDialog = new NFCEnableDialog();
|
||||
mEnableNfcDialog.show(mActivity.getFragmentManager(), NFCEnableDialog.TAG);
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
|
||||
private int unsuccessReadCount = 0;
|
||||
private Tag lastTag = null;
|
||||
private String lastRead_UID = "";
|
||||
// private String lastRead_UID = "";
|
||||
|
||||
public Main() {
|
||||
}
|
||||
|
|
@ -119,9 +119,9 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
int cardIndex = viewHolder.getAdapterPosition();
|
||||
if (cardIndex < 0 || cardIndex >= mCardListAdapter.getItemCount()) return;
|
||||
slCardUIDs.remove(mCardListAdapter.getCard(cardIndex).getUID());
|
||||
if (mCardListAdapter.getCard(cardIndex).getUID() == lastRead_UID) {
|
||||
lastRead_UID = "";
|
||||
}
|
||||
// if (mCardListAdapter.getCard(cardIndex).getUID() == lastRead_UID) {
|
||||
// lastRead_UID = "";
|
||||
// }
|
||||
mCardListAdapter.removeCard(cardIndex);
|
||||
if (mCardListAdapter.getItemCount() == 0 && getActivity().getClass() == MainActivity.class) {
|
||||
((MainActivity) getActivity()).hideCleanButton();
|
||||
|
|
@ -142,6 +142,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
ReadCardInfoTask.resetLastReadInfo();
|
||||
mNfcManager.onResume();
|
||||
}
|
||||
|
||||
|
|
@ -201,8 +202,13 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
}
|
||||
|
||||
}
|
||||
} else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_ENTER_PIN_ACTIVITY) {
|
||||
if (lastTag != null) onTagDiscovered(lastTag);
|
||||
} else if (requestCode == REQUEST_CODE_ENTER_PIN_ACTIVITY) {
|
||||
if( resultCode == Activity.RESULT_OK && lastTag != null)
|
||||
{
|
||||
onTagDiscovered(lastTag);
|
||||
}else{
|
||||
ReadCardInfoTask.resetLastReadInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,13 +239,14 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
}
|
||||
lastTag = tag;
|
||||
|
||||
readCardInfoTask = new ReadCardInfoTask(getActivity(), mNfcManager, lastRead_UID, isoDep, this);
|
||||
readCardInfoTask = new ReadCardInfoTask(getActivity(), mNfcManager, isoDep, this);
|
||||
readCardInfoTask.start();
|
||||
|
||||
Log.i(TAG, "onTagDiscovered " + Arrays.toString(tag.getId()));
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mNfcManager.notifyReadResult(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -290,6 +297,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
readCardInfoTask = null;
|
||||
if (cardProtocol != null) {
|
||||
if (cardProtocol.getError() == null) {
|
||||
mNfcManager.notifyReadResult(true);
|
||||
progressBar.post(() -> {
|
||||
rlProgressBar.setVisibility(View.GONE);
|
||||
progressBar.setProgress(100);
|
||||
|
|
@ -334,7 +342,6 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
for (RequestWalletInfoTask rt : requestTasks) {
|
||||
rt.cancel(true);
|
||||
}
|
||||
lastRead_UID = "";
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -349,12 +356,15 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
slCardUIDs.remove(cardProtocol.getCard().getUID());
|
||||
if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) {
|
||||
doEnterPIN();
|
||||
} else if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
|
||||
new NoExtendedLengthSupportDialog().show(Objects.requireNonNull(getActivity()).getFragmentManager(), NoExtendedLengthSupportDialog.TAG);
|
||||
}
|
||||
} else {
|
||||
if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
|
||||
new NoExtendedLengthSupportDialog().show(Objects.requireNonNull(getActivity()).getFragmentManager(), NoExtendedLengthSupportDialog.TAG);
|
||||
}
|
||||
}
|
||||
lastTag = null;
|
||||
ReadCardInfoTask.resetLastReadInfo();
|
||||
mNfcManager.notifyReadResult(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -376,6 +386,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
|
||||
public void OnReadCancel() {
|
||||
readCardInfoTask = null;
|
||||
ReadCardInfoTask.resetLastReadInfo();
|
||||
progressBar.postDelayed(() -> {
|
||||
try {
|
||||
rlProgressBar.setVisibility(View.GONE);
|
||||
|
|
@ -410,7 +421,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis
|
|||
for (RequestWalletInfoTask rt : requestTasks) {
|
||||
rt.cancel(true);
|
||||
}
|
||||
lastRead_UID = "";
|
||||
ReadCardInfoTask.resetLastReadInfo();
|
||||
}
|
||||
|
||||
public void refreshCard(TangemCard card) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue